Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 29 Aug 2016 17:40:50 -0700
changeset 407064 45aba91445a23c7ac14c88e84e575e99afdefe50
parent 407063 7293879d87d780790cb0f67e98226b7d4de6d4e1 (current diff)
parent 407023 c945b9db25012f65327ce8b265927b34c437848d (diff)
child 407065 6c0d74730bd57da6c1c9bf3d05608cd8156bb65f
push id27893
push userbmo:gasolin@mozilla.com
push dateTue, 30 Aug 2016 03:41:38 +0000
reviewersmerge
milestone51.0a1
Merge inbound to m-c a=merge
dom/base/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
js/src/vm/Caches-inl.h
modules/libpref/init/all.js
testing/web-platform/meta/XMLHttpRequest/send-after-setting-document-domain.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-authentication-basic-cors-not-enabled.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-redirect-bogus-sync.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-redirect-infinite-sync.htm.ini
testing/web-platform/meta/XMLHttpRequest/xmlhttprequest-network-error-sync.htm.ini
testing/web-platform/tests/XMLHttpRequest/event-error.html
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -78,63 +78,33 @@ addEventListener("pageshow", function(ev
 });
 addEventListener("DOMAutoComplete", function(event) {
   LoginManagerContent.onUsernameInput(event);
 });
 addEventListener("blur", function(event) {
   LoginManagerContent.onUsernameInput(event);
 });
 
-var gLastContextMenuEvent = null; // null or a WeakReference to a contextmenu event
 var handleContentContextMenu = function (event) {
-  gLastContextMenuEvent = null;
   let defaultPrevented = event.defaultPrevented;
   if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
     let plugin = null;
     try {
       plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent);
     } catch (e) {}
     if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
       // Don't open a context menu for plugins.
       return;
     }
 
     defaultPrevented = false;
   }
 
-  if (defaultPrevented) {
-    return;
-  }
-
-  if (event.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
-    // If this was triggered by touch, then we don't want to show the actual
-    // context menu until we get the APZ:LongTapUp notification. However, we
-    // will need the |event| object when we get that notification, so we save
-    // it in a WeakReference. That way it won't leak things if we never get
-    // the APZ:LongTapUp notification (which is quite possible).
-    gLastContextMenuEvent = Cu.getWeakReference(event);
+  if (defaultPrevented)
     return;
-  }
-
-  // For non-touch-derived contextmenu events, we can handle it right away.
-  showContentContextMenu(event);
-}
-
-var showContentContextMenu = function (event) {
-  if (event == null) {
-    // If we weren't given an event, then this is being invoked from the
-    // APZ:LongTapUp observer, and the contextmenu event is stashed in
-    // gLastContextMenuEvent.
-    event = (gLastContextMenuEvent ? gLastContextMenuEvent.get() : null);
-    gLastContextMenuEvent = null;
-    if (event == null) {
-      // Still no event? We can't do anything, bail out.
-      return;
-    }
-  }
 
   let addonInfo = {};
   let subject = {
     event: event,
     addonInfo: addonInfo,
   };
   subject.wrappedJSObject = subject;
   Services.obs.notifyObservers(subject, "content-contextmenu", null);
@@ -242,21 +212,16 @@ var showContentContextMenu = function (e
     };
   }
 }
 
 Cc["@mozilla.org/eventlistenerservice;1"]
   .getService(Ci.nsIEventListenerService)
   .addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
 
-Services.obs.addObserver(showContentContextMenu, "APZ:LongTapUp", false);
-addEventListener("unload", () => {
-  Services.obs.removeObserver(showContentContextMenu, "APZ:LongTapUp")
-}, false);
-
 // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
 const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
 const TLS_ERROR_REPORT_TELEMETRY_EXPANDED = 1;
 const TLS_ERROR_REPORT_TELEMETRY_SUCCESS  = 6;
 const TLS_ERROR_REPORT_TELEMETRY_FAILURE  = 7;
 
 const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -34,16 +34,31 @@ const PAGECONTENT_SMALL =
   "</select><select id='two'>" +
   "  <option value='Three'>Three</option>" +
   "  <option value='Four'>Four</option>" +
   "</select><select id='three'>" +
   "  <option value='Five'>Five</option>" +
   "  <option value='Six'>Six</option>" +
   "</select></body></html>";
 
+const PAGECONTENT_SOMEHIDDEN =
+  "<html>" +
+  "<body><select id='one'>" +
+  "  <option value='One' style='display: none;'>OneHidden</option>" +
+  "  <option value='Two' style='display: none;'>TwoHidden</option>" +
+  "  <option value='Three'>ThreeVisible</option>" +
+  "  <option value='Four'style='display: table;'>FourVisible</option>" +
+  "  <option value='Five'>FiveVisible</option>" +
+  "  <optgroup label='GroupHidden' style='display: none;'>" +
+  "    <option value='Four'>Six.OneHidden</option>" +
+  "    <option value='Five' style='display: block;'>Six.TwoHidden</option>" +
+  "  </optgroup>" +
+  "  <option value='Six'>SevenVisible</option>" +
+  "</select></body></html>";
+
 const PAGECONTENT_TRANSLATED =
   "<html><body>" +
   "<div id='div'>" +
   "<iframe id='frame' width='320' height='295' style='border: none;'" +
   "        src='data:text/html,<select id=select autofocus><option>he he he</option><option>boo boo</option><option>baz baz</option></select>'" +
   "</iframe>" +
   "</div></body></html>";
 
@@ -480,8 +495,38 @@ add_task(function* test_mousemove_correc
     let sizeModeChanged = BrowserTestUtils.waitForEvent(window, "sizemodechange");
     BrowserFullScreen();
     yield sizeModeChanged;
     yield popupHiddenPromise;
   }
 
   yield BrowserTestUtils.removeTab(tab);
 });
+
+// This test checks when a <select> element has some options with altered display values.
+add_task(function* test_somehidden() {
+  const pageUrl = "data:text/html," + escape(PAGECONTENT_SOMEHIDDEN);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+  let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
+
+  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
+  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
+  yield popupShownPromise;
+
+  // The exact number is not needed; just ensure the height is larger than 4 items to accomodate any popup borders.
+  ok(selectPopup.getBoundingClientRect().height >= selectPopup.lastChild.getBoundingClientRect().height * 4, "Height contains at least 4 items");
+  ok(selectPopup.getBoundingClientRect().height < selectPopup.lastChild.getBoundingClientRect().height * 5, "Height doesn't contain 5 items");
+
+  // The label contains the substring 'Visible' for items that are visible.
+  // Otherwise, it is expected to be display: none.
+  is(selectPopup.parentNode.itemCount, 9, "Correct number of items");
+  let child = selectPopup.firstChild;
+  let idx = 1;
+  while (child) {
+    is(getComputedStyle(child).display, child.label.indexOf("Visible") > 0 ? "-moz-box" : "none",
+       "Item " + (idx++) + " is visible");
+    child = child.nextSibling;
+  }
+
+  yield hideSelectPopup(selectPopup, "escape");
+  yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/base/content/test/plugins/browser_bug812562.js
+++ b/browser/base/content/test/plugins/browser_bug812562.js
@@ -54,17 +54,17 @@ add_task(function* () {
   ok(!popupNotification, "test part 2: Should not have a click-to-play notification");
   yield ContentTask.spawn(gTestBrowser, null, function* () {
     Assert.ok(!content.document.getElementById("test"),
       "test part 2: plugin should not be activated");
   });
 
   let obsPromise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
   let overlayPromise = promisePopupNotification("click-to-play-plugins");
-  gTestBrowser.contentWindow.history.back();
+  gTestBrowser.goBack();
   yield obsPromise;
   yield overlayPromise;
 });
 
 add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -172,11 +172,20 @@ featureDisableRequiresRestart=%S must re
 shouldRestartTitle=Restart %S
 okToRestartButton=Restart %S now
 revertNoRestartButton=Revert
 
 restartNow=Restart Now
 restartLater=Restart Later
 
 disableContainersAlertTitle=Close All Container Tabs?
+
+# LOCALIZATION NOTE (disableContainersMsg): Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #S is the number of container tabs
 disableContainersMsg=If you disable Container Tabs now, #S container tab will be closed. Are you sure you want to disable Container Tabs?;If you disable Containers Tabs now, #S container tabs will be closed. Are you sure you want to disable Containers Tabs?
+
+# LOCALIZATION NOTE (disableContainersOkButton): Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #S is the number of container tabs
 disableContainersOkButton=Close #S Container Tab;Close #S Container Tabs
+
 disableContainersButton2=Keep enabled
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1049,17 +1049,17 @@ toolbaritem[cui-areatype="menu-panel"] >
 }
 
 #identity-box:-moz-locale-dir(rtl) {
   border-top-right-radius: 1.5px;
   border-bottom-right-radius: 1.5px;
 }
 
 #identity-box:-moz-focusring {
-  outline: 1px dotted #000;
+  outline: 1px dotted;
   outline-offset: -3px;
 }
 
 %include ../shared/identity-block/identity-block.inc.css
 
 %include ../shared/notification-icons.inc.css
 
 .popup-notification-body[popupid="addon-progress"],
--- a/browser/themes/shared/urlbarSearchSuggestionsNotification.inc.css
+++ b/browser/themes/shared/urlbarSearchSuggestionsNotification.inc.css
@@ -1,10 +1,10 @@
 #PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] {
-  border-bottom: 1px solid hsla(210, 4%, 10%, 0.14);
+  border-bottom: 1px solid var(--panel-separator-color);
   background-color: hsla(210, 4%, 10%, 0.07);
   padding: 6px 0;
   padding-inline-start: 44px;
   padding-inline-end: 6px;
   background-image: url("chrome://browser/skin/info.svg");
   background-clip: padding-box;
   background-position: 20px center;
   background-repeat: no-repeat;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1484,17 +1484,17 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 
 #search-container {
   min-width: calc(54px + 11ch);
 }
 
 /* identity box */
 
 #identity-box:-moz-focusring {
-  outline: 1px dotted #000;
+  outline: 1px dotted;
   outline-offset: -3px;
 }
 
 /* Location bar dropmarker */
 
 .urlbar-history-dropmarker {
   -moz-appearance: none;
   padding: 0 3px;
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -215,19 +215,19 @@ nsPrincipal::SubsumesInternal(nsIPrincip
 
     // If either has .domain set, we have equality i.f.f. the domains match.
     // Otherwise, we fall through to the non-document-domain-considering case.
     if (thisDomain || otherDomain) {
       return nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain);
     }
   }
 
-    nsCOMPtr<nsIURI> otherURI;
-    rv = aOther->GetURI(getter_AddRefs(otherURI));
-    NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIURI> otherURI;
+  rv = aOther->GetURI(getter_AddRefs(otherURI));
+  NS_ENSURE_SUCCESS(rv, false);
 
   // Compare codebases.
   return nsScriptSecurityManager::SecurityCompareURIs(mCodebase, otherURI);
 }
 
 NS_IMETHODIMP
 nsPrincipal::GetURI(nsIURI** aURI)
 {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4839,18 +4839,16 @@ nsDocument::GetScriptHandlingObjectInter
   return scriptHandlingObject;
 }
 void
 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
 {
   NS_ASSERTION(!mScriptGlobalObject ||
                mScriptGlobalObject == aScriptObject,
                "Wrong script object!");
-  // XXXkhuey why bother?
-  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aScriptObject);
   if (aScriptObject) {
     mScopeObject = do_GetWeakReference(aScriptObject);
     mHasHadScriptHandlingObject = true;
     mHasHadDefaultView = false;
   }
 }
 
 bool
@@ -7705,18 +7703,16 @@ nsIDocument::GetCompatMode(nsString& aCo
 
 void
 nsDOMAttributeMap::BlastSubtreeToPieces(nsINode *aNode)
 {
   if (aNode->IsElement()) {
     Element *element = aNode->AsElement();
     const nsDOMAttributeMap *map = element->GetAttributeMap();
     if (map) {
-      nsCOMPtr<nsIAttribute> attr;
-
       // This non-standard style of iteration is presumably used because some
       // of the code in the loop body can trigger element removal, which
       // invalidates the iterator.
       while (true) {
         auto iter = map->mAttributeCache.ConstIter();
         if (iter.Done()) {
           break;
         }
@@ -8759,17 +8755,16 @@ nsDocument::Sanitize()
   // in case there is ever an exploit that allows a cached document to be
   // accessed from a different document.
 
   // First locate all input elements, regardless of whether they are
   // in a form, and reset the password and autocomplete=off elements.
 
   RefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
 
-  nsCOMPtr<nsIContent> item;
   nsAutoString value;
 
   uint32_t length = nodes->Length(true);
   for (uint32_t i = 0; i < length; ++i) {
     NS_ASSERTION(nodes->Item(i), "null item in node list!");
 
     RefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i));
     if (!input)
@@ -10434,17 +10429,16 @@ nsresult
 nsDocument::GetStateObject(nsIVariant** aState)
 {
   // Get the document's current state object. This is the object backing both
   // history.state and popStateEvent.state.
   //
   // mStateObjectContainer may be null; this just means that there's no
   // current state object.
 
-  nsCOMPtr<nsIVariant> stateObj;
   if (!mStateObjectCached && mStateObjectContainer) {
     AutoJSContext cx;
     nsIGlobalObject* sgo = GetScopeObject();
     NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
     JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
     NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
     JSAutoCompartment ac(cx, global);
 
@@ -10875,17 +10869,16 @@ nsIDocument::CaretPositionFromPoint(floa
 
   nsCOMPtr<nsIContent> node = offsets.content;
   uint32_t offset = offsets.offset;
   nsCOMPtr<nsIContent> anonNode = node;
   bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
   if (nodeIsAnonymous) {
     node = ptFrame->GetContent();
     nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
-    nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
     nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
     nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
     nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
     if (textFrame || numberFrame) {
       // If the anonymous content node has a child, then we need to make sure
       // that we get the appropriate child, as otherwise the offset may not be
       // correct when we construct a range for it.
       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Object that can be used to serialize selections, ranges, or nodes
  * to strings in a gazillion different ways.
  */
- 
+
 #include "nsIDocumentEncoder.h"
 
 #include "nscore.h"
 #include "nsIFactory.h"
 #include "nsISupports.h"
 #include "nsIDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsCOMPtr.h"
@@ -84,18 +84,18 @@ protected:
                                       bool aDontSerializeRoot,
                                       uint32_t aMaxLength = 0);
   nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr);
   // This serializes the content of aNode.
   nsresult SerializeToStringIterative(nsINode* aNode,
                                       nsAString& aStr);
   nsresult SerializeRangeToString(nsRange *aRange,
                                   nsAString& aOutputString);
-  nsresult SerializeRangeNodes(nsRange* aRange, 
-                               nsINode* aNode, 
+  nsresult SerializeRangeNodes(nsRange* aRange,
+                               nsINode* aNode,
                                nsAString& aString,
                                int32_t aDepth);
   nsresult SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
                                       nsAString& aString);
   nsresult SerializeRangeContextEnd(const nsTArray<nsINode*>& aAncestorArray,
                                     nsAString& aString);
   virtual int32_t
   GetImmediateContextCount(const nsTArray<nsINode*>& aAncestorArray)
@@ -162,17 +162,17 @@ protected:
   uint32_t          mEndDepth;
   int32_t           mStartRootIndex;
   int32_t           mEndRootIndex;
   AutoTArray<nsINode*, 8>    mCommonAncestors;
   AutoTArray<nsIContent*, 8> mStartNodes;
   AutoTArray<int32_t, 8>     mStartOffsets;
   AutoTArray<nsIContent*, 8> mEndNodes;
   AutoTArray<int32_t, 8>     mEndOffsets;
-  bool              mHaltRangeHint;  
+  bool              mHaltRangeHint;
   // Used when context has already been serialized for
   // table cell selections (where parent is <tr>)
   bool              mDisableContextSerialize;
   bool              mIsCopying;  // Set to true only while copying
   bool              mNodeIsContainer;
   nsStringBuffer*   mCachedBuffer;
 };
 
@@ -407,38 +407,38 @@ nsresult
 nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
                                       int32_t aStartOffset,
                                       int32_t aEndOffset,
                                       nsAString& aStr,
                                       nsINode* aOriginalNode)
 {
   if (!IsVisibleNode(aNode))
     return NS_OK;
-  
+
   nsINode* node = nullptr;
   nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
 
   // Caller didn't do fixup, so we'll do it ourselves
   if (!aOriginalNode) {
     aOriginalNode = aNode;
-    if (mNodeFixup) { 
+    if (mNodeFixup) {
       bool dummy;
       nsCOMPtr<nsIDOMNode> domNodeIn = do_QueryInterface(aNode);
       nsCOMPtr<nsIDOMNode> domNodeOut;
       mNodeFixup->FixupNode(domNodeIn, &dummy, getter_AddRefs(domNodeOut));
       fixedNodeKungfuDeathGrip = do_QueryInterface(domNodeOut);
       node = fixedNodeKungfuDeathGrip;
     }
   }
 
   // Either there was no fixed-up node,
   // or the caller did fixup themselves and aNode is already fixed
   if (!node)
     node = aNode;
-  
+
   if (node->IsElement()) {
     if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
                    nsIDocumentEncoder::OutputDropInvisibleBreak)) &&
         IsInvisibleBreak(node)) {
       return NS_OK;
     }
     Element* originalElement =
       aOriginalNode && aOriginalNode->IsElement() ?
@@ -650,33 +650,33 @@ ConvertAndWrite(const nsAString& aString
 
     uint32_t written;
     rv = aStream->Write(charXferBuf, charLength, &written);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // If the converter couldn't convert a chraacer we replace the
     // character with a characre entity.
     if (convert_rv == NS_ERROR_UENC_NOMAPPING) {
-      // Finishes the conversion. 
+      // Finishes the conversion.
       // The converter has the possibility to write some extra data and flush its final state.
       char finish_buf[33];
       charLength = sizeof(finish_buf) - 1;
       rv = aEncoder->Finish(finish_buf, &charLength);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Make sure finish_buf is null-terminated before we call
       // Write().
 
       finish_buf[charLength] = '\0';
 
       rv = aStream->Write(finish_buf, charLength, &written);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsAutoCString entString("&#");
-      if (NS_IS_HIGH_SURROGATE(unicodeBuf[unicodeLength - 1]) && 
+      if (NS_IS_HIGH_SURROGATE(unicodeBuf[unicodeLength - 1]) &&
           unicodeLength < startLength && NS_IS_LOW_SURROGATE(unicodeBuf[unicodeLength]))  {
         entString.AppendInt(SURROGATE_TO_UCS4(unicodeBuf[unicodeLength - 1],
                                               unicodeBuf[unicodeLength]));
         unicodeLength += 1;
       }
       else
         entString.AppendInt(unicodeBuf[unicodeLength - 1]);
       entString.Append(';');
@@ -854,17 +854,17 @@ nsDocumentEncoder::SerializeRangeNodes(n
   {
     // node is completely contained in range.  Serialize the whole subtree
     // rooted by this node.
     rv = SerializeToStringRecursive(aNode, aString, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else
   {
-    // due to implementation it is impossible for text node to be both start and end of 
+    // due to implementation it is impossible for text node to be both start and end of
     // range.  We would have handled that case without getting here.
     //XXXsmaug What does this all mean?
     if (IsTextNode(aNode))
     {
       if (startNode == content)
       {
         int32_t startOffset = aRange->StartOffset();
         rv = SerializeNodeStart(aNode, startOffset, -1, aString);
@@ -884,42 +884,42 @@ nsDocumentEncoder::SerializeRangeNodes(n
         if (IncludeInContext(aNode))
         {
           // halt the incrementing of mStartDepth/mEndDepth.  This is
           // so paste client will include this node in paste.
           mHaltRangeHint = true;
         }
         if ((startNode == content) && !mHaltRangeHint) mStartDepth++;
         if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
-      
+
         // serialize the start of this node
         rv = SerializeNodeStart(aNode, 0, -1, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
-      
+
       // do some calculations that will tell us which children of this
       // node are in the range.
       nsIContent* childAsNode = nullptr;
       int32_t startOffset = 0, endOffset = -1;
       if (startNode == content && mStartRootIndex >= aDepth)
         startOffset = mStartOffsets[mStartRootIndex - aDepth];
       if (endNode == content && mEndRootIndex >= aDepth)
         endOffset = mEndOffsets[mEndRootIndex - aDepth];
-      // generated content will cause offset values of -1 to be returned.  
+      // generated content will cause offset values of -1 to be returned.
       int32_t j;
       uint32_t childCount = content->GetChildCount();
 
       if (startOffset == -1) startOffset = 0;
       if (endOffset == -1) endOffset = childCount;
       else
       {
         // if we are at the "tip" of the selection, endOffset is fine.
         // otherwise, we need to add one.  This is because of the semantics
         // of the offset list created by GetAncestorsAndOffsets().  The
-        // intermediate points on the list use the endOffset of the 
+        // intermediate points on the list use the endOffset of the
         // location of the ancestor, rather than just past it.  So we need
         // to add one here in order to include it in the children we serialize.
         if (aNode != aRange->GetEndParent())
         {
           endOffset++;
         }
       }
       // serialize the children of this node that are in the range
@@ -934,17 +934,17 @@ nsDocumentEncoder::SerializeRangeNodes(n
 
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // serialize the end of this node
       if (aNode != mCommonParent)
       {
         rv = SerializeNodeEnd(aNode, aString);
-        NS_ENSURE_SUCCESS(rv, rv); 
+        NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   return NS_OK;
 }
 
 nsresult
 nsDocumentEncoder::SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
@@ -1015,17 +1015,17 @@ nsDocumentEncoder::SerializeRangeToStrin
 {
   if (!aRange || aRange->Collapsed())
     return NS_OK;
 
   mCommonParent = aRange->GetCommonAncestor();
 
   if (!mCommonParent)
     return NS_OK;
-  
+
   nsINode* startParent = aRange->GetStartParent();
   NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
   int32_t startOffset = aRange->StartOffset();
 
   nsINode* endParent = aRange->GetEndParent();
   NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE);
   int32_t endOffset = aRange->EndOffset();
 
@@ -1042,17 +1042,17 @@ nsDocumentEncoder::SerializeRangeToStrin
                                          &mStartNodes, &mStartOffsets);
   nsCOMPtr<nsIDOMNode> ep = do_QueryInterface(endParent);
   nsContentUtils::GetAncestorsAndOffsets(ep, endOffset,
                                          &mEndNodes, &mEndOffsets);
 
   nsCOMPtr<nsIContent> commonContent = do_QueryInterface(mCommonParent);
   mStartRootIndex = mStartNodes.IndexOf(commonContent);
   mEndRootIndex = mEndNodes.IndexOf(commonContent);
-  
+
   nsresult rv = NS_OK;
 
   rv = SerializeRangeContextStart(mCommonAncestors, aOutputString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if ((startParent == endParent) && IsTextNode(startParent))
   {
     if (mFlags & SkipInvisibleContent) {
@@ -1103,30 +1103,28 @@ nsDocumentEncoder::EncodeToStringWithMax
     }
   }
   NS_ASSERTION(!mCachedBuffer->IsReadonly(),
                "DocumentEncoder shouldn't keep reference to non-readonly buffer!");
   static_cast<char16_t*>(mCachedBuffer->Data())[0] = char16_t(0);
   mCachedBuffer->ToString(0, output, true);
   // output owns the buffer now!
   mCachedBuffer = nullptr;
-  
+
 
   if (!mSerializer) {
     nsAutoCString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX);
     AppendUTF16toUTF8(mMimeType, progId);
 
     mSerializer = do_CreateInstance(progId.get());
     NS_ENSURE_TRUE(mSerializer, NS_ERROR_NOT_IMPLEMENTED);
   }
 
   nsresult rv = NS_OK;
 
-  nsCOMPtr<nsIAtom> charsetAtom;
-  
   bool rewriteEncodingDeclaration = !(mSelection || mRange || mNode) && !(mFlags & OutputDontRewriteEncodingDeclaration);
   mSerializer->Init(mFlags, mWrapColumn, mCharset.get(), mIsCopying, rewriteEncodingDeclaration);
 
   if (mSelection) {
     nsCOMPtr<nsIDOMRange> range;
     int32_t i, count = 0;
 
     rv = mSelection->GetRangeCount(&count);
@@ -1190,23 +1188,23 @@ nsDocumentEncoder::EncodeToStringWithMax
     mStartDepth = firstRangeStartDepth;
 
     if (prevNode) {
       nsCOMPtr<nsINode> p = do_QueryInterface(prevNode);
       rv = SerializeNodeEnd(p, output);
       NS_ENSURE_SUCCESS(rv, rv);
       mCommonAncestors.Clear();
       nsContentUtils::GetAncestors(p->GetParentNode(), mCommonAncestors);
-      mDisableContextSerialize = false; 
+      mDisableContextSerialize = false;
       rv = SerializeRangeContextEnd(mCommonAncestors, output);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Just to be safe
-    mDisableContextSerialize = false; 
+    mDisableContextSerialize = false;
 
     mSelection = nullptr;
   } else if (mRange) {
       rv = SerializeRangeToString(mRange, output);
 
       mRange = nullptr;
   } else if (mNode) {
     if (!mNodeFixup && !(mFlags & SkipInvisibleContent) && !mStream &&
@@ -1221,17 +1219,17 @@ nsDocumentEncoder::EncodeToStringWithMax
 
     if (NS_SUCCEEDED(rv)) {
       rv = SerializeToStringRecursive(mDocument, output, false, aMaxLength);
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mSerializer->Flush(output);
- 
+
   mCachedBuffer = nsStringBuffer::FromString(output);
   // We have to be careful how we set aOutputString, because we don't
   // want it to end up sharing mCachedBuffer if we plan to reuse it.
   bool setOutput = false;
   // Try to cache the buffer.
   if (mCachedBuffer) {
     if (mCachedBuffer->StorageSize() == bufferSize &&
         !mCachedBuffer->IsReadonly()) {
@@ -1330,22 +1328,22 @@ public:
 
 protected:
 
   enum Endpoint
   {
     kStart,
     kEnd
   };
-  
+
   nsresult PromoteRange(nsIDOMRange *inRange);
-  nsresult PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode, 
-                                int32_t *ioStartOffset, 
+  nsresult PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
+                                int32_t *ioStartOffset,
                                 int32_t *ioEndOffset);
-  nsresult GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset, 
+  nsresult GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset,
                             nsCOMPtr<nsIDOMNode> *outNode, int32_t *outOffset, nsIDOMNode *aCommon);
   nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode *aParent, int32_t aOffset);
   bool IsMozBR(nsIDOMNode* aNode);
   nsresult GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, int32_t *outOffset);
   bool IsRoot(nsIDOMNode* aNode);
   bool IsFirstNode(nsIDOMNode *aNode);
   bool IsLastNode(nsIDOMNode *aNode);
   bool IsEmptyTextContent(nsIDOMNode* aNode);
@@ -1401,29 +1399,29 @@ nsHTMLCopyEncoder::Init(nsIDOMDocument* 
 }
 
 NS_IMETHODIMP
 nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
 {
   // check for text widgets: we need to recognize these so that
   // we don't tweak the selection to be outside of the magic
   // div that ender-lite text widgets are embedded in.
-  
-  if (!aSelection) 
+
+  if (!aSelection)
     return NS_ERROR_NULL_POINTER;
-  
+
   nsCOMPtr<nsIDOMRange> range;
   nsCOMPtr<nsIDOMNode> commonParent;
   Selection* selection = aSelection->AsSelection();
   uint32_t rangeCount = selection->RangeCount();
 
   // if selection is uninitialized return
   if (!rangeCount)
     return NS_ERROR_FAILURE;
-  
+
   // we'll just use the common parent of the first range.  Implicit assumption
   // here that multi-range selections are table cell selections, in which case
   // the common parent is somewhere in the table and we don't really care where.
   nsresult rv = aSelection->GetRangeAt(0, getter_AddRefs(range));
   NS_ENSURE_SUCCESS(rv, rv);
   if (!range)
     return NS_ERROR_NULL_POINTER;
   range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
@@ -1476,50 +1474,50 @@ nsHTMLCopyEncoder::SetSelection(nsISelec
         mIsTextWidget = true;
         break;
       }
     }
 #endif
   }
 
   // normalize selection if we are not in a widget
-  if (mIsTextWidget) 
+  if (mIsTextWidget)
   {
     mSelection = aSelection;
     mMimeType.AssignLiteral("text/plain");
     return NS_OK;
   }
 
   // also consider ourselves in a text widget if we can't find an html document
   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
   if (!(htmlDoc && mDocument->IsHTMLDocument())) {
     mIsTextWidget = true;
     mSelection = aSelection;
     // mMimeType is set to text/plain when encoding starts.
     return NS_OK;
   }
-  
+
   // there's no Clone() for selection! fix...
   //nsresult rv = aSelection->Clone(getter_AddRefs(mSelection);
   //NS_ENSURE_SUCCESS(rv, rv);
   NS_NewDomSelection(getter_AddRefs(mSelection));
   NS_ENSURE_TRUE(mSelection, NS_ERROR_FAILURE);
-  
+
   // loop thru the ranges in the selection
   for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
     range = selection->GetRangeAt(rangeIdx);
     NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
     nsCOMPtr<nsIDOMRange> myRange;
     range->CloneRange(getter_AddRefs(myRange));
     NS_ENSURE_TRUE(myRange, NS_ERROR_FAILURE);
 
     // adjust range to include any ancestors who's children are entirely selected
     rv = PromoteRange(myRange);
     NS_ENSURE_SUCCESS(rv, rv);
-    
+
     rv = mSelection->AddRange(myRange);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1539,60 +1537,60 @@ nsHTMLCopyEncoder::EncodeToStringWithCon
   nsresult rv = EncodeToString(aEncodedString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // do not encode any context info or range hints if we are in a text widget.
   if (mIsTextWidget) return NS_OK;
 
   // now encode common ancestors into aContextString.  Note that the common ancestors
   // will be for the last range in the selection in the case of multirange selections.
-  // encoding ancestors every range in a multirange selection in a way that could be 
+  // encoding ancestors every range in a multirange selection in a way that could be
   // understood by the paste code would be a lot more work to do.  As a practical matter,
   // selections are single range, and the ones that aren't are table cell selections
   // where all the cells are in the same table.
 
   // leaf of ancestors might be text node.  If so discard it.
   int32_t count = mCommonAncestors.Length();
   int32_t i;
   nsCOMPtr<nsINode> node;
   if (count > 0)
     node = mCommonAncestors.ElementAt(0);
 
-  if (node && IsTextNode(node)) 
+  if (node && IsTextNode(node))
   {
     mCommonAncestors.RemoveElementAt(0);
     // don't forget to adjust range depth info
     if (mStartDepth) mStartDepth--;
     if (mEndDepth) mEndDepth--;
     // and the count
     count--;
   }
-  
+
   i = count;
   while (i > 0)
   {
     node = mCommonAncestors.ElementAt(--i);
     SerializeNodeStart(node, 0, -1, aContextString);
   }
   //i = 0; guaranteed by above
   while (i < count)
   {
     node = mCommonAncestors.ElementAt(i++);
     SerializeNodeEnd(node, aContextString);
   }
 
-  // encode range info : the start and end depth of the selection, where the depth is 
+  // encode range info : the start and end depth of the selection, where the depth is
   // distance down in the parent hierarchy.  Later we will need to add leading/trailing
   // whitespace info to this.
   nsAutoString infoString;
   infoString.AppendInt(mStartDepth);
   infoString.Append(char16_t(','));
   infoString.AppendInt(mEndDepth);
   aInfoString = infoString;
-  
+
   return NS_OK;
 }
 
 
 bool
 nsHTMLCopyEncoder::IncludeInContext(nsINode *aNode)
 {
   nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
@@ -1624,82 +1622,81 @@ nsHTMLCopyEncoder::IncludeInContext(nsIN
                                       nsGkAtoms::h2,
                                       nsGkAtoms::h3,
                                       nsGkAtoms::h4,
                                       nsGkAtoms::h5,
                                       nsGkAtoms::h6);
 }
 
 
-nsresult 
+nsresult
 nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
 {
   if (!inRange) return NS_ERROR_NULL_POINTER;
   nsresult rv;
   nsCOMPtr<nsIDOMNode> startNode, endNode, common;
   int32_t startOffset, endOffset;
-  
+
   rv = inRange->GetCommonAncestorContainer(getter_AddRefs(common));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = inRange->GetStartContainer(getter_AddRefs(startNode));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = inRange->GetStartOffset(&startOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = inRange->GetEndContainer(getter_AddRefs(endNode));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = inRange->GetEndOffset(&endOffset);
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   nsCOMPtr<nsIDOMNode> opStartNode;
   nsCOMPtr<nsIDOMNode> opEndNode;
   int32_t opStartOffset, opEndOffset;
-  nsCOMPtr<nsIDOMRange> opRange;
-  
-  // examine range endpoints.  
+
+  // examine range endpoints.
   rv = GetPromotedPoint( kStart, startNode, startOffset, address_of(opStartNode), &opStartOffset, common);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = GetPromotedPoint( kEnd, endNode, endOffset, address_of(opEndNode), &opEndOffset, common);
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   // if both range endpoints are at the common ancestor, check for possible inclusion of ancestors
   if ( (opStartNode == common) && (opEndNode == common) )
   {
     rv = PromoteAncestorChain(address_of(opStartNode), &opStartOffset, &opEndOffset);
     NS_ENSURE_SUCCESS(rv, rv);
     opEndNode = opStartNode;
   }
-  
+
   // set the range to the new values
   rv = inRange->SetStart(opStartNode, opStartOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = inRange->SetEnd(opEndNode, opEndOffset);
   return rv;
-} 
+}
 
 
 // PromoteAncestorChain will promote a range represented by [{*ioNode,*ioStartOffset} , {*ioNode,*ioEndOffset}]
 // The promotion is different from that found in getPromotedPoint: it will only promote one endpoint if it can
 // promote the other.  Thus, instead of having a startnode/endNode, there is just the one ioNode.
 nsresult
-nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode, 
-                                        int32_t *ioStartOffset, 
-                                        int32_t *ioEndOffset) 
+nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
+                                        int32_t *ioStartOffset,
+                                        int32_t *ioEndOffset)
 {
   if (!ioNode || !ioStartOffset || !ioEndOffset) return NS_ERROR_NULL_POINTER;
 
   nsresult rv = NS_OK;
   bool done = false;
 
   nsCOMPtr<nsIDOMNode> frontNode, endNode, parent;
   int32_t frontOffset, endOffset;
 
   //save the editable state of the ioNode, so we don't promote an ancestor if it has different editable state
   nsCOMPtr<nsINode> node = do_QueryInterface(*ioNode);
   bool isEditable = node->IsEditable();
-  
+
   // loop for as long as we can promote both endpoints
   while (!done)
   {
     rv = (*ioNode)->GetParentNode(getter_AddRefs(parent));
     if ((NS_FAILED(rv)) || !parent)
       done = true;
     else
     {
@@ -1707,56 +1704,56 @@ nsHTMLCopyEncoder::PromoteAncestorChain(
       // up the hierarchy.
       rv = GetPromotedPoint( kStart, *ioNode, *ioStartOffset, address_of(frontNode), &frontOffset, parent);
       NS_ENSURE_SUCCESS(rv, rv);
       // then we make the same attempt with the endpoint
       rv = GetPromotedPoint( kEnd, *ioNode, *ioEndOffset, address_of(endNode), &endOffset, parent);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsINode> frontINode = do_QueryInterface(frontNode);
-      // if both endpoints were promoted one level and isEditable is the same as the original node, 
+      // if both endpoints were promoted one level and isEditable is the same as the original node,
       // keep looping - otherwise we are done.
       if ( (frontNode != parent) || (endNode != parent) || (frontINode->IsEditable() != isEditable) )
         done = true;
       else
       {
-        *ioNode = frontNode;  
+        *ioNode = frontNode;
         *ioStartOffset = frontOffset;
         *ioEndOffset = endOffset;
       }
     }
   }
   return rv;
 }
 
 nsresult
-nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset, 
+nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset,
                                   nsCOMPtr<nsIDOMNode> *outNode, int32_t *outOffset, nsIDOMNode *common)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIDOMNode> node = aNode;
   nsCOMPtr<nsIDOMNode> parent = aNode;
   int32_t offset = aOffset;
   bool    bResetPromotion = false;
-  
+
   // default values
   *outNode = node;
   *outOffset = offset;
 
-  if (common == node) 
+  if (common == node)
     return NS_OK;
-    
+
   if (aWhere == kStart)
   {
     // some special casing for text nodes
     nsCOMPtr<nsINode> t = do_QueryInterface(aNode);
     if (IsTextNode(t))
     {
       // if not at beginning of text node, we are done
-      if (offset >  0) 
+      if (offset >  0)
       {
         // unless everything before us in just whitespace.  NOTE: we need a more
         // general solution that truly detects all cases of non-significant
         // whitesace with no false alarms.
         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
         nsAutoString text;
         nodeAsText->SubstringData(0, offset, text);
         text.CompressWhitespace();
@@ -1769,17 +1766,17 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endp
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else
     {
       node = GetChildAt(parent,offset);
     }
     if (!node) node = parent;
 
-    // finding the real start for this point.  look up the tree for as long as we are the 
+    // finding the real start for this point.  look up the tree for as long as we are the
     // first node in the container, and as long as we haven't hit the body node.
     if (!IsRoot(node) && (parent != common))
     {
       rv = GetNodeLocation(node, address_of(parent), &offset);
       NS_ENSURE_SUCCESS(rv, rv);
       if (offset == -1) return NS_OK; // we hit generated content; STOP
       nsIParserService *parserService = nsContentUtils::GetParserService();
       if (!parserService)
@@ -1793,44 +1790,44 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endp
           {
             bool isBlock = false;
             parserService->IsBlock(parserService->HTMLAtomTagToId(
                                      content->NodeInfo()->NameAtom()), isBlock);
             if (isBlock)
             {
               bResetPromotion = false;
             }
-          }   
+          }
         }
-         
+
         node = parent;
         rv = GetNodeLocation(node, address_of(parent), &offset);
         NS_ENSURE_SUCCESS(rv, rv);
         if (offset == -1)  // we hit generated content; STOP
         {
           // back up a bit
           parent = node;
           offset = 0;
           break;
         }
-      } 
+      }
       if (bResetPromotion)
       {
         *outNode = aNode;
         *outOffset = aOffset;
       }
       else
       {
         *outNode = parent;
         *outOffset = offset;
       }
       return rv;
     }
   }
-  
+
   if (aWhere == kEnd)
   {
     // some special casing for text nodes
     nsCOMPtr<nsINode> n = do_QueryInterface(aNode);
     if (IsTextNode(n))
     {
       // if not at end of text node, we are done
       uint32_t len = n->Length();
@@ -1851,18 +1848,18 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endp
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else
     {
       if (offset) offset--; // we want node _before_ offset
       node = GetChildAt(parent,offset);
     }
     if (!node) node = parent;
-    
-    // finding the real end for this point.  look up the tree for as long as we are the 
+
+    // finding the real end for this point.  look up the tree for as long as we are the
     // last node in the container, and as long as we haven't hit the body node.
     if (!IsRoot(node) && (parent != common))
     {
       rv = GetNodeLocation(node, address_of(parent), &offset);
       NS_ENSURE_SUCCESS(rv, rv);
       if (offset == -1) return NS_OK; // we hit generated content; STOP
       nsIParserService *parserService = nsContentUtils::GetParserService();
       if (!parserService)
@@ -1876,76 +1873,76 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endp
           {
             bool isBlock = false;
             parserService->IsBlock(parserService->HTMLAtomTagToId(
                                      content->NodeInfo()->NameAtom()), isBlock);
             if (isBlock)
             {
               bResetPromotion = false;
             }
-          }   
+          }
         }
-          
+
         node = parent;
         rv = GetNodeLocation(node, address_of(parent), &offset);
         NS_ENSURE_SUCCESS(rv, rv);
         if (offset == -1)  // we hit generated content; STOP
         {
           // back up a bit
           parent = node;
           offset = 0;
           break;
         }
-      } 
+      }
       if (bResetPromotion)
       {
         *outNode = aNode;
         *outOffset = aOffset;
       }
       else
       {
         *outNode = parent;
         offset++;  // add one since this in an endpoint - want to be AFTER node.
         *outOffset = offset;
       }
       return rv;
     }
   }
-  
+
   return rv;
 }
 
-nsCOMPtr<nsIDOMNode> 
+nsCOMPtr<nsIDOMNode>
 nsHTMLCopyEncoder::GetChildAt(nsIDOMNode *aParent, int32_t aOffset)
 {
   nsCOMPtr<nsIDOMNode> resultNode;
-  
-  if (!aParent) 
+
+  if (!aParent)
     return resultNode;
-  
+
   nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
   NS_PRECONDITION(content, "null content in nsHTMLCopyEncoder::GetChildAt");
 
   resultNode = do_QueryInterface(content->GetChildAt(aOffset));
 
   return resultNode;
 }
 
-bool 
+bool
 nsHTMLCopyEncoder::IsMozBR(nsIDOMNode* aNode)
 {
   MOZ_ASSERT(aNode);
   nsCOMPtr<Element> element = do_QueryInterface(aNode);
   return element &&
          element->IsHTMLElement(nsGkAtoms::br) &&
          element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                               NS_LITERAL_STRING("_moz"), eIgnoreCase);
 }
 
-nsresult 
+nsresult
 nsHTMLCopyEncoder::GetNodeLocation(nsIDOMNode *inChild,
                                    nsCOMPtr<nsIDOMNode> *outParent,
                                    int32_t *outOffset)
 {
   NS_ASSERTION((inChild && outParent && outOffset), "bad args");
   nsresult result = NS_ERROR_NULL_POINTER;
   if (inChild && outParent && outOffset)
   {
@@ -1981,91 +1978,91 @@ nsHTMLCopyEncoder::IsRoot(nsIDOMNode* aN
 }
 
 bool
 nsHTMLCopyEncoder::IsFirstNode(nsIDOMNode *aNode)
 {
   nsCOMPtr<nsIDOMNode> parent;
   int32_t offset, j=0;
   nsresult rv = GetNodeLocation(aNode, address_of(parent), &offset);
-  if (NS_FAILED(rv)) 
+  if (NS_FAILED(rv))
   {
     NS_NOTREACHED("failure in IsFirstNode");
     return false;
   }
   if (offset == 0)  // easy case, we are first dom child
     return true;
-  if (!parent)  
+  if (!parent)
     return true;
-  
+
   // need to check if any nodes before us are really visible.
   // Mike wrote something for me along these lines in nsSelectionController,
   // but I don't think it's ready for use yet - revisit.
-  // HACK: for now, simply consider all whitespace text nodes to be 
+  // HACK: for now, simply consider all whitespace text nodes to be
   // invisible formatting nodes.
   nsCOMPtr<nsIDOMNodeList> childList;
   nsCOMPtr<nsIDOMNode> child;
 
   rv = parent->GetChildNodes(getter_AddRefs(childList));
-  if (NS_FAILED(rv) || !childList) 
+  if (NS_FAILED(rv) || !childList)
   {
     NS_NOTREACHED("failure in IsFirstNode");
     return true;
   }
   while (j < offset)
   {
     childList->Item(j, getter_AddRefs(child));
-    if (!IsEmptyTextContent(child)) 
+    if (!IsEmptyTextContent(child))
       return false;
     j++;
   }
   return true;
 }
 
 
 bool
 nsHTMLCopyEncoder::IsLastNode(nsIDOMNode *aNode)
 {
   nsCOMPtr<nsIDOMNode> parent;
   int32_t offset,j;
   nsresult rv = GetNodeLocation(aNode, address_of(parent), &offset);
-  if (NS_FAILED(rv)) 
+  if (NS_FAILED(rv))
   {
     NS_NOTREACHED("failure in IsLastNode");
     return false;
   }
   nsCOMPtr<nsINode> parentNode = do_QueryInterface(parent);
   if (!parentNode) {
     return true;
   }
 
   uint32_t numChildren = parentNode->Length();
   if (offset+1 == (int32_t)numChildren) // easy case, we are last dom child
     return true;
   // need to check if any nodes after us are really visible.
   // Mike wrote something for me along these lines in nsSelectionController,
   // but I don't think it's ready for use yet - revisit.
-  // HACK: for now, simply consider all whitespace text nodes to be 
+  // HACK: for now, simply consider all whitespace text nodes to be
   // invisible formatting nodes.
   j = (int32_t)numChildren-1;
   nsCOMPtr<nsIDOMNodeList>childList;
   nsCOMPtr<nsIDOMNode> child;
   rv = parent->GetChildNodes(getter_AddRefs(childList));
-  if (NS_FAILED(rv) || !childList) 
+  if (NS_FAILED(rv) || !childList)
   {
     NS_NOTREACHED("failure in IsLastNode");
     return true;
   }
   while (j > offset)
   {
     childList->Item(j, getter_AddRefs(child));
     j--;
-    if (IsMozBR(child))  // we ignore trailing moz BRs.  
+    if (IsMozBR(child))  // we ignore trailing moz BRs.
       continue;
-    if (!IsEmptyTextContent(child)) 
+    if (!IsEmptyTextContent(child))
       return false;
   }
   return true;
 }
 
 bool
 nsHTMLCopyEncoder::IsEmptyTextContent(nsIDOMNode* aNode)
 {
@@ -2100,9 +2097,8 @@ nsHTMLCopyEncoder::GetImmediateContextCo
                                       nsGkAtoms::tfoot,
                                       nsGkAtoms::table)) {
       break;
     }
     ++j;
   }
   return j;
 }
-
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -14015,17 +14015,16 @@ nsGlobalWindow::GetReturnValue(JSContext
                             aError, );
 }
 
 NS_IMETHODIMP
 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
 
-  nsCOMPtr<nsIVariant> result;
   if (!mReturnValue) {
     nsCOMPtr<nsIVariant> variant = CreateVoidVariant();
     variant.forget(aRetVal);
     return NS_OK;
   }
   return mReturnValue->Get(nsContentUtils::SubjectPrincipal(), aRetVal);
 }
 
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -215,17 +215,16 @@ class BlobURLsReporter final : public ns
         BlobImpl* blobImpl = iter.UserData()->mBlobImpl;
         MOZ_ASSERT(blobImpl);
 
         NS_NAMED_LITERAL_CSTRING(desc,
           "A blob URL allocated with URL.createObjectURL; the referenced "
           "blob cannot be freed until all URLs for it have been explicitly "
           "invalidated with URL.revokeObjectURL.");
         nsAutoCString path, url, owner, specialDesc;
-        nsCOMPtr<nsIURI> principalURI;
         uint64_t size = 0;
         uint32_t refCount = 1;
         DebugOnly<bool> blobImplWasCounted;
 
         blobImplWasCounted = refCounts.Get(blobImpl, &refCount);
         MOZ_ASSERT(blobImplWasCounted);
         MOZ_ASSERT(refCount > 0);
 
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -258,17 +258,16 @@ nsLocation::GetWritableURI(nsIURI** aURI
 }
 
 nsresult
 nsLocation::SetURI(nsIURI* aURI, bool aReplace)
 {
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
   if (docShell) {
     nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
-    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
 
     if(NS_FAILED(CheckURL(aURI, getter_AddRefs(loadInfo))))
       return NS_ERROR_FAILURE;
 
     if (aReplace) {
       loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
     } else {
       loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContent);
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -2655,17 +2655,16 @@ nsScriptLoader::PrepareLoadedRequest(nsS
     // acceptable.
     nsAutoCString mimeType;
     channel->GetContentType(mimeType);
     NS_ConvertUTF8toUTF16 typeString(mimeType);
     if (!nsContentUtils::IsJavascriptMIMEType(typeString)) {
       return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsIURI> baseURL;
     channel->GetURI(getter_AddRefs(request->mBaseURL));
 
     // Attempt to compile off main thread.
     rv = AttemptAsyncScriptCompile(request);
     if (NS_SUCCEEDED(rv)) {
       return rv;
     }
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -3113,18 +3113,16 @@ nsDOMDeviceStorage::AddOrAppendNamed(Blo
   MOZ_ASSERT(IsOwningThread());
   MOZ_ASSERT(aCreate || !aPath.IsEmpty());
 
   // if the blob is null here, bail
   if (!aBlob) {
     return nullptr;
   }
 
-  nsCOMPtr<nsIRunnable> r;
-
   if (IsFullPath(aPath)) {
     nsString storagePath;
     RefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       return CreateAndRejectDOMRequest(POST_ERROR_EVENT_UNKNOWN, aRv);
     }
 
     return ds->AddOrAppendNamed(aBlob, storagePath, aCreate, aRv);
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -609,17 +609,16 @@ HTMLSelectElement::Add(nsIDOMHTMLElement
   if (dataType == nsIDataType::VTYPE_EMPTY ||
       dataType == nsIDataType::VTYPE_VOID) {
     ErrorResult error;
     Add(*htmlElement, (nsGenericHTMLElement*)nullptr, error);
     return error.StealNSResult();
   }
 
   nsCOMPtr<nsISupports> supports;
-  nsCOMPtr<nsIDOMHTMLElement> beforeElement;
 
   // whether aBefore is nsIDOMHTMLElement...
   if (NS_SUCCEEDED(aBefore->GetAsISupports(getter_AddRefs(supports)))) {
     nsCOMPtr<nsIContent> beforeElement = do_QueryInterface(supports);
     nsGenericHTMLElement* beforeHTMLElement =
       nsGenericHTMLElement::FromContentOrNull(beforeElement);
 
     NS_ENSURE_TRUE(beforeHTMLElement, NS_ERROR_DOM_SYNTAX_ERR);
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -3118,19 +3118,16 @@ nsGenericHTMLElement::GetInnerText(mozil
   }
 
   nsRange::GetInnerTextNoFlush(aValue, aError, this, 0, this, GetChildCount());
 }
 
 void
 nsGenericHTMLElement::SetInnerText(const nsAString& aValue)
 {
-  // Fire DOMNodeRemoved mutation events before we do anything else.
-  nsCOMPtr<nsIContent> kungFuDeathGrip;
-
   // Batch possible DOMSubtreeModified events.
   mozAutoSubtreeModified subtree(OwnerDoc(), nullptr);
   FireNodeRemovedForChildren();
 
   // Might as well stick a batch around this since we're performing several
   // mutations.
   mozAutoDocUpdate updateBatch(GetComposedDoc(),
     UPDATE_CONTENT_MODEL, true);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1298,23 +1298,32 @@ StartMacOSContentSandbox()
   }
 
   nsAutoCString tempDirPath;
   rv = tempDir->GetNativePath(tempDirPath);
   if (NS_FAILED(rv)) {
     MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
   }
 
+  nsCOMPtr<nsIFile> profileDir;
+  ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir));
+  nsCString profileDirPath;
+  rv = profileDir->GetNativePath(profileDirPath);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH("Failed to get profile path");
+  }
+
   MacSandboxInfo info;
   info.type = MacSandboxType_Content;
   info.level = info.level = sandboxLevel;
   info.appPath.assign(appPath.get());
   info.appBinaryPath.assign(appBinaryPath.get());
   info.appDir.assign(appDir.get());
   info.appTempDir.assign(tempDirPath.get());
+  info.profileDir.assign(profileDirPath.get());
 
   std::string err;
   if (!mozilla::StartMacSandbox(info, err)) {
     NS_WARNING(err.c_str());
     MOZ_CRASH("sandbox_init() failed");
   }
 
   return true;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -16,16 +16,19 @@
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsTHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #include "nsWeakPtr.h"
 #include "nsIWindowProvider.h"
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsIFile.h"
+#endif
 
 struct ChromePackage;
 class nsIObserver;
 struct SubstitutionMapping;
 struct OverrideMapping;
 class nsIDomainPolicy;
 
 namespace mozilla {
@@ -109,16 +112,29 @@ public:
   }
 
   void SetProcessName(const nsAString& aName, bool aDontOverride = false);
 
   void GetProcessName(nsAString& aName) const;
 
   void GetProcessName(nsACString& aName) const;
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  void GetProfileDir(nsIFile** aProfileDir) const
+  {
+    *aProfileDir = mProfileDir;
+    NS_IF_ADDREF(*aProfileDir);
+  }
+
+  void SetProfileDir(nsIFile* aProfileDir)
+  {
+    mProfileDir = aProfileDir;
+  }
+#endif
+
   bool IsAlive() const;
 
   static void AppendProcessId(nsACString& aName);
 
   ContentBridgeParent* GetLastBridge()
   {
     MOZ_ASSERT(mLastBridge);
     ContentBridgeParent* parent = mLastBridge;
@@ -676,16 +692,20 @@ private:
   bool mIsAlive;
   nsString mProcessName;
 
   static ContentChild* sSingleton;
 
   nsCOMPtr<nsIDomainPolicy> mPolicy;
   nsCOMPtr<nsITimer> mForceKillTimer;
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  nsCOMPtr<nsIFile> mProfileDir;
+#endif
+
   // Hashtable to keep track of the pending GetFilesHelper objects.
   // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
   // received.
  nsRefPtrHashtable<nsIDHashKey, GetFilesHelperChild> mGetFilesPendingRequests;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1080,17 +1080,16 @@ ContentParent::CreateBrowserOrApp(const 
 
   nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
   TabId openerTabId;
   if (docShell) {
     openerTabId = TabParent::GetTabIdFrom(docShell);
   }
 
   if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) {
-    RefPtr<TabParent> tp;
     RefPtr<nsIContentParent> constructorSender;
     if (isInContentProcess) {
       MOZ_ASSERT(aContext.IsMozBrowserElement());
       constructorSender = CreateContentBridgeParent(aContext, initialPriority,
                                                     openerTabId, &tabId);
     } else {
       if (aOpenerContentParent) {
         constructorSender = aOpenerContentParent;
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -109,26 +109,45 @@ SetUpSandboxEnvironment()
 #endif
 
 void
 ContentProcess::SetAppDir(const nsACString& aPath)
 {
   mXREEmbed.SetAppDir(aPath);
 }
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+void
+ContentProcess::SetProfile(const nsACString& aProfile)
+{
+  bool flag;
+  nsresult rv =
+    XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir));
+  if (NS_FAILED(rv) ||
+      NS_FAILED(mProfileDir->Exists(&flag)) || !flag) {
+    NS_WARNING("Invalid profile directory passed to content process.");
+    mProfileDir = nullptr;
+  }
+}
+#endif
+
 bool
 ContentProcess::Init()
 {
     mContent.Init(IOThreadChild::message_loop(),
                   ParentPid(),
                   IOThreadChild::channel());
     mXREEmbed.Start();
     mContent.InitXPCOM();
     mContent.InitGraphicsDeviceData();
 
+#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+    mContent.SetProfileDir(mProfileDir);
+#endif
+
 #if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
     SetUpSandboxEnvironment();
 #endif
 
     return true;
 }
 
 // Note: CleanUp() never gets called in non-debug builds because we exit early
--- a/dom/ipc/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -34,19 +34,28 @@ public:
   ~ContentProcess()
   { }
 
   virtual bool Init() override;
   virtual void CleanUp() override;
 
   void SetAppDir(const nsACString& aPath);
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  void SetProfile(const nsACString& aProfile);
+#endif
+
 private:
   ContentChild mContent;
   mozilla::ipc::ScopedXREEmbed mXREEmbed;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  nsCOMPtr<nsIFile> mProfileDir;
+#endif
+
 #if defined(XP_WIN)
   // This object initializes and configures COM.
   mozilla::mscom::MainThreadRuntime mCOMRuntime;
 #endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -393,49 +393,65 @@ OfflineClockDriver::GetCurrentTimeStamp(
 
 void
 SystemClockDriver::WaitForNextIteration()
 {
   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
 
   PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
   TimeStamp now = TimeStamp::Now();
-  if (mGraphImpl->mNeedAnotherIteration) {
+
+  // This lets us avoid hitting the Atomic twice when we know we won't sleep
+  bool another = mGraphImpl->mNeedAnotherIteration; // atomic
+  if (!another) {
+    mGraphImpl->mGraphDriverAsleep = true; // atomic
+    mWaitState = WAITSTATE_WAITING_INDEFINITELY;
+  }
+  // NOTE: mNeedAnotherIteration while also atomic may have changed before
+  // we could set mGraphDriverAsleep, so we must re-test it.
+  // (EnsureNextIteration sets mNeedAnotherIteration, then tests
+  // mGraphDriverAsleep
+  if (another || mGraphImpl->mNeedAnotherIteration) { // atomic
     int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
       int64_t((now - mCurrentTimeStamp).ToMilliseconds());
     // Make sure timeoutMS doesn't overflow 32 bits by waking up at
     // least once a minute, if we need to wake up at all
     timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
     timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
-    STREAM_LOG(LogLevel::Verbose, ("Waiting for next iteration; at %f, timeout=%f", (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
+    STREAM_LOG(LogLevel::Verbose,
+               ("Waiting for next iteration; at %f, timeout=%f",
+                (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
     if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
       mGraphImpl->mGraphDriverAsleep = false; // atomic
     }
     mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
-  } else {
-    mGraphImpl->mGraphDriverAsleep = true; // atomic
-    mWaitState = WAITSTATE_WAITING_INDEFINITELY;
   }
   if (timeout > 0) {
     mGraphImpl->GetMonitor().Wait(timeout);
     STREAM_LOG(LogLevel::Verbose, ("Resuming after timeout; at %f, elapsed=%f",
           (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
           (TimeStamp::Now() - now).ToSeconds()));
   }
 
   if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
     mGraphImpl->mGraphDriverAsleep = false; // atomic
   }
+  // Note: this can race against the EnsureNextIteration setting
+  // WAITSTATE_RUNNING and setting mGraphDriverAsleep to false, so you can
+  // have an iteration with WAITSTATE_WAKING_UP instead of RUNNING.
   mWaitState = WAITSTATE_RUNNING;
-  mGraphImpl->mNeedAnotherIteration = false;
+  mGraphImpl->mNeedAnotherIteration = false; // atomic
 }
 
 void SystemClockDriver::WakeUp()
 {
   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
+  // Note: this can race against the thread setting WAITSTATE_RUNNING and
+  // setting mGraphDriverAsleep to false, so you can have an iteration
+  // with WAITSTATE_WAKING_UP instead of RUNNING.
   mWaitState = WAITSTATE_WAKING_UP;
   mGraphImpl->mGraphDriverAsleep = false; // atomic
   mGraphImpl->GetMonitor().Notify();
 }
 
 OfflineClockDriver::OfflineClockDriver(MediaStreamGraphImpl* aGraphImpl, GraphTime aSlice)
   : ThreadedDriver(aGraphImpl),
     mSlice(aSlice)
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -523,18 +523,16 @@ MediaDecoderReader::Shutdown()
   ReleaseResources();
   mDuration.DisconnectIfConnected();
   mBuffered.DisconnectAll();
   mIsSuspended.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
   mWatchManager.Shutdown();
 
-  RefPtr<ShutdownPromise> p;
-
   mDecoder = nullptr;
 
   ReaderQueue::Instance().Remove(this);
 
   return mTaskQueue->BeginShutdown();
 }
 
 } // namespace mozilla
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -517,16 +517,18 @@ public:
   Monitor& GetMonitor()
   {
     return mMonitor;
   }
 
   void EnsureNextIteration()
   {
     mNeedAnotherIteration = true; // atomic
+    // Note: GraphDriver must ensure that there's no race on setting
+    // mNeedAnotherIteration and mGraphDriverAsleep -- see WaitForNextIteration()
     if (mGraphDriverAsleep) { // atomic
       MonitorAutoLock mon(mMonitor);
       CurrentDriver()->WakeUp(); // Might not be the same driver; might have woken already
     }
   }
 
   void EnsureNextIterationLocked()
   {
--- a/dom/mobilemessage/MobileMessageManager.cpp
+++ b/dom/mobilemessage/MobileMessageManager.cpp
@@ -31,17 +31,17 @@
 #include "nsIMobileMessageService.h"
 #include "nsIObserverService.h"
 #include "nsISmsService.h"
 #include "nsServiceManagerUtils.h" // For do_GetService()
 
 // Service instantiation
 #include "ipc/SmsIPCService.h"
 #include "MobileMessageService.h"
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_WEBSMS_BACKEND)
 #include "android/MobileMessageDatabaseService.h"
 #include "android/SmsService.h"
 #elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
 #include "nsIGonkMobileMessageDatabaseService.h"
 #include "nsIGonkSmsService.h"
 #endif
 #include "nsXULAppAPI.h" // For XRE_GetProcessType()
 
@@ -848,34 +848,34 @@ MobileMessageManager::SetSmscAddress(con
 already_AddRefed<nsISmsService>
 NS_CreateSmsService()
 {
   nsCOMPtr<nsISmsService> smsService;
 
   if (XRE_IsContentProcess()) {
     smsService = SmsIPCService::GetSingleton();
   } else {
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_WEBSMS_BACKEND)
     smsService = new SmsService();
 #elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
     smsService = do_GetService(GONK_SMSSERVICE_CONTRACTID);
 #endif
   }
 
   return smsService.forget();
 }
 
 already_AddRefed<nsIMobileMessageDatabaseService>
 NS_CreateMobileMessageDatabaseService()
 {
   nsCOMPtr<nsIMobileMessageDatabaseService> mobileMessageDBService;
   if (XRE_IsContentProcess()) {
     mobileMessageDBService = SmsIPCService::GetSingleton();
   } else {
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_WEBSMS_BACKEND)
     mobileMessageDBService = new MobileMessageDatabaseService();
 #elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
     mobileMessageDBService =
       do_CreateInstance(GONK_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
 #endif
   }
 
   return mobileMessageDBService.forget();
--- a/dom/mobilemessage/moz.build
+++ b/dom/mobilemessage/moz.build
@@ -11,17 +11,17 @@ XPCSHELL_TESTS_MANIFESTS += ['tests/xpcs
 
 EXPORTS.mozilla.dom.mobilemessage += [
     'Constants.h',            # Required by almost all cpp files
     'ipc/SmsChild.h',
     'ipc/SmsParent.h',
     'Types.h',                # Required by IPDL SmsTypes.h
 ]
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android' and CONFIG['MOZ_WEBSMS_BACKEND']:
     SOURCES += [
         'android/MobileMessageDatabaseService.cpp',
         'android/SmsManager.cpp',
         'android/SmsService.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
     EXTRA_JS_MODULES += [
         'gonk/mms_consts.js',
new file mode 100644
--- /dev/null
+++ b/dom/permission/tests/file_empty.html
@@ -0,0 +1,2 @@
+<h1>I'm just a support file</h1>
+<p>I get loaded to do permission testing.</p>
\ No newline at end of file
--- a/dom/permission/tests/mochitest.ini
+++ b/dom/permission/tests/mochitest.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   file_framework.js
   file_shim.html
+  file_empty.html
 
 [test_alarms.html]
 skip-if = true
 [test_browser.html]
 skip-if = true
 [test_embed-apps.html]
 skip-if = true || e10s #Bug 931116, b2g desktop specific, initial triage ### Bug 1255339: blacklist because no more mozApps
 [test_idle.html]
--- a/dom/permission/tests/test_permissions_api.html
+++ b/dom/permission/tests/test_permissions_api.html
@@ -1,146 +1,206 @@
 <!--
   Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!DOCTYPE HTML>
 <html>
+
 <head>
   <meta charset="utf-8">
   <title>Test for Permissions API</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 </head>
-<body onload='runTests()'>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-'use strict';
+
+<body>
+  <pre id="test"></pre>
+  <script type="application/javascript;version=1.8">
+  /*globals SpecialPowers, SimpleTest, is, ok, */
+  'use strict';
+
+  const {
+    UNKNOWN_ACTION,
+    PROMPT_ACTION,
+    ALLOW_ACTION,
+    DENY_ACTION
+  } = SpecialPowers.Ci.nsIPermissionManager;
+
+  SimpleTest.waitForExplicitFinish();
 
-let { UNKNOWN_ACTION, PROMPT_ACTION, ALLOW_ACTION, DENY_ACTION } =
-  SpecialPowers.Ci.nsIPermissionManager;
+  const PERMISSIONS = [{
+    name: 'geolocation',
+    type: 'geo'
+  }, {
+    name: 'notifications',
+    type: 'desktop-notification'
+  }, {
+    name: 'push',
+    type: 'desktop-notification'
+  }, ];
 
-SimpleTest.waitForExplicitFinish();
+  const UNSUPPORTED_PERMISSIONS = [
+    'foobarbaz', // Not in spec, for testing only.
+    'midi',
+  ];
 
-const PERMISSIONS = [
-  { name: 'geolocation', perm: 'geo' },
-  { name: 'notifications', perm: 'desktop-notification' },
-  { name: 'push', perm: 'desktop-notification' },
-];
+  // Create a closure, so that tests are run on the correct window object.
+  function createPermissionTester(aWindow) {
+    return {
+      setPermissions(allow) {
+        const permissions = PERMISSIONS.map(({ type }) => {
+          return {
+            type,
+            allow,
+            'context': aWindow.document
+          };
+        });
+        return new Promise((resolve) => {
+          SpecialPowers.popPermissions(() => {
+            SpecialPowers.pushPermissions(permissions, resolve);
+          });
+        });
+      },
+      revokePermissions() {
+        const promisesToRevoke = PERMISSIONS.map(({ name })  => {
+          return aWindow.navigator.permissions
+            .revoke({ name })
+            .then(
+              ({ state }) => is(state, 'prompt', `correct state for '${name}'`),
+              () => ok(false, `revoke should not have rejected for '${name}'`)
+            );
+        });
+        return Promise.all(promisesToRevoke);
+      },
+      revokeUnsupportedPermissions() {
+        const promisesToRevoke = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
+          return aWindow.navigator.permissions
+            .revoke({ name })
+            .then(
+              () => ok(false, `revoke should not have resolved for '${name}'`),
+              error => is(error.name, 'TypeError', `revoke should have thrown TypeError for '${name}'`)
+            );
+        });
+        return Promise.all(promisesToRevoke);
+      },
+      checkPermissions(state) {
+        const promisesToQuery = PERMISSIONS.map(({ name }) => {
+          return aWindow.navigator.permissions
+            .query({ name })
+            .then(
+              () => is(state, state, `correct state for '${name}'`),
+              () => ok(false, `query should not have rejected for '${name}'`)
+            );
+          });
+        return Promise.all(promisesToQuery);
+      },
+      checkUnsupportedPermissions() {
+        const promisesToQuery = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
+          return aWindow.navigator.permissions
+            .query({ name })
+            .then(
+              () => ok(false, `query should not have resolved for '${name}'`),
+              error => {
+                is(error.name, 'TypeError',
+                  `query should have thrown TypeError for '${name}'`);
+              }
+            );
+          });
+        return Promise.all(promisesToQuery);
+      },
+      promiseStateChanged(name, state) {
+        return aWindow.navigator.permissions
+          .query({ name })
+          .then(status => {
+            return new Promise( resolve => {
+              status.onchange = () => {
+                status.onchange = null;
+                is(status.state, state, `state changed for '${name}'`);
+                resolve();
+              };
+            });
+          },
+          () => ok(false, `query should not have rejected for '${name}'`));
+      },
+      testStatusOnChange() {
+        return new Promise((resolve) => {
+          SpecialPowers.popPermissions(() => {
+            const permission = 'geolocation';
+            const promiseGranted = this.promiseStateChanged(permission, 'granted');
+            this.setPermissions(ALLOW_ACTION);
+            promiseGranted.then(() => {
+              const promisePrompt = this.promiseStateChanged(permission, 'prompt');
+              SpecialPowers.popPermissions();
+              return promisePrompt;
+            }).then(resolve);
+          });
+        });
+      },
+      testInvalidQuery() {
+        return aWindow.navigator.permissions
+          .query({ name: 'invalid' })
+          .then(
+            () => ok(false, 'invalid query should not have resolved'),
+            () => ok(true, 'invalid query should have rejected')
+          );
+      },
+      testInvalidRevoke() {
+        return aWindow.navigator.permissions
+          .revoke({ name: 'invalid' })
+          .then(
+            () => ok(false, 'invalid revoke should not have resolved'),
+            () => ok(true, 'invalid revoke should have rejected')
+          );
+      },
+    };
+  }
 
-const UNSUPPORTED_PERMISSIONS = [
-  'foobarbaz',  // Not in spec, for testing only.
-  'midi'
-];
+  function enablePrefs() {
+    const ops = {
+      'set': [
+        ['dom.permissions.revoke.enable', true],
+      ],
+    };
+    return SpecialPowers.pushPrefEnv(ops);
+  }
 
-function setPermissions(action) {
-  let permissions = PERMISSIONS.map(x => {
-    return { 'type': x.perm, 'allow': action, 'context': document };
-  });
-  return new Promise((resolve, reject) => {
-    SpecialPowers.popPermissions(() => {
-      SpecialPowers.pushPermissions(permissions, resolve);
+  function createIframe() {
+    return new Promise((resolve) => {
+      const iframe = document.createElement('iframe');
+      iframe.src = 'file_empty.html';
+      iframe.onload = () => resolve(iframe.contentWindow);
+      document.body.appendChild(iframe);
     });
-  });
-}
-
-function revokePermissions(action) {
-  return Promise.all(PERMISSIONS.map(x =>
-    navigator.permissions.revoke({ name: x.name }).then(
-      result => is(result.state, "prompt", `correct state for '${x.name}'`),
-      error => ok(false, `revoke should not have rejected for '${x.name}'`))
-  ));
-}
-
-function revokeUnsupportedPermissions() {
-  return Promise.all(UNSUPPORTED_PERMISSIONS.map(name =>
-    navigator.permissions.revoke({ name: name }).then(
-      result => ok(false, `revoke should not have resolved for '${name}'`),
-      error => is(error.name, 'TypeError', `revoke should have thrown TypeError for '${name}'`))
-  ));
-}
-
-function checkPermissions(state) {
-  return Promise.all(PERMISSIONS.map(x => {
-    return navigator.permissions.query({ name: x.name }).then(
-      result => is(result.state, state, `correct state for '${x.name}'`),
-      error => ok(false, `query should not have rejected for '${x.name}'`));
-  }));
-}
-
-function checkUnsupportedPermissions() {
-  return Promise.all(UNSUPPORTED_PERMISSIONS.map(name => {
-    return navigator.permissions.query({ name: name }).then(
-      result => ok(false, `query should not have resolved for '${name}'`),
-      error => {
-        is(error.name, 'TypeError',
-           `query should have thrown TypeError for '${name}'`);
+  }
+  debugger;
+  window.onload = () => {
+    enablePrefs()
+      .then(createIframe)
+      .then(createPermissionTester)
+      .then((tester) => {
+        return tester
+          .checkUnsupportedPermissions()
+          .then(() => tester.setPermissions(UNKNOWN_ACTION))
+          .then(() => tester.checkPermissions('prompt'))
+          .then(() => tester.setPermissions(PROMPT_ACTION))
+          .then(() => tester.checkPermissions('prompt'))
+          .then(() => tester.setPermissions(ALLOW_ACTION))
+          .then(() => tester.checkPermissions('granted'))
+          .then(() => tester.setPermissions(DENY_ACTION))
+          .then(() => tester.checkPermissions('denied'))
+          .then(() => tester.testStatusOnChange())
+          .then(() => tester.testInvalidQuery())
+          .then(() => tester.revokeUnsupportedPermissions())
+          .then(() => tester.revokePermissions())
+          .then(() => tester.checkPermissions('prompt'))
+          .then(() => tester.testInvalidRevoke());
+      })
+      .then(SimpleTest.finish)
+      .catch((e) => {
+        ok(false, `Unexpected error ${e}`);
+        SimpleTest.finish();
       });
-  }));
-}
-
-function promiseStateChanged(name, state) {
-  return navigator.permissions.query({ name }).then(
-    status => {
-      return new Promise((resolve, reject) => {
-        status.onchange = () => {
-          status.onchange = null;
-          is(status.state, state, `state changed for '${name}'`);
-          resolve();
-        };
-      });
-    },
-    error => ok(false, `query should not have rejected for '${name}'`));
-}
-
-function testStatusOnChange() {
-  return new Promise((resolve, reject) => {
-    SpecialPowers.popPermissions(() => {
-      let permission = 'geolocation';
-      let promiseGranted = promiseStateChanged(permission, 'granted');
-      setPermissions(ALLOW_ACTION);
-      promiseGranted.then(() => {
-        let promisePrompt = promiseStateChanged(permission, 'prompt');
-        SpecialPowers.popPermissions();
-        return promisePrompt;
-      }).then(resolve);
-    });
-  });
-}
+  };
+  </script>
+</body>
 
-function testInvalidQuery() {
-  navigator.permissions.query({ name: 'invalid' }).then(
-    result => ok(false, 'invalid query should not have resolved'),
-    error => ok(true, 'invalid query should have rejected'));
-}
-
-function testInvalidRevoke() {
-  navigator.permissions.revoke({ name: 'invalid' }).then(
-    result => ok(false, 'invalid revoke should not have resolved'),
-    error => ok(true, 'invalid revoke should have rejected'));
-}
-
-function runTests() {
-  checkUnsupportedPermissions()
-    .then(() => setPermissions(UNKNOWN_ACTION))
-    .then(() => checkPermissions('prompt'))
-    .then(() => setPermissions(PROMPT_ACTION))
-    .then(() => checkPermissions('prompt'))
-    .then(() => setPermissions(ALLOW_ACTION))
-    .then(() => checkPermissions('granted'))
-    .then(() => setPermissions(DENY_ACTION))
-    .then(() => checkPermissions('denied'))
-    .then(testStatusOnChange)
-    .then(testInvalidQuery)
-    .then(revokeUnsupportedPermissions)
-    .then(revokePermissions)
-    .then(() => checkPermissions('prompt'))
-    .then(testInvalidRevoke)
-    .then(SimpleTest.finish)
-    .catch ((e) => {
-      ok(false, 'Unexpected error ' + e);
-      SimpleTest.finish();
-    });
-}
-</script>
-</pre>
-</body>
 </html>
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -406,17 +406,16 @@ nsPluginStreamListenerPeer::SetupPluginC
       return rv;
 
     // Yes, make it unique.
     rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
     if (NS_FAILED(rv))
       return rv;
 
     // create a file output stream to write to...
-    nsCOMPtr<nsIOutputStream> outstream;
     rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
     if (NS_FAILED(rv))
       return rv;
 
     // save the file.
     mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
   }
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -3350,17 +3350,16 @@ PluginInstanceChild::DoAsyncSetWindow(co
 
 bool
 PluginInstanceChild::CreateOptSurface(void)
 {
     MOZ_ASSERT(mSurfaceType != gfxSurfaceType::Max,
                "Need a valid surface type here");
     NS_ASSERTION(!mCurrentSurface, "mCurrentSurfaceActor can get out of sync.");
 
-    RefPtr<gfxASurface> retsurf;
     // Use an opaque surface unless we're transparent and *don't* have
     // a background to source from.
     gfxImageFormat format =
         (mIsTransparent && !mBackground) ? SurfaceFormat::A8R8G8B8_UINT32 :
                                            SurfaceFormat::X8R8G8B8_UINT32;
 
 #ifdef MOZ_X11
     Display* dpy = mWsInfo.display;
@@ -3576,17 +3575,19 @@ PluginInstanceChild::EnsureCurrentBuffer
     }
 #endif
     return true;
 }
 
 void
 PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
 {
+#if defined(MOZ_X11) || defined(XP_WIN)
     RefPtr<gfxASurface> curSurface = mHelperSurface ? mHelperSurface : mCurrentSurface;
+#endif // Only used within MOZ_X11 or XP_WIN blocks. Unused variable otherwise
     bool needWindowUpdate = aForceSetWindow;
 #ifdef MOZ_X11
     Visual* visual = nullptr;
     Colormap colormap = 0;
     if (curSurface && curSurface->GetType() == gfxSurfaceType::Xlib) {
         static_cast<gfxXlibSurface*>(curSurface.get())->
             GetColormapAndVisual(&colormap, &visual);
         if (visual != mWsInfo.visual || colormap != mWsInfo.colormap) {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -7483,18 +7483,16 @@ UpgradeDirectoryMetadataFrom1To2Helper::
   return NS_OK;
 }
 
 nsresult
 UpgradeDirectoryMetadataFrom1To2Helper::DoProcessOriginDirectories()
 {
   AssertIsOnIOThread();
 
-  nsCOMPtr<nsIFile> permanentStorageDir;
-
   for (uint32_t count = mOriginProps.Length(), index = 0;
        index < count;
        index++) {
     OriginProps& originProps = mOriginProps[index];
 
     nsresult rv;
 
     if (originProps.mNeedsRestore) {
--- a/dom/storage/DOMStorageDBThread.cpp
+++ b/dom/storage/DOMStorageDBThread.cpp
@@ -452,17 +452,16 @@ DOMStorageDBThread::OpenDatabaseConnecti
   nsresult rv;
 
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<mozIStorageService> service
       = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<mozIStorageConnection> connection;
   rv = service->OpenUnsharedDatabase(mDatabaseFile, getter_AddRefs(mWorkerConnection));
   if (rv == NS_ERROR_FILE_CORRUPTED) {
     // delete the db and try opening again
     rv = mDatabaseFile->Remove(false);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = service->OpenUnsharedDatabase(mDatabaseFile, getter_AddRefs(mWorkerConnection));
   }
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/webidl/Permissions.webidl
+++ b/dom/webidl/Permissions.webidl
@@ -20,11 +20,11 @@ dictionary PermissionDescriptor {
 
 // We don't implement `PushPermissionDescriptor` because we use a background
 // message quota instead of `userVisibleOnly`.
 
 [Exposed=(Window)]
 interface Permissions {
   [Throws]
   Promise<PermissionStatus> query(object permission);
-  [Throws]
+  [Throws, Pref="dom.permissions.revoke.enable"]
   Promise<PermissionStatus> revoke(object permission);
 };
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1045,17 +1045,18 @@ public:
   // (if any).
   static void
   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
               bool aFireAtScope, WorkerPrivate* aTarget,
               const nsString& aMessage, const nsString& aFilename,
               const nsString& aLine, uint32_t aLineNumber,
               uint32_t aColumnNumber, uint32_t aFlags,
               uint32_t aErrorNumber, JSExnType aExnType,
-              bool aMutedError, uint64_t aInnerWindowId)
+              bool aMutedError, uint64_t aInnerWindowId,
+              JS::Handle<JS::Value> aException = JS::NullHandleValue)
   {
     if (aWorkerPrivate) {
       aWorkerPrivate->AssertIsOnWorkerThread();
     } else {
       AssertIsOnMainThread();
     }
 
     // We should not fire error events for warnings but instead make sure that
@@ -1065,16 +1066,17 @@ public:
       RootedDictionary<ErrorEventInit> init(aCx);
 
       if (aMutedError) {
         init.mMessage.AssignLiteral("Script error.");
       } else {
         init.mMessage = aMessage;
         init.mFilename = aFilename;
         init.mLineno = aLineNumber;
+        init.mError = aException;
       }
 
       init.mCancelable = true;
       init.mBubbles = false;
 
       if (aTarget) {
         RefPtr<ErrorEvent> event =
           ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
@@ -5914,16 +5916,22 @@ WorkerPrivate::ReportError(JSContext* aC
   if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
     return;
   }
 
   NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
                mErrorHandlerRecursionCount == 1,
                "Bad recursion logic!");
 
+  JS::Rooted<JS::Value> exn(aCx);
+  if (!JS_GetPendingException(aCx, &exn)) {
+    // Probably shouldn't actually happen?  But let's go ahead and just use null
+    // for lack of anything better.
+    exn.setNull();
+  }
   JS_ClearPendingException(aCx);
 
   nsString message, filename, line;
   uint32_t lineNumber, columnNumber, flags, errorNumber;
   JSExnType exnType = JSEXN_ERR;
   bool mutedError = aReport && aReport->isMuted;
 
   if (aReport) {
@@ -5961,17 +5969,17 @@ WorkerPrivate::ReportError(JSContext* aC
   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
                      !mCloseHandlerStarted &&
                      errorNumber != JSMSG_OUT_OF_MEMORY &&
                      JS::CurrentGlobalOrNull(aCx);
 
   ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
                                    filename, line, lineNumber,
                                    columnNumber, flags, errorNumber, exnType,
-                                   mutedError, 0);
+                                   mutedError, 0, exn);
 
   mErrorHandlerRecursionCount--;
 }
 
 // static
 void
 WorkerPrivate::ReportErrorToConsole(const char* aMessage)
 {
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2608,17 +2608,17 @@ XMLHttpRequestMainThread::InitiateFetch(
     // ref to us to be extra safe.
     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
     mChannel = nullptr;
 
     mErrorLoad = true;
 
     // Per spec, we throw on sync errors, but not async.
     if (mFlagSynchronous) {
-      return rv;
+      return NS_ERROR_DOM_NETWORK_ERR;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
@@ -2869,17 +2869,17 @@ XMLHttpRequestMainThread::SendInternal(c
       DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
                             0, mUploadTotal);
     }
   }
 
   if (!mChannel) {
     // Per spec, silently fail on async request failures; throw for sync.
     if (mFlagSynchronous) {
-      return NS_ERROR_FAILURE;
+      return NS_ERROR_DOM_NETWORK_ERR;
     } else {
       // Defer the actual sending of async events just in case listeners
       // are attached after the send() method is called.
       NS_DispatchToCurrentThread(
         NewRunnableMethod<ProgressEventType>(this,
           &XMLHttpRequestMainThread::CloseRequestWithError,
           ProgressEventType::error));
       return NS_OK;
--- a/dom/xml/nsXMLFragmentContentSink.cpp
+++ b/dom/xml/nsXMLFragmentContentSink.cpp
@@ -162,18 +162,16 @@ nsXMLFragmentContentSink::WillBuildModel
   mRoot = new DocumentFragment(mNodeInfoManager);
   
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsXMLFragmentContentSink::DidBuildModel(bool aTerminated)
 {
-  RefPtr<nsParserBase> kungFuDeathGrip(mParser);
-
   // Drop our reference to the parser to get rid of a circular
   // reference.
   mParser = nullptr;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
--- a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp
+++ b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp
@@ -220,17 +220,16 @@ nsXULTemplateQueryProcessorStorage::GetD
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);
         if (NS_FAILED(rv)) { // if it fails, not a file url
             nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI);
             return rv;
         }
 
-        nsCOMPtr<nsIFile> file;
         rv = fileChannel->GetFile(getter_AddRefs(databaseFile));
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // ok now we have an URI of a sqlite file
     nsCOMPtr<mozIStorageConnection> connection;
     rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection));
     if (NS_FAILED(rv)) {
--- a/gfx/2d/Rect.h
+++ b/gfx/2d/Rect.h
@@ -276,21 +276,20 @@ IntRectTyped<units> TruncatedToInt(const
 }
 
 template<class units>
 RectTyped<units> IntRectToRect(const IntRectTyped<units>& aRect)
 {
   return RectTyped<units>(aRect.x, aRect.y, aRect.width, aRect.height);
 }
 
-// Convenience function for intersecting two IntRects wrapped in Maybes.
-template <typename Units>
-Maybe<IntRectTyped<Units>>
-IntersectMaybeRects(const Maybe<IntRectTyped<Units>>& a,
-                    const Maybe<IntRectTyped<Units>>& b)
+// Convenience function for intersecting two rectangles wrapped in Maybes.
+template <typename T>
+Maybe<T>
+IntersectMaybeRects(const Maybe<T>& a, const Maybe<T>& b)
 {
   if (!a) {
     return b;
   } else if (!b) {
     return a;
   } else {
     return Some(a->Intersect(*b));
   }
--- a/gfx/ipc/CompositorSession.h
+++ b/gfx/ipc/CompositorSession.h
@@ -46,17 +46,17 @@ public:
 
   // 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 Async Pan/Zoom Tree Manager for this compositor.
-  virtual already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const = 0;
+  virtual RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const = 0;
 
   // Return the child end of the compositor IPC bridge.
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   // Return the proxy for accessing the compositor's widget.
   CompositorWidgetDelegate* GetCompositorWidgetDelegate() {
     return mCompositorWidgetDelegate;
   }
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -6,16 +6,17 @@
 #include "GPUParent.h"
 #include "gfxConfig.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "nsDebugImpl.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "ProcessUtils.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
@@ -58,16 +59,17 @@ GPUParent::Init(base::ProcessId aParentP
 #if defined(XP_WIN)
   DeviceManagerDx::Init();
   DeviceManagerD3D9::Init();
 #endif
   if (NS_FAILED(NS_InitMinimalXPCOM())) {
     return false;
   }
   CompositorThreadHolder::Start();
+  APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
   VRManager::ManagerInit();
   LayerTreeOwnerTracker::Initialize();
   mozilla::ipc::SetThisProcessName("GPU Process");
   return true;
 }
 
 bool
 GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs,
@@ -165,18 +167,22 @@ bool
 GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut)
 {
   CopyFeatureChange(Feature::D3D11_COMPOSITING, &aOut->d3d11Compositing());
   CopyFeatureChange(Feature::D3D9_COMPOSITING, &aOut->d3d9Compositing());
   CopyFeatureChange(Feature::OPENGL_COMPOSITING, &aOut->oglCompositing());
 
 #if defined(XP_WIN)
   if (DeviceManagerDx* dm = DeviceManagerDx::Get()) {
-    dm->ExportDeviceInfo(&aOut->d3d11Device());
+    D3D11DeviceStatus deviceStatus;
+    dm->ExportDeviceInfo(&deviceStatus);
+    aOut->gpuDevice() = deviceStatus;
   }
+#else
+  aOut->gpuDevice() = null_t();
 #endif
 
   return true;
 }
 
 static void
 OpenParent(RefPtr<CompositorBridgeParent> aParent,
            Endpoint<PCompositorBridgeParent>&& aEndpoint)
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "GPUProcessManager.h"
 #include "GPUProcessHost.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/InProcessCompositorSession.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/RemoteCompositorSession.h"
 #include "mozilla/widget/PlatformWidgetTypes.h"
 #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
@@ -389,18 +390,27 @@ GPUProcessManager::CreateRemoteSession(n
   CompositorWidgetChild* widget = new CompositorWidgetChild(dispatcher, observer);
   if (!child->SendPCompositorWidgetConstructor(widget, initData)) {
     return nullptr;
   }
   if (!child->SendInitialize(aRootLayerTreeId)) {
     return nullptr;
   }
 
+  RefPtr<APZCTreeManagerChild> apz = nullptr;
+  if (aUseAPZ) {
+    PAPZCTreeManagerChild* papz = child->SendPAPZCTreeManagerConstructor(0);
+    if (!papz) {
+      return nullptr;
+    }
+    apz = static_cast<APZCTreeManagerChild*>(papz);
+  }
+
   RefPtr<RemoteCompositorSession> session =
-    new RemoteCompositorSession(child, widget, aRootLayerTreeId);
+    new RemoteCompositorSession(child, widget, apz, aRootLayerTreeId);
   return session.forget();
 #else
   gfxCriticalNote << "Platform does not support out-of-process compositing";
   return nullptr;
 #endif
 }
 
 bool
--- a/gfx/ipc/GraphicsMessages.ipdlh
+++ b/gfx/ipc/GraphicsMessages.ipdlh
@@ -49,22 +49,28 @@ struct FeatureFailure
 // If a feature state has changed from Enabled -> Failure, this will be non-
 // null.
 union FeatureChange
 {
   null_t;
   FeatureFailure;
 };
 
+union GPUDeviceStatus
+{
+  null_t;
+  D3D11DeviceStatus;
+};
+
 struct GPUDeviceData
 {
   FeatureChange d3d11Compositing;
   FeatureChange d3d9Compositing;
   FeatureChange oglCompositing;
-  D3D11DeviceStatus d3d11Device;
+  GPUDeviceStatus gpuDevice;
 };
 
 union GfxVarValue
 {
   BackendType;
   bool;
   gfxImageFormat;
   IntSize;
--- a/gfx/ipc/InProcessCompositorSession.cpp
+++ b/gfx/ipc/InProcessCompositorSession.cpp
@@ -49,17 +49,17 @@ InProcessCompositorSession::GetInProcess
 }
 
 void
 InProcessCompositorSession::SetContentController(GeckoContentController* aController)
 {
   mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, aController);
 }
 
-already_AddRefed<IAPZCTreeManager>
+RefPtr<IAPZCTreeManager>
 InProcessCompositorSession::GetAPZCTreeManager() const
 {
   return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId);
 }
 
 void
 InProcessCompositorSession::Shutdown()
 {
--- a/gfx/ipc/InProcessCompositorSession.h
+++ b/gfx/ipc/InProcessCompositorSession.h
@@ -24,17 +24,17 @@ public:
     const uint64_t& aRootLayerTreeId,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
-  already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const override;
+  RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
   void Shutdown() override;
 
 private:
   InProcessCompositorSession(widget::CompositorWidget* aWidget,
                              CompositorBridgeChild* aChild,
                              CompositorBridgeParent* aParent);
 
 private:
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -1,44 +1,50 @@
 /* -*- 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 "RemoteCompositorSession.h"
 
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 using namespace widget;
 
 RemoteCompositorSession::RemoteCompositorSession(CompositorBridgeChild* aChild,
                                                  CompositorWidgetDelegate* aWidgetDelegate,
+                                                 APZCTreeManagerChild* aAPZ,
                                                  const uint64_t& aRootLayerTreeId)
  : CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId)
+ , mAPZ(aAPZ)
 {
 }
 
 CompositorBridgeParent*
 RemoteCompositorSession::GetInProcessBridge() const
 {
   return nullptr;
 }
 
 void
 RemoteCompositorSession::SetContentController(GeckoContentController* aController)
 {
-  MOZ_CRASH("NYI");
+  mCompositorBridgeChild->SendPAPZConstructor(new APZChild(aController), 0);
 }
 
-already_AddRefed<IAPZCTreeManager>
+RefPtr<IAPZCTreeManager>
 RemoteCompositorSession::GetAPZCTreeManager() const
 {
-  return nullptr;
+  return mAPZ;
 }
 
 void
 RemoteCompositorSession::Shutdown()
 {
   mCompositorBridgeChild->Destroy();
   mCompositorBridgeChild = nullptr;
   mCompositorWidgetDelegate = nullptr;
--- a/gfx/ipc/RemoteCompositorSession.h
+++ b/gfx/ipc/RemoteCompositorSession.h
@@ -13,21 +13,24 @@
 namespace mozilla {
 namespace layers {
 
 class RemoteCompositorSession final : public CompositorSession
 {
 public:
   RemoteCompositorSession(CompositorBridgeChild* aChild,
                           CompositorWidgetDelegate* aWidgetDelegate,
+                          APZCTreeManagerChild* aAPZ,
                           const uint64_t& aRootLayerTreeId);
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
-  already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const override;
+  RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
   void Shutdown() override;
 
+private:
+  RefPtr<APZCTreeManagerChild> mAPZ;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -22,17 +22,21 @@ namespace layers {
 class GeckoContentController
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GeckoContentController)
 
   /**
    * Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
    * Implementations per-platform are responsible for actually handling this.
-   * This method will always be called on the Gecko main thread.
+   *
+   * This method must always be called on the repaint thread, which depends
+   * on the GeckoContentController. For ChromeProcessController it is the
+   * Gecko main thread, while for RemoteContentController it is the compositor
+   * thread where it can send IPDL messages.
    */
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
 
   /**
    * Different types of tap-related events that can be sent in
    * the HandleTap function. The names should be relatively self-explanatory.
    * Note that the eLongTapUp will always be preceded by an eLongTap, but not
    * all eLongTap notifications will be followed by an eLongTapUp (for instance,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -166,16 +166,20 @@ ChromeProcessController::HandleTap(TapTy
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
                                         ScrollableLayerGuid, uint64_t>(this,
                         &ChromeProcessController::HandleTap,
                         aType, aPoint, aModifiers, aGuid, aInputBlockId));
     return;
   }
 
+  if (!mAPZEventState) {
+    return;
+  }
+
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   if (!presShell) {
     return;
   }
   if (!presShell->GetPresContext()) {
     return;
   }
   CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
@@ -212,16 +216,20 @@ ChromeProcessController::NotifyAPZStateC
     mUILoop->PostTask(NewRunnableMethod
                       <ScrollableLayerGuid,
                        APZStateChange,
                        int>(this, &ChromeProcessController::NotifyAPZStateChange,
                             aGuid, aChange, aArg));
     return;
   }
 
+  if (!mAPZEventState) {
+    return;
+  }
+
   mAPZEventState->ProcessAPZStateChange(GetRootDocument(), aGuid.mScrollId, aChange, aArg);
 }
 
 void
 ChromeProcessController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
 {
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(NewRunnableMethod
--- a/gfx/layers/apz/util/ChromeProcessController.h
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -19,18 +19,24 @@ class MessageLoop;
 
 namespace mozilla {
 
 namespace layers {
 
 class IAPZCTreeManager;
 class APZEventState;
 
-// A ChromeProcessController is attached to the root of a compositor's layer
-// tree.
+/**
+ * ChromeProcessController is a GeckoContentController attached to the root of
+ * a compositor's layer tree. It's used directly by APZ by default, and remoted
+ * using PAPZ if there is a gpu process.
+ *
+ * If ChromeProcessController needs to implement a new method on GeckoContentController
+ * PAPZ, APZChild, and RemoteContentController must be updated to handle it.
+ */
 class ChromeProcessController : public mozilla::layers::GeckoContentController
 {
 protected:
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
 
 public:
   explicit ChromeProcessController(nsIWidget* aWidget, APZEventState* aAPZEventState, IAPZCTreeManager* aAPZCTreeManager);
--- a/gfx/layers/apz/util/ContentProcessController.h
+++ b/gfx/layers/apz/util/ContentProcessController.h
@@ -16,16 +16,27 @@ namespace mozilla {
 namespace dom {
 class TabChild;
 } // namespace dom
 
 namespace layers {
 
 class APZChild;
 
+/**
+ * ContentProcessController is a GeckoContentController for a TabChild, and is always
+ * remoted using PAPZ/APZChild.
+ *
+ * ContentProcessController is created in ContentChild when a layer tree id has
+ * been allocated for a PBrowser that lives in that content process, and is destroyed
+ * when the Destroy message is received, or when the tab dies.
+ *
+ * If ContentProcessController needs to implement a new method on GeckoContentController
+ * PAPZ, APZChild, and RemoteContentController must be updated to handle it.
+ */
 class ContentProcessController final
       : public GeckoContentController
 {
 public:
   ~ContentProcessController();
 
   static APZChild* Create(const dom::TabId& aTabId);
 
--- a/gfx/layers/ipc/APZChild.h
+++ b/gfx/layers/ipc/APZChild.h
@@ -10,16 +10,20 @@
 #include "mozilla/layers/PAPZChild.h"
 
 namespace mozilla {
 
 namespace layers {
 
 class GeckoContentController;
 
+/**
+ * APZChild implements PAPZChild and is used to remote a GeckoContentController
+ * that lives in a different process than where APZ lives.
+ */
 class APZChild final : public PAPZChild
 {
 public:
   explicit APZChild(RefPtr<GeckoContentController> aController);
   ~APZChild();
 
   bool RecvRequestContentRepaint(const FrameMetrics& frame) override;
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1427,41 +1427,74 @@ CompositorBridgeParent::ForceComposeToTa
   AutoRestore<bool> override(mOverrideComposeReadiness);
   mOverrideComposeReadiness = true;
   mCompositorScheduler->ForceComposeToTarget(aTarget, aRect);
 }
 
 PAPZCTreeManagerParent*
 CompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId)
 {
-  return nullptr;
+  // The main process should pass in 0 because we assume mRootLayerTreeID
+  MOZ_ASSERT(aLayersId == 0);
+
+  // This message doubles as initialization
+  MOZ_ASSERT(!mApzcTreeManager);
+  mApzcTreeManager = new APZCTreeManager();
+
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
+  MOZ_ASSERT(state.mParent);
+  MOZ_ASSERT(!state.mApzcTreeManagerParent);
+  state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, state.mParent->GetAPZCTreeManager());
+
+  return state.mApzcTreeManagerParent;
 }
 
 bool
 CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
 {
-  return false;
+  delete aActor;
+  return true;
 }
 
 PAPZParent*
 CompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId)
 {
-  return nullptr;
+  // The main process should pass in 0 because we assume mRootLayerTreeID
+  MOZ_ASSERT(aLayersId == 0);
+
+  RemoteContentController* controller = new RemoteContentController();
+
+  // Increment the controller's refcount before we return it. This will keep the
+  // controller alive until it is released by IPDL in DeallocPAPZParent.
+  controller->AddRef();
+
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
+  MOZ_ASSERT(!state.mController);
+  state.mController = controller;
+
+  return controller;
 }
 
 bool
 CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
 {
-  return false;
+  RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
+  controller->Release();
+  return true;
 }
 
 bool
 CompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
 {
-  return false;
+  // The main process should pass in 0 because we assume mRootLayerTreeID
+  MOZ_ASSERT(aLayersId == 0);
+  *aHasAPZ = AsyncPanZoomEnabled();
+  return true;
 }
 
 RefPtr<APZCTreeManager>
 CompositorBridgeParent::GetAPZCTreeManager()
 {
   return mApzcTreeManager;
 }
 
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -24,35 +24,42 @@ using mozilla::layers::AsyncDragMetrics 
 using mozilla::Modifiers from "mozilla/EventForwards.h";
 using class nsRegion from "nsRegion.h";
 
 namespace mozilla {
 namespace layers {
 
 
 /**
- * If APZ is enabled then one PAPZ will be opened per PBrowser between the
- * process where the PBrowser child actor lives and the main process (the
- * PBrowser parent actor doesn't necessarily live in the main process, for
- * example with nested browsers). This will typically be set up when the layers
- * id is allocated for the PBrowser.
+ * PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the
+ * PCompositorBridge protocol which either connects to the compositor thread
+ * in the main process, or to the compositor thread in the gpu processs.
  *
- * Opened through PContent and runs on the main thread in both parent and child.
+ * PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted
+ * GeckoContentController lives (generally the main thread of the main or content process).
+ * RemoteContentController implements PAPZParent, while APZChild implements PAPZChild.
+ *
+ * PAPZ is always used for ContentProcessController and only used for ChromeProcessController
+ * when there is a gpu process, otherwhise ChromeProcessController is used directly on the
+ * compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController
+ * are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController
+ * must be updated to handle it.
  */
 sync protocol PAPZ
 {
   manager PCompositorBridge;
 
 parent:
 
   async UpdateHitRegion(nsRegion aRegion);
 
   async __delete__();
 
 child:
+
   async RequestContentRepaint(FrameMetrics frame);
 
   // The aCallTakeFocusForClickFromTap argument is used for eSingleTap types,
   // to request that the child take focus before dispatching the mouse events
   // for the tap (otherwise the resulting focus behaviour is incorrect).
   async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
                   ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
                   bool aCallTakeFocusForClickFromTap);
--- a/gfx/layers/ipc/PAPZCTreeManager.ipdl
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -29,16 +29,25 @@ using class mozilla::MouseInput from "In
 using class mozilla::PanGestureInput from "InputData.h";
 using class mozilla::PinchGestureInput from "InputData.h";
 using class mozilla::TapGestureInput from "InputData.h";
 using class mozilla::ScrollWheelInput from "InputData.h";
 
 namespace mozilla {
 namespace layers {
 
+/**
+ * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
+ * lives on the PCompositorBridge protocol which either connects to the compositor
+ * thread in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
+ * lives in the main thread of the main or the content process. APZCTreeManagerParent
+ * and APZCTreeManagerChild implement this protocol.
+ */
 sync protocol PAPZCTreeManager
 {
 manager PCompositorBridge;
 
 parent:
 
   // These messages correspond to the methods
   // on the IAPZCTreeManager interface
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -15,21 +15,24 @@ namespace mozilla {
 
 namespace dom {
 class TabParent;
 }
 
 namespace layers {
 
 /**
- * RemoteContentController uses the PAPZ protocol to implement a
- * GeckoContentController for a browser living in a remote process.
- * Most of the member functions can be called on any thread, exceptions are
- * annotated in comments. The PAPZ protocol runs on the main thread (so all the
- * Recv* member functions do too).
+ * RemoteContentController implements PAPZChild and is used to access a
+ * GeckoContentController that lives in a different process.
+ *
+ * RemoteContentController lives on the compositor thread. All methods can
+ * be called off the compositor thread and will get dispatched to the right
+ * thread, with the exception of RequestContentRepaint and NotifyFlushComplete,
+ * which must be called on the repaint thread, which in this case is the compositor
+ * thread.
  */
 class RemoteContentController : public GeckoContentController
                               , public PAPZParent
 {
   using GeckoContentController::TapType;
   using GeckoContentController::APZStateChange;
 
 public:
--- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
@@ -214,16 +214,19 @@ public:
         if (!fPattern && isLCD(*rec)) {
             rec->fMaskFormat = SkMask::kA8_Format;
         }
 
         // rotated text looks bad with hinting, so we disable it as needed
         if (!gFontHintingEnabled || !isAxisAligned(*rec)) {
             rec->setHinting(SkPaint::kNo_Hinting);
         }
+
+        // Don't apply any gamma so that we match cairo-ft's results.
+        rec->ignorePreBlend();
     }
 
     virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const override
     {
         SkDEBUGCODE(SkDebugf("SkCairoFTTypeface::onGetFontDescriptor unimplemented\n"));
     }
 
     virtual int onCharsToGlyphs(void const*, SkTypeface::Encoding, uint16_t*, int) const override
@@ -755,17 +758,17 @@ void SkScalerContext_CairoFT::generateIm
 }
 
 void SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph, SkPath* path)
 {
     SkASSERT(fScaledFont != nullptr);
     CairoLockedFTFace faceLock(fScaledFont);
     FT_Face face = faceLock.getFace();
 
-    SkASSERT(&glyph && path);
+    SkASSERT(path);
 
     uint32_t flags = fLoadGlyphFlags;
     flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
     flags &= ~FT_LOAD_RENDER;   // don't scan convert (we just want the outline)
 
     FT_Error err = FT_Load_Glyph(face, glyph.getGlyphID(), flags);
 
     if (err != 0) {
@@ -775,17 +778,19 @@ void SkScalerContext_CairoFT::generatePa
 
     prepareGlyph(face->glyph);
 
     generateGlyphPath(face, path);
 }
 
 void SkScalerContext_CairoFT::generateFontMetrics(SkPaint::FontMetrics* metrics)
 {
-    SkDEBUGCODE(SkDebugf("SkScalerContext_CairoFT::generateFontMetrics unimplemented\n"));
+    if (metrics) {
+        memset(metrics, 0, sizeof(SkPaint::FontMetrics));
+    }
 }
 
 SkUnichar SkScalerContext_CairoFT::generateGlyphToChar(uint16_t glyph)
 {
     SkASSERT(fScaledFont != nullptr);
     CairoLockedFTFace faceLock(fScaledFont);
     FT_Face face = faceLock.getFace();
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1999,17 +1999,17 @@ gfxWindowsPlatform::ImportGPUDeviceData(
 
   gfxPlatform::ImportGPUDeviceData(aData);
 
   gfxConfig::ImportChange(Feature::D3D11_COMPOSITING, aData.d3d11Compositing());
   gfxConfig::ImportChange(Feature::D3D9_COMPOSITING, aData.d3d9Compositing());
 
   DeviceManagerDx* dm = DeviceManagerDx::Get();
   if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
-    dm->ImportDeviceInfo(aData.d3d11Device());
+    dm->ImportDeviceInfo(aData.gpuDevice().get_D3D11DeviceStatus());
   } else {
     // There should be no devices, so this just takes away the device status.
     dm->ResetDevices();
 
     // Make sure we disable D2D if content processes might use it.
     FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
     if (d2d1.IsEnabled()) {
       d2d1.SetFailed(
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -18,16 +18,20 @@
 #include "SharedMemoryBasic.h"
 #endif
 
 #include "MainThreadUtils.h"
 #include "mozilla/Sprintf.h"
 #include "prenv.h"
 #include "nsXPCOMPrivate.h"
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsAppDirectoryServiceDefs.h"
+#endif
+
 #include "nsExceptionHandler.h"
 
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsPrintfCString.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
@@ -603,16 +607,30 @@ AddAppDirToCommandLine(std::vector<std::
         aCmdLine.AppendLooseValue(wpath);
 #else
         nsAutoCString path;
         MOZ_ALWAYS_SUCCEEDS(appDir->GetNativePath(path));
         aCmdLine.push_back("-appdir");
         aCmdLine.push_back(path.get());
 #endif
       }
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+      // Full path to the profile dir
+      nsCOMPtr<nsIFile> profileDir;
+      rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
+                                 NS_GET_IID(nsIFile),
+                                 getter_AddRefs(profileDir));
+      if (NS_SUCCEEDED(rv)) {
+        nsAutoCString path;
+        MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
+        aCmdLine.push_back("-profile");
+        aCmdLine.push_back(path.get());
+      }
+#endif
     }
   }
 }
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 static void
 MaybeAddNsprLogFileAccess(std::vector<std::wstring>& aAllowedFilesReadWrite)
 {
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -459,10 +459,22 @@ UnionTypeReadError(const char* aUnionNam
 }
 
 void ArrayLengthReadError(const char* aElementName)
 {
   nsPrintfCString message("error deserializing length of %s[]", aElementName);
   NS_RUNTIMEABORT(message.get());
 }
 
+void
+TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
+             nsTArray<void*>& aArray)
+{
+  uint32_t i = 0;
+  void** elements = aArray.AppendElements(aTable.Count());
+  for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+    elements[i] = iter.Get()->GetKey();
+    ++i;
+  }
+}
+
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=4 ts=4 et :
  */
 /* 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_ipc_ProtocolUtils_h
 #define mozilla_ipc_ProtocolUtils_h 1
@@ -116,21 +116,23 @@ struct ActorHandle
 // that they can share the same state machine implementation.  To
 // further normalize, |Send| is used for 'call', |Recv| for 'answer'.
 struct Trigger
 {
     enum Action { Send, Recv };
 
     Trigger(Action action, int32_t msg) :
         mAction(action),
-        mMsg(msg)
-    {}
+        mMessage(msg)
+    {
+      MOZ_ASSERT(0 <= msg && msg < INT32_MAX);
+    }
 
-    Action mAction;
-    int32_t mMsg;
+    uint32_t mAction : 1;
+    uint32_t mMessage : 31;
 };
 
 class ProtocolCloneContext
 {
   typedef mozilla::dom::ContentParent ContentParent;
   typedef mozilla::net::NeckoParent NeckoParent;
 
   RefPtr<ContentParent> mContentParent;
@@ -599,20 +601,41 @@ CreateEndpoints(const PrivateIPDLInterfa
                                           parentTransport, aParentDestPid, aChildDestPid, aProtocol);
 
   *aChildEndpoint = Endpoint<PFooChild>(aPrivate, mozilla::ipc::Transport::MODE_CLIENT,
                                         childTransport, aChildDestPid, aParentDestPid, aChildProtocol);
 
   return NS_OK;
 }
 
+void
+TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
+             nsTArray<void*>& aArray);
+
 } // namespace ipc
 
 template<typename Protocol>
-using ManagedContainer = nsTHashtable<nsPtrHashKey<Protocol>>;
+class ManagedContainer : public nsTHashtable<nsPtrHashKey<Protocol>>
+{
+  typedef nsTHashtable<nsPtrHashKey<Protocol>> BaseClass;
+
+public:
+  // Having the core logic work on void pointers, rather than typed pointers,
+  // means that we can have one instance of this code out-of-line, rather
+  // than several hundred instances of this code out-of-lined.  (Those
+  // repeated instances don't necessarily get folded together by the linker
+  // because they contain member offsets and such that differ between the
+  // functions.)  We do have to pay for it with some eye-bleedingly bad casts,
+  // though.
+  void ToArray(nsTArray<Protocol*>& aArray) const {
+    ::mozilla::ipc::TableToArray(*reinterpret_cast<const nsTHashtable<nsPtrHashKey<void>>*>
+                                 (static_cast<const BaseClass*>(this)),
+                                 reinterpret_cast<nsTArray<void*>&>(aArray));
+  }
+};
 
 template<typename Protocol>
 Protocol*
 LoneManagedOrNullAsserts(const ManagedContainer<Protocol>& aManagees)
 {
     if (aManagees.IsEmpty()) {
         return nullptr;
     }
--- a/ipc/ipdl/ipdl/builtin.py
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -45,14 +45,15 @@ HeaderIncludes = (
     'mozilla/Attributes.h',
     'IPCMessageStart.h',
     'ipc/IPCMessageUtils.h',
     'mozilla/RefPtr.h',
     'nsStringGlue.h',
     'nsTArray.h',
     'mozilla/ipc/ProtocolUtils.h',
     'nsTHashtable.h',
+    'mozilla/OperatorNewExtensions.h',
 )
 
 CppIncludes = (
     'nsIFile.h',
     'GeckoProfiler.h',
 )
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -846,17 +846,17 @@ IPDL union type."""
 
         if self.recursive:
             return ExprAssn(self.callGetPtr(),
                             ExprNew(self.bareType(self.side),
                                     args=args))
         else:
             return ExprNew(self.bareType(self.side),
                            args=args,
-                           newargs=[ self.callGetPtr() ])
+                           newargs=[ ExprVar('mozilla::KnownNotNull'), self.callGetPtr() ])
 
     def callDtor(self):
         if self.recursive:
             return ExprDelete(self.callGetPtr())
         else:
             return ExprCall(
                 ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef()))
 
@@ -1792,27 +1792,27 @@ class _GenerateProtocolCode(ipdl.ast.Vis
                                   actionexpr)
 
         def stateEnum(s):
             if s is ipdl.ast.State.DEAD:
                 return _deadState()
             else:
                 return ExprVar(s.decl.cxxname)
 
-        # bool Transition(State from, Trigger trigger, State* next)
+        # bool Transition(Trigger trigger, State* next)
+        # The state we are transitioning from is stored in *next.
         fromvar = ExprVar('from')
         triggervar = ExprVar('trigger')
         nextvar = ExprVar('next')
-        msgexpr = ExprSelect(triggervar, '.', 'mMsg')
+        msgexpr = ExprSelect(triggervar, '.', 'mMessage')
         actionexpr = ExprSelect(triggervar, '.', 'mAction')
 
         transitionfunc = FunctionDefn(FunctionDecl(
             'Transition',
-            params=[ Decl(Type('State'), fromvar.name),
-                     Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
+            params=[ Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
                      Decl(Type('State', ptr=1), nextvar.name) ],
             ret=Type.BOOL))
 
         fromswitch = StmtSwitch(fromvar)
 
         for ts in self.protocol.transitionStmts:
             msgswitch = StmtSwitch(msgexpr)
 
@@ -1897,16 +1897,18 @@ class _GenerateProtocolCode(ipdl.ast.Vis
                          init=ExprVar('mozilla::ipc::Trigger::Send')))
         if userecv:
             transitionfunc.addstmt(
                 StmtDecl(Decl(Type('int32_t', const=1), recvvar.name),
                          init=ExprVar('mozilla::ipc::Trigger::Recv')))
         if usesend or userecv:
             transitionfunc.addstmt(Whitespace.NL)
 
+        transitionfunc.addstmt(StmtDecl(Decl(Type('State'), fromvar.name),
+                                        init=ExprDeref(nextvar)))
         transitionfunc.addstmt(fromswitch)
         # all --> Error transitions break to here.  But only insert this
         # block if there is any possibility of such transitions.
         if self.protocol.transitionStmts:
             transitionfunc.addstmts([
                 StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
                 StmtReturn(ExprLiteral.FALSE),
             ])
@@ -3081,32 +3083,20 @@ class _GenerateProtocolActorCode(ipdl.as
         ## const Array<T>& Managed() const
         for managed in ptype.manages:
             arrvar = ExprVar('aArr')
             meth = MethodDefn(MethodDecl(
                 p.managedMethod(managed, self.side).name,
                 params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1),
                               arrvar.name) ],
                 const=1))
-            ivar = ExprVar('i')
-            elementsvar = ExprVar('elements')
-            itervar = ExprVar('iter')
-            meth.addstmt(StmtDecl(Decl(Type.UINT32, ivar.name),
-                                  init=ExprLiteral.ZERO))
-            meth.addstmt(StmtDecl(Decl(Type(_actorName(managed.name(), self.side), ptrptr=1), elementsvar.name),
-                                  init=ExprCall(ExprSelect(arrvar, '.', 'AppendElements'),
-                                                args=[ ExprCall(ExprSelect(p.managedVar(managed, self.side),
-                                                                           '.', 'Count')) ])))
-            foreachaccumulate = forLoopOverHashtable(p.managedVar(managed, self.side),
-                                                     itervar, const=True)
-            foreachaccumulate.addstmt(StmtExpr(
-                ExprAssn(ExprIndex(elementsvar, ivar),
-                         actorFromIter(itervar))))
-            foreachaccumulate.addstmt(StmtExpr(ExprPrefixUnop(ivar, '++')))
-            meth.addstmt(foreachaccumulate)
+            meth.addstmt(StmtExpr(
+                ExprCall(ExprSelect(p.managedVar(managed, self.side),
+                                    '.', 'ToArray'),
+                         args=[ arrvar ])))
 
             refmeth = MethodDefn(MethodDecl(
                 p.managedMethod(managed, self.side).name,
                 params=[ ],
                 ret=p.managedVarType(managed, self.side, const=1, ref=1),
                 const=1))
             refmeth.addstmt(StmtReturn(p.managedVar(managed, self.side)))
 
@@ -5436,18 +5426,17 @@ class _GenerateProtocolActorCode(ipdl.as
             or self.side is 'child' and direction is 'out'):
             action = ExprVar('Trigger::Recv')
         else: assert 0 and 'unknown combo %s/%s'% (self.side, direction)
 
         msgid = md.pqMsgId() if not reply else md.pqReplyId()
         ifbad = StmtIf(ExprNot(
             ExprCall(
                 ExprVar(self.protocol.name +'::Transition'),
-                args=[ stateexpr,
-                       ExprCall(ExprVar('Trigger'),
+                args=[ ExprCall(ExprVar('Trigger'),
                                 args=[ action, ExprVar(msgid) ]),
                        ExprAddrOf(stateexpr) ])))
         ifbad.addifstmts(_badTransition())
         return [ ifbad ]
 
     def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype, sentinelKey, sentinel=True):
         ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
         if isinstance(paramtype, list):
--- a/ipc/mscom/COMPtrHolder.h
+++ b/ipc/mscom/COMPtrHolder.h
@@ -127,17 +127,17 @@ struct ParamTraits<mozilla::mscom::COMPt
     mozilla::mscom::ProxyStream proxyStream(buf.get(), length);
     if (!proxyStream.IsValid()) {
       return false;
     }
     Interface* rawInterface = nullptr;
     if (!proxyStream.GetInterface(_IID, (void**)&rawInterface)) {
       return false;
     }
-    paramType::COMPtrType ptr(rawInterface);
+    typename paramType::COMPtrType ptr(rawInterface);
     aResult->Set(mozilla::Move(ptr));
     return true;
   }
 };
 
 } // namespace IPC
 
 #endif // mozilla_mscom_COMPtrHolder_h
--- a/ipc/mscom/EnsureMTA.h
+++ b/ipc/mscom/EnsureMTA.h
@@ -7,17 +7,17 @@
 #ifndef mozilla_mscom_EnsureMTA_h
 #define mozilla_mscom_EnsureMTA_h
 
 #include "MainThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Function.h"
 #include "mozilla/mscom/COMApartmentRegion.h"
-#include "mozilla/mscom/utils.h"
+#include "mozilla/mscom/Utils.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 
 #include <windows.h>
 
 namespace mozilla {
 namespace mscom {
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -6,17 +6,17 @@
 
 #define INITGUID
 #include "mozilla/mscom/Interceptor.h"
 #include "mozilla/mscom/InterceptorLog.h"
 
 #include "mozilla/mscom/DispatchForwarder.h"
 #include "mozilla/mscom/MainThreadInvoker.h"
 #include "mozilla/mscom/Registration.h"
-#include "mozilla/mscom/utils.h"
+#include "mozilla/mscom/Utils.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
--- a/ipc/mscom/MainThreadHandoff.cpp
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/mscom/MainThreadHandoff.h"
 
 #include "mozilla/mscom/InterceptorLog.h"
 #include "mozilla/mscom/Registration.h"
-#include "mozilla/mscom/utils.h"
+#include "mozilla/mscom/Utils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsThreadUtils.h"
 
 using mozilla::DebugOnly;
 
 namespace {
 
--- a/ipc/mscom/ProxyStream.cpp
+++ b/ipc/mscom/ProxyStream.cpp
@@ -2,17 +2,17 @@
 /* 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 "DynamicallyLinkedFunctionPtr.h"
 #include "mozilla/mscom/EnsureMTA.h"
 #include "mozilla/mscom/ProxyStream.h"
-#include "mozilla/mscom/utils.h"
+#include "mozilla/mscom/Utils.h"
 
 #include "mozilla/Move.h"
 
 #include <windows.h>
 #include <objbase.h>
 #include <shlwapi.h>
 
 namespace mozilla {
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -776,17 +776,17 @@ ParseVarOrConstStatement(AsmJSParser& pa
     TokenKind tk;
     if (!PeekToken(parser, &tk))
         return false;
     if (tk != TOK_VAR && tk != TOK_CONST) {
         *var = nullptr;
         return true;
     }
 
-    *var = parser.statement(YieldIsName);
+    *var = parser.statementListItem(YieldIsName);
     if (!*var)
         return false;
 
     MOZ_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST));
     return true;
 }
 
 /*****************************************************************************/
@@ -7201,17 +7201,17 @@ CheckModuleReturn(ModuleValidator& m)
     TokenStream& ts = m.parser().tokenStream;
     if (tk != TOK_RETURN) {
         return m.failCurrentOffset((tk == TOK_RC || tk == TOK_EOF)
                                    ? "expecting return statement"
                                    : "invalid asm.js. statement");
     }
     ts.ungetToken();
 
-    ParseNode* returnStmt = m.parser().statement(YieldIsName);
+    ParseNode* returnStmt = m.parser().statementListItem(YieldIsName);
     if (!returnStmt)
         return false;
 
     ParseNode* returnExpr = ReturnExpr(returnStmt);
     if (!returnExpr)
         return m.fail(returnStmt, "export statement must return something");
 
     if (returnExpr->isKind(PNK_OBJECT)) {
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -693,46 +693,59 @@ class FunctionCompiler
     void assign(unsigned slot, MDefinition* def)
     {
         if (inDeadCode())
             return;
         curBlock_->setSlot(info().localSlot(slot), def);
     }
 
   private:
+    // False means we're sure to be out-of-bounds after this bounds check.
+    bool maybeAddBoundsCheck(MDefinition* base, const MWasmMemoryAccess& access)
+    {
+        if (access.offset() > uint32_t(INT32_MAX)) {
+            curBlock_->end(MWasmTrap::New(alloc(), Trap::OutOfBounds));
+            curBlock_ = nullptr;
+            return false;
+        }
+        if (!mg().usesSignal.forOOB)
+            curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
+        return true;
+    }
+
     MDefinition* loadHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access,
                                  bool isInt64 = false)
     {
         if (inDeadCode())
             return nullptr;
 
         MInstruction* load = nullptr;
         if (mg().isAsmJS()) {
             load = MAsmJSLoadHeap::New(alloc(), base, access);
         } else {
-            if (!mg().usesSignal.forOOB)
-                curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
+            if (!maybeAddBoundsCheck(base, access))
+                return nullptr;
             load = MWasmLoad::New(alloc(), base, access, isInt64);
         }
 
         curBlock_->add(load);
         return load;
     }
 
     void storeHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
     {
         if (inDeadCode())
             return;
 
         MInstruction* store = nullptr;
         if (mg().isAsmJS()) {
             store = MAsmJSStoreHeap::New(alloc(), base, access, v);
         } else {
-            if (!mg().usesSignal.forOOB)
-                curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
+            if (!maybeAddBoundsCheck(base, access))
+                return;
             store = MWasmStore::New(alloc(), base, access, v);
         }
 
         curBlock_->add(store);
     }
 
   public:
     MDefinition* loadHeap(MDefinition* base, const MWasmMemoryAccess& access, bool isInt64)
@@ -1104,17 +1117,17 @@ class FunctionCompiler
         curBlock_ = nullptr;
     }
 
     void unreachableTrap()
     {
         if (inDeadCode())
             return;
 
-        auto* ins = MAsmThrowUnreachable::New(alloc());
+        auto* ins = MWasmTrap::New(alloc(), wasm::Trap::Unreachable);
         curBlock_->end(ins);
         curBlock_ = nullptr;
     }
 
   private:
     static bool hasPushed(MBasicBlock* block)
     {
         uint32_t numPushed = block->stackDepth() - block->info().firstStackSlot();
--- a/js/src/builtin/Sorting.js
+++ b/js/src/builtin/Sorting.js
@@ -96,29 +96,34 @@ function SortByColumn(array, len, aux, c
 function RadixSort(array, len, buffer, nbytes, signed, floating, comparefn) {
 
     // Determined by performance testing.
     if (len < 128) {
         QuickSort(array, len, comparefn);
         return array;
     }
 
-    // Verify that the buffer is non-null
-    assert(buffer !== null, "Attached data buffer should be reified when array length is >= 128.");
-
     let aux = new List();
     for (let i = 0; i < len; i++) {
         aux[i] = 0;
     }
 
     let view = array;
     let signMask = 1 << nbytes * 8 - 1;
 
     // Preprocess
     if (floating) {
+        // This happens if the array object is constructed under JIT
+        if (buffer === null) {
+            buffer = callFunction(std_TypedArray_buffer, array);
+        }
+
+        // Verify that the buffer is non-null
+        assert(buffer !== null, "Attached data buffer should be reified when array length is >= 128.");
+
         view = new Int32Array(buffer);
 
         // Flip sign bit for positive numbers; flip all bits for negative
         // numbers
         for (let i = 0; i < len; i++) {
             if (view[i] & signMask) {
                 view[i] ^= 0xFFFFFFFF;
             } else {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -761,17 +761,17 @@ Parser<ParseHandler>::parse()
     ParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
         return null();
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return null();
 
-    Node pn = statements(YieldIsName);
+    Node pn = statementList(YieldIsName);
     if (!pn)
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (tt != TOK_EOF) {
         report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
@@ -1291,17 +1291,17 @@ Parser<ParseHandler>::propagateFreeNames
 
 template <>
 bool
 Parser<FullParseHandler>::checkStatementsEOF()
 {
     // This is designed to be paired with parsing a statement list at the top
     // level.
     //
-    // The statements() call breaks on TOK_RC, so make sure we've
+    // The statementList() call breaks on TOK_RC, so make sure we've
     // reached EOF here.
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_EOF) {
         report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                "expression", TokenKindToDesc(tt));
         return false;
@@ -1727,17 +1727,17 @@ Parser<FullParseHandler>::evalBody(EvalS
     if (!varScope.init(pc))
         return nullptr;
 
     // All evals have an implicit non-extensible lexical scope.
     ParseContext::Scope lexicalScope(this);
     if (!lexicalScope.init(pc))
         return nullptr;
 
-    ParseNode* body = statements(YieldIsName);
+    ParseNode* body = statementList(YieldIsName);
     if (!body)
         return nullptr;
 
     if (!checkStatementsEOF())
         return nullptr;
 
     body = finishLexicalScope(lexicalScope, body);
     if (!body)
@@ -1800,17 +1800,17 @@ Parser<FullParseHandler>::globalBody(Glo
     ParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
-    ParseNode* body = statements(YieldIsName);
+    ParseNode* body = statementList(YieldIsName);
     if (!body)
         return nullptr;
 
     if (!checkStatementsEOF())
         return nullptr;
 
     if (!FoldConstants(context, &body, this))
         return nullptr;
@@ -1839,17 +1839,17 @@ Parser<FullParseHandler>::moduleBody(Mod
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule();
     if (!mn)
         return null();
 
-    ParseNode* pn = statements(YieldIsKeyword);
+    ParseNode* pn = statementList(YieldIsKeyword);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -2244,17 +2244,17 @@ Parser<ParseHandler>::functionBody(InHan
     MOZ_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
 
 #ifdef DEBUG
     uint32_t startYieldOffset = pc->lastYieldOffset;
 #endif
 
     Node pn;
     if (type == StatementListBody) {
-        pn = statements(yieldHandling);
+        pn = statementList(yieldHandling);
         if (!pn)
             return null();
     } else {
         MOZ_ASSERT(type == ExpressionBody);
 
         Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
         if (!kid)
             return null();
@@ -2709,48 +2709,47 @@ Parser<ParseHandler>::checkFunctionDefin
     if (kind == Statement) {
         TokenPos pos = handler.getPosition(pn);
         RootedPropertyName funName(context, funAtom->asPropertyName());
 
         // In sloppy mode, Annex B.3.2 allows labelled function
         // declarations. Otherwise it is a parse error.
         ParseContext::Statement* declaredInStmt = pc->innermostStatement();
         if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
-            if (pc->sc()->strict()) {
-                reportWithOffset(ParseError, false, pos.begin, JSMSG_FUNCTION_LABEL);
-                return false;
-            }
-
-            // Find the innermost non-label statement.
+            MOZ_ASSERT(!pc->sc()->strict(),
+                       "labeled functions shouldn't be parsed in strict mode");
+
+            // Find the innermost non-label statement.  Report an error if it's
+            // unbraced: functions can't appear in it.  Otherwise the statement
+            // (or its absence) determines the scope the function's bound in.
             while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label)
                 declaredInStmt = declaredInStmt->enclosing();
 
             if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) {
                 reportWithOffset(ParseError, false, pos.begin, JSMSG_SLOPPY_FUNCTION_LABEL);
                 return false;
             }
         }
 
         if (declaredInStmt) {
-            DeclarationKind declKind = DeclarationKind::LexicalFunction;
-            if (!checkLexicalDeclarationDirectlyWithinBlock(*declaredInStmt, declKind, pos))
-                return false;
+            MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
+            MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
 
             if (!pc->sc()->strict()) {
                 // Under sloppy mode, try Annex B.3.3 semantics. If making an
                 // additional 'var' binding of the same name does not throw an
                 // early error, do so. This 'var' binding would be assigned
                 // the function object when its declaration is reached, not at
                 // the start of the block.
 
                 if (!tryDeclareVarForAnnexBLexicalFunction(funName, tryAnnexB))
                     return false;
             }
 
-            if (!noteDeclaredName(funName, declKind, pos))
+            if (!noteDeclaredName(funName, DeclarationKind::LexicalFunction, pos))
                 return false;
         } else {
             if (!noteDeclaredName(funName, DeclarationKind::BodyLevelFunction, pos))
                 return false;
 
             // Body-level functions in modules are always closed over.
             if (pc->atModuleLevel())
                 pc->varScope().lookupDeclaredName(funName)->value()->setClosedOver();
@@ -3523,24 +3522,19 @@ Parser<ParseHandler>::maybeParseDirectiv
             if (pc->isFunctionBox())
                 return asmJS(list);
             return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
         }
     }
     return true;
 }
 
-/*
- * Parse the statements in a block, creating a StatementList node that lists
- * the statements.  If called from block-parsing code, the caller must match
- * '{' before and '}' after.
- */
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::statements(YieldHandling yieldHandling)
+Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
 {
     JS_CHECK_RECURSION(context, return null());
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
 
     bool canHaveDirectives = pc->atBodyLevel();
@@ -3557,17 +3551,17 @@ Parser<ParseHandler>::statements(YieldHa
         if (tt == TOK_EOF || tt == TOK_RC)
             break;
         if (afterReturn) {
             TokenPos pos(0, 0);
             if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
                 return null();
             statementBegin = pos.begin;
         }
-        Node next = statement(yieldHandling, canHaveDirectives);
+        Node next = statementListItem(yieldHandling, canHaveDirectives);
         if (!next) {
             if (tokenStream.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (!warnedAboutStatementsAfterReturn) {
             if (afterReturn) {
                 if (!handler.isStatementPermittedAfterReturnStatement(next)) {
@@ -3952,17 +3946,17 @@ Parser<ParseHandler>::blockStatement(Yie
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
-    Node list = statements(yieldHandling);
+    Node list = statementList(yieldHandling);
     if (!list)
         return null();
 
     MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber);
 
     return finishLexicalScope(scope, list);
 }
 
@@ -4859,16 +4853,37 @@ Parser<ParseHandler>::expressionStatemen
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited, invoked);
     if (!pnexpr)
         return null();
     if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
+template <class ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
+{
+    TokenKind next;
+    if (!tokenStream.peekToken(&next, TokenStream::Operand))
+        return null();
+
+    if (next == TOK_FUNCTION) {
+        // Apply Annex B.3.4 in non-strict code to allow FunctionDeclaration as
+        // the consequent/alternative of an |if| or |else|.  Parser::statement
+        // will report the strict mode error.
+        if (!pc->sc()->strict()) {
+            tokenStream.consumeKnownToken(next, TokenStream::Operand);
+            return functionStmt(yieldHandling, NameRequired);
+        }
+    }
+
+    return statement(yieldHandling);
+}
+
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
 {
     Vector<Node, 4> condList(context), thenList(context);
     Vector<uint32_t, 4> posList(context);
     Node elseBranch;
 
@@ -4885,32 +4900,32 @@ Parser<ParseHandler>::ifStatement(YieldH
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_SEMI) {
             if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
                 return null();
         }
 
-        Node thenBranch = statement(yieldHandling);
+        Node thenBranch = consequentOrAlternative(yieldHandling);
         if (!thenBranch)
             return null();
 
         if (!condList.append(cond) || !thenList.append(thenBranch) || !posList.append(begin))
             return null();
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
             return null();
         if (matched) {
             if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
                 return null();
             if (matched)
                 continue;
-            elseBranch = statement(yieldHandling);
+            elseBranch = consequentOrAlternative(yieldHandling);
             if (!elseBranch)
                 return null();
         } else {
             elseBranch = null();
         }
         break;
     }
 
@@ -5057,23 +5072,34 @@ Parser<ParseHandler>::forHeadStart(Yield
     // parse as an identifier.  (|let| in for-of is always a declaration.)
     // Thus we must can't just sniff out TOK_CONST/TOK_LET here.  :-(
     bool parsingLexicalDeclaration = false;
     bool letIsIdentifier = false;
     if (tt == TOK_LET || tt == TOK_CONST) {
         parsingLexicalDeclaration = true;
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
     } else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) {
-        // Check for the backwards-compatibility corner case in sloppy
-        // mode like |for (let in e)| where the 'let' token should be
-        // parsed as an identifier.
-        if (!peekShouldParseLetDeclaration(&parsingLexicalDeclaration, TokenStream::Operand))
+        MOZ_ASSERT(!pc->sc()->strict(),
+                   "should parse |let| as TOK_LET in strict mode code");
+
+        // We could have a {For,Lexical}Declaration, or we could have a
+        // LeftHandSideExpression with lookahead restrictions so it's not
+        // ambiguous with the former.  Check for a continuation of the former
+        // to decide which we have.
+        tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
+
+        TokenKind next;
+        if (!tokenStream.peekToken(&next))
             return false;
 
-        letIsIdentifier = !parsingLexicalDeclaration;
+        parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next, yieldHandling);
+        if (!parsingLexicalDeclaration) {
+            tokenStream.ungetToken();
+            letIsIdentifier = true;
+        }
     }
 
     if (parsingLexicalDeclaration) {
         forLoopLexicalScope.emplace(this);
         if (!forLoopLexicalScope->init(pc))
             return null();
 
         // Push a temporary ForLoopLexicalHead Statement that allows for
@@ -5381,17 +5407,17 @@ Parser<ParseHandler>::switchStatement(Yi
             if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
                 break;
             if (afterReturn) {
                 TokenPos pos(0, 0);
                 if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
                     return null();
                 statementBegin = pos.begin;
             }
-            Node stmt = statement(yieldHandling);
+            Node stmt = statementListItem(yieldHandling);
             if (!stmt)
                 return null();
             if (!warnedAboutStatementsAfterReturn) {
                 if (afterReturn) {
                     if (!handler.isStatementPermittedAfterReturnStatement(stmt)) {
                         if (!reportWithOffset(ParseWarning, false, statementBegin,
                                               JSMSG_STMT_AFTER_RETURN))
                         {
@@ -5771,16 +5797,51 @@ SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::withStatement(YieldHandling yieldHandling)
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return null();
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
+Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling)
+{
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+
+    if (tt == TOK_FUNCTION) {
+        TokenKind next;
+        if (!tokenStream.peekToken(&next))
+            return null();
+
+        // GeneratorDeclaration is only matched by HoistableDeclaration in
+        // StatementListItem, so generators can't be inside labels.
+        if (next == TOK_MUL) {
+            report(ParseError, false, null(), JSMSG_GENERATOR_LABEL);
+            return null();
+        }
+
+        // Per 13.13.1 it's a syntax error if LabelledItem: FunctionDeclaration
+        // is ever matched.  Per Annex B.3.2 that modifies this text, this
+        // applies only to strict mode code.
+        if (pc->sc()->strict()) {
+            report(ParseError, false, null(), JSMSG_FUNCTION_LABEL);
+            return null();
+        }
+
+        return functionStmt(yieldHandling, NameRequired);
+    }
+
+    tokenStream.ungetToken();
+    return statement(yieldHandling);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
 Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
 {
     uint32_t begin = pos().begin;
     RootedPropertyName label(context, tokenStream.currentName());
 
     auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
         return stmt->label() == label;
     };
@@ -5789,17 +5850,17 @@ Parser<ParseHandler>::labeledStatement(Y
         report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
         return null();
     }
 
     tokenStream.consumeKnownToken(TOK_COLON);
 
     /* Push a label struct and parse the statement. */
     ParseContext::LabelStatement stmt(pc, label);
-    Node pn = statement(yieldHandling);
+    Node pn = labeledItem(yieldHandling);
     if (!pn)
         return null();
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
@@ -5860,17 +5921,17 @@ Parser<ParseHandler>::tryStatement(Yield
     {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
 
         ParseContext::Statement stmt(pc, StatementKind::Try);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
-        innerBlock = statements(yieldHandling);
+        innerBlock = statementList(yieldHandling);
         if (!innerBlock)
             return null();
 
         innerBlock = finishLexicalScope(scope, innerBlock);
         if (!innerBlock)
             return null();
 
         MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY);
@@ -6000,17 +6061,17 @@ Parser<ParseHandler>::tryStatement(Yield
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
 
         ParseContext::Statement stmt(pc, StatementKind::Finally);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
-        finallyBlock = statements(yieldHandling);
+        finallyBlock = statementList(yieldHandling);
         if (!finallyBlock)
             return null();
 
         finallyBlock = finishLexicalScope(scope, finallyBlock);
         if (!finallyBlock)
             return null();
 
         MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY);
@@ -6042,27 +6103,27 @@ Parser<ParseHandler>::catchBlockStatemen
         if (!scope.init(pc))
             return null();
 
         // The catch parameter name cannot be redeclared inside the catch
         // block, so declare the name in the inner scope.
         if (!noteDeclaredName(simpleCatchParam, DeclarationKind::SimpleCatchParameter, pos()))
             return null();
 
-        Node list = statements(yieldHandling);
+        Node list = statementList(yieldHandling);
         if (!list)
             return null();
 
         // The catch parameter name is not bound in this scope, so remove it
         // before generating bindings.
         scope.removeSimpleCatchParameter(pc, simpleCatchParam);
 
         body = finishLexicalScope(scope, list);
     } else {
-        body = statements(yieldHandling);
+        body = statementList(yieldHandling);
     }
     if (!body)
         return null();
 
     MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH);
 
     return body;
 }
@@ -6356,127 +6417,104 @@ SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::classDefinition(YieldHandling yieldHandling,
                                             ClassContext classContext,
                                             DefaultHandling defaultHandling)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
-template <typename ParseHandler>
+template <class ParseHandler>
 bool
-Parser<ParseHandler>::shouldParseLetDeclaration(bool* parseDeclOut)
-{
-    TokenKind tt;
-    if (!tokenStream.peekToken(&tt))
-        return false;
-
-    if (tt == TOK_NAME) {
-        // |let| followed by a name is a lexical declaration.  This is so even
-        // if the name is on a new line.  ASI applies *only* if an offending
-        // token not allowed by the grammar is encountered, and there's no
-        // [no LineTerminator here] restriction in LexicalDeclaration or
-        // ForDeclaration forbidding a line break.
+Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME),
+               "TOK_LET should have been summarily considered a "
+               "LexicalDeclaration");
+    MOZ_ASSERT(tokenStream.currentName() == context->names().let);
+
+#ifdef DEBUG
+    TokenKind verify;
+    MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
+    MOZ_ASSERT(next == verify);
+#endif
+
+    // Destructuring is (for once) the easy case.
+    if (next == TOK_LB || next == TOK_LC)
+        return true;
+
+    // Otherwise a let declaration must have a name.
+    if (next == TOK_NAME) {
+        // One non-"yield" TOK_NAME edge case deserves special comment.
+        // Consider this:
         //
-        // It's a tricky point, but this is true *even if* the name is "let", a
-        // name that can't be bound by LexicalDeclaration or ForDeclaration.
-        // Per ES6 5.3, static semantics early errors are validated *after*
-        // determining productions matching the source text.  So in this
-        // example:
-        //
-        //   let   // ASI opportunity...except not
+        //   let     // not an ASI opportunity
         //   let;
         //
-        // the text matches LexicalDeclaration.  *Then* static semantics in
-        // ES6 13.3.1.1 (corresponding to the LexicalDeclaration production
-        // just chosen), per ES6 5.3, are validated to recognize the Script as
-        // invalid.  It can't be evaluated, so a SyntaxError is thrown.
-        *parseDeclOut = true;
-    } else if (tt == TOK_LB || tt == TOK_LC) {
-        *parseDeclOut = true;
-    } else {
-        // Whatever we have isn't a declaration.  Either it's an expression, or
-        // it's invalid: expression-parsing code will decide.
-        *parseDeclOut = false;
-    }
-
-    return true;
-}
-
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::peekShouldParseLetDeclaration(bool* parseDeclOut,
-                                                    TokenStream::Modifier modifier)
-{
-    // 'let' is a reserved keyword in strict mode and we shouldn't get here.
-    MOZ_ASSERT(!pc->sc()->strict());
-
-    *parseDeclOut = false;
-
-#ifdef DEBUG
-    TokenKind tt;
-    if (!tokenStream.peekToken(&tt, modifier))
+        // Static semantics in §13.3.1.1 turn a LexicalDeclaration that binds
+        // "let" into an early error.  Does this retroactively permit ASI so
+        // that we should parse this as two ExpressionStatements?   No.  ASI
+        // resolves during parsing.  Static semantics only apply to the full
+        // parse tree with ASI applied.  No backsies!
+        if (tokenStream.nextName() != context->names().yield)
+            return true;
+    } else if (next != TOK_YIELD) {
         return false;
-    MOZ_ASSERT(tt == TOK_NAME && tokenStream.nextName() == context->names().let);
-#endif
-
-    tokenStream.consumeKnownToken(TOK_NAME, modifier);
-    if (!shouldParseLetDeclaration(parseDeclOut))
-        return false;
-
-    // Unget the TOK_NAME of 'let' if not parsing a declaration.
-    if (!*parseDeclOut)
-        tokenStream.ungetToken();
-
-    return true;
+    }
+
+    // We have the name "yield": the grammar parameter exactly states whether
+    // this is okay.  Even if YieldIsKeyword, the code might be valid if ASI
+    // induces a preceding semicolon.  If YieldIsName, the code is valid
+    // outside strict mode, and declaration-parsing code will enforce strict
+    // mode restrictions.
+    //
+    // No checkYieldNameValidity for TOK_YIELD is needed here.  It'll happen
+    // when TOK_YIELD is consumed as BindingIdentifier or as start of a fresh
+    // Statement.
+    return yieldHandling == YieldIsName;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
+Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
+{
+    Node vars = declarationList(yieldHandling, PNK_VAR);
+    if (!vars)
+        return null();
+    if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+        return null();
+    return vars;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     switch (tt) {
       // BlockStatement[?Yield, ?Return]
       case TOK_LC:
         return blockStatement(yieldHandling);
 
       // VariableStatement[?Yield]
-      case TOK_VAR: {
-        Node pn = declarationList(yieldHandling, PNK_VAR);
-        if (!pn)
-            return null();
-        if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
-            return null();
-        return pn;
-      }
+      case TOK_VAR:
+        return variableStatement(yieldHandling);
 
       // EmptyStatement
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
 
       // ExpressionStatement[?Yield].
-      //
-      // These should probably be handled by a single ExpressionStatement
-      // function in a default, not split up this way.
-      case TOK_STRING:
-        if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
-            if (!abortIfSyntaxParser())
-                return null();
-            if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
-                return null();
-        }
-        return expressionStatement(yieldHandling);
 
       case TOK_YIELD: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
         TokenStream::Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
@@ -6489,39 +6527,68 @@ Parser<ParseHandler>::statement(YieldHan
             if (!checkYieldNameValidity())
                 return null();
             return labeledStatement(yieldHandling);
         }
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NAME: {
-        // 'let' is a contextual keyword outside strict mode.  In strict mode
-        // it's always tokenized as TOK_LET except in this one weird case:
-        //
-        //   "use strict" // ExpressionStatement, terminated by ASI
-        //   let a = 1;   // LexicalDeclaration
-        //
-        // We can't apply strict mode until we know "use strict" is the entire
-        // statement, but we can't know "use strict" is the entire statement
-        // until we see the next token.  So 'let' is still TOK_NAME here.
-        if (tokenStream.currentName() == context->names().let) {
-            bool parseDecl;
-            if (!shouldParseLetDeclaration(&parseDecl))
-                return null();
-
-            if (parseDecl)
-                return lexicalDeclaration(yieldHandling, /* isConst = */ false);
-        }
-
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
+
+#ifdef DEBUG
+        if (tokenStream.currentName() == context->names().let) {
+            MOZ_ASSERT(!pc->sc()->strict(),
+                       "observing |let| as TOK_NAME and not TOK_LET implies "
+                       "non-strict code (and the edge case of 'use strict' "
+                       "immediately followed by |let| on a new line only "
+                       "applies to StatementListItems, not to Statements)");
+        }
+#endif
+
+        // Statement context forbids LexicalDeclaration.
+        if ((next == TOK_LB || next == TOK_LC || next == TOK_NAME) &&
+            tokenStream.currentName() == context->names().let)
+        {
+            bool forbiddenLetDeclaration = false;
+            if (next == TOK_LB) {
+                // ExpressionStatement has a 'let [' lookahead restriction.
+                forbiddenLetDeclaration = true;
+            } else {
+                // 'let {' and 'let foo' aren't completely forbidden, if ASI
+                // causes 'let' to be the entire Statement.  But if they're
+                // same-line, we can aggressively give a better error message.
+                //
+                // Note that this ignores 'yield' as TOK_YIELD: we'll handle it
+                // correctly but with a worse error message.
+                TokenKind nextSameLine;
+                if (!tokenStream.peekTokenSameLine(&nextSameLine))
+                    return null();
+
+                MOZ_ASSERT(nextSameLine == TOK_NAME ||
+                           nextSameLine == TOK_LC ||
+                           nextSameLine == TOK_EOL);
+
+                forbiddenLetDeclaration = nextSameLine != TOK_EOL;
+            }
+
+            if (forbiddenLetDeclaration) {
+                report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT,
+                       "lexical declarations");
+                return null();
+            }
+        }
+
+        // NOTE: It's unfortunately allowed to have a label named 'let' in
+        //       non-strict code.  💯
         if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
+
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NEW:
         return expressionStatement(yieldHandling, PredictInvoked);
 
       default:
         return expressionStatement(yieldHandling);
@@ -6580,33 +6647,232 @@ Parser<ParseHandler>::statement(YieldHan
       // TryStatement[?Yield, ?Return]
       case TOK_TRY:
         return tryStatement(yieldHandling);
 
       // DebuggerStatement
       case TOK_DEBUGGER:
         return debuggerStatement();
 
-      // HoistableDeclaration[?Yield]
+      // |function| is forbidden by lookahead restriction (unless as child
+      // statement of |if| or |else|, but Parser::consequentOrAlternative
+      // handles that).
+      case TOK_FUNCTION:
+        report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
+        return null();
+
+      // |class| is also forbidden by lookahead restriction.
+      case TOK_CLASS:
+        report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
+        return null();
+
+      // ImportDeclaration (only inside modules)
+      case TOK_IMPORT:
+        return importDeclaration();
+
+      // ExportDeclaration (only inside modules)
+      case TOK_EXPORT:
+        return exportDeclaration();
+
+      // Miscellaneous error cases arguably better caught here than elsewhere.
+
+      case TOK_CATCH:
+        report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
+        return null();
+
+      case TOK_FINALLY:
+        report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
+        return null();
+
+      // TOK_LET implies we're in strict mode code where static semantics
+      // forbid IdentifierName to be "let": a stronger restriction than
+      // Statement's lookahead restriction on |let [|.  Provide a better error
+      // message here than the default case would.
+      case TOK_LET:
+        report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "let declarations");
+        return null();
+
+      // NOTE: default case handled in the ExpressionStatement section.
+    }
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
+                                        bool canHaveDirectives /* = false */)
+{
+    MOZ_ASSERT(checkOptionsCalled);
+
+    JS_CHECK_RECURSION(context, return null());
+
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+
+    switch (tt) {
+      // BlockStatement[?Yield, ?Return]
+      case TOK_LC:
+        return blockStatement(yieldHandling);
+
+      // VariableStatement[?Yield]
+      case TOK_VAR:
+        return variableStatement(yieldHandling);
+
+      // EmptyStatement
+      case TOK_SEMI:
+        return handler.newEmptyStatement(pos());
+
+      // ExpressionStatement[?Yield].
+      //
+      // These should probably be handled by a single ExpressionStatement
+      // function in a default, not split up this way.
+      case TOK_STRING:
+        if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
+            if (!abortIfSyntaxParser())
+                return null();
+            if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
+                return null();
+        }
+        return expressionStatement(yieldHandling);
+
+      case TOK_YIELD: {
+        // Don't use a ternary operator here due to obscure linker issues
+        // around using static consts in the arms of a ternary.
+        TokenStream::Modifier modifier;
+        if (yieldExpressionsSupported())
+            modifier = TokenStream::Operand;
+        else
+            modifier = TokenStream::None;
+
+        TokenKind next;
+        if (!tokenStream.peekToken(&next, modifier))
+            return null();
+        if (next == TOK_COLON) {
+            if (!checkYieldNameValidity())
+                return null();
+            return labeledStatement(yieldHandling);
+        }
+        return expressionStatement(yieldHandling);
+      }
+
+      case TOK_NAME: {
+        TokenKind next;
+        if (!tokenStream.peekToken(&next))
+            return null();
+
+        if (tokenStream.currentName() == context->names().let) {
+            if (nextTokenContinuesLetDeclaration(next, yieldHandling))
+                return lexicalDeclaration(yieldHandling, /* isConst = */ false);
+
+            // IdentifierName can't be "let" in strict mode code.  |let| in
+            // strict mode code is usually TOK_LET, but in this one weird case
+            // in global code it's TOK_NAME:
+            //
+            //   "use strict"              // ExpressionStatement ended by ASI
+            //   let <...whatever else...> // a fresh StatementListItem
+            //
+            // Carefully reject strict mode |let| non-declarations.
+            if (pc->sc()->strict()) {
+                report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+                       "declaration pattern", TokenKindToDesc(next));
+                return null();
+            }
+        }
+
+        if (next == TOK_COLON)
+            return labeledStatement(yieldHandling);
+
+        return expressionStatement(yieldHandling);
+      }
+
+      case TOK_NEW:
+        return expressionStatement(yieldHandling, PredictInvoked);
+
+      default:
+        return expressionStatement(yieldHandling);
+
+      // IfStatement[?Yield, ?Return]
+      case TOK_IF:
+        return ifStatement(yieldHandling);
+
+      // BreakableStatement[?Yield, ?Return]
+      //
+      // BreakableStatement[Yield, Return]:
+      //   IterationStatement[?Yield, ?Return]
+      //   SwitchStatement[?Yield, ?Return]
+      case TOK_DO:
+        return doWhileStatement(yieldHandling);
+
+      case TOK_WHILE:
+        return whileStatement(yieldHandling);
+
+      case TOK_FOR:
+        return forStatement(yieldHandling);
+
+      case TOK_SWITCH:
+        return switchStatement(yieldHandling);
+
+      // ContinueStatement[?Yield]
+      case TOK_CONTINUE:
+        return continueStatement(yieldHandling);
+
+      // BreakStatement[?Yield]
+      case TOK_BREAK:
+        return breakStatement(yieldHandling);
+
+      // [+Return] ReturnStatement[?Yield]
+      case TOK_RETURN:
+        // The Return parameter is only used here, and the effect is easily
+        // detected this way, so don't bother passing around an extra parameter
+        // everywhere.
+        if (!pc->isFunctionBox()) {
+            report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
+            return null();
+        }
+        return returnStatement(yieldHandling);
+
+      // WithStatement[?Yield, ?Return]
+      case TOK_WITH:
+        return withStatement(yieldHandling);
+
+      // LabelledStatement[?Yield, ?Return]
+      // This is really handled by TOK_NAME and TOK_YIELD cases above.
+
+      // ThrowStatement[?Yield]
+      case TOK_THROW:
+        return throwStatement(yieldHandling);
+
+      // TryStatement[?Yield, ?Return]
+      case TOK_TRY:
+        return tryStatement(yieldHandling);
+
+      // DebuggerStatement
+      case TOK_DEBUGGER:
+        return debuggerStatement();
+
+      // Declaration[Yield]:
+
+      //   HoistableDeclaration[?Yield, ~Default]
       case TOK_FUNCTION:
         return functionStmt(yieldHandling, NameRequired);
 
-      // ClassDeclaration[?Yield]
+      //   ClassDeclaration[?Yield, ~Default]
       case TOK_CLASS:
         if (!abortIfSyntaxParser())
             return null();
         return classDefinition(yieldHandling, ClassStatement, NameRequired);
 
-      // LexicalDeclaration[In, ?Yield]
+      //   LexicalDeclaration[In, ?Yield]
+      //     LetOrConst BindingList[?In, ?Yield]
       case TOK_LET:
       case TOK_CONST:
         if (!abortIfSyntaxParser())
             return null();
-        // [In] is the default behavior, because for-loops currently specially
-        // parse their heads to handle |in| in this situation.
+        // [In] is the default behavior, because for-loops specially parse
+        // their heads to handle |in| in this situation.
         return lexicalDeclaration(yieldHandling, /* isConst = */ tt == TOK_CONST);
 
       // ImportDeclaration (only inside modules)
       case TOK_IMPORT:
         return importDeclaration();
 
       // ExportDeclaration (only inside modules)
       case TOK_EXPORT:
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -933,17 +933,18 @@ class Parser final : private JS::AutoGCR
     inline Node newName(PropertyName* name);
     inline Node newName(PropertyName* name, TokenPos pos);
     inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
 
     inline bool abortIfSyntaxParser();
 
   public:
     /* Public entry points for parsing. */
-    Node statement(YieldHandling yieldHandling, bool canHaveDirectives = false);
+    Node statement(YieldHandling yieldHandling);
+    Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
 
     bool maybeParseDirective(Node list, Node pn, bool* cont);
 
     // Parse the body of an eval.
     //
     // Eval scripts are distinguished from global scripts in that in ES6, per
     // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh
     // lexical scope.
@@ -1012,21 +1013,21 @@ class Parser final : private JS::AutoGCR
      * number of the parsers this is convenient and avoids a lot of
      * unnecessary ungetting and regetting of tokens.
      *
      * Some parsers have two versions:  an always-inlined version (with an 'i'
      * suffix) and a never-inlined version (with an 'n' suffix).
      */
     Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling);
     Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
-    Node statements(YieldHandling yieldHandling);
+
+    Node statementList(YieldHandling yieldHandling);
 
     Node blockStatement(YieldHandling yieldHandling,
                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
-    Node ifStatement(YieldHandling yieldHandling);
     Node doWhileStatement(YieldHandling yieldHandling);
     Node whileStatement(YieldHandling yieldHandling);
 
     Node forStatement(YieldHandling yieldHandling);
     bool forHeadStart(YieldHandling yieldHandling,
                       ParseNodeKind* forHeadKind,
                       Node* forInitialPart,
                       mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
@@ -1034,23 +1035,36 @@ class Parser final : private JS::AutoGCR
     bool validateForInOrOfLHSExpression(Node target);
     Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
 
     Node switchStatement(YieldHandling yieldHandling);
     Node continueStatement(YieldHandling yieldHandling);
     Node breakStatement(YieldHandling yieldHandling);
     Node returnStatement(YieldHandling yieldHandling);
     Node withStatement(YieldHandling yieldHandling);
-    Node labeledStatement(YieldHandling yieldHandling);
     Node throwStatement(YieldHandling yieldHandling);
     Node tryStatement(YieldHandling yieldHandling);
     Node catchBlockStatement(YieldHandling yieldHandling, HandlePropertyName simpleCatchParam);
     Node debuggerStatement();
 
+    Node variableStatement(YieldHandling yieldHandling);
+
+    Node labeledStatement(YieldHandling yieldHandling);
+    Node labeledItem(YieldHandling yieldHandling);
+
+    Node ifStatement(YieldHandling yieldHandling);
+    Node consequentOrAlternative(YieldHandling yieldHandling);
+
+    // While on a |let| TOK_NAME token, examine |next|.  Indicate whether
+    // |next|, the next token already gotten with modifier TokenStream::None,
+    // continues a LexicalDeclaration.
+    bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling);
+
     Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst);
+
     Node importDeclaration();
     Node exportDeclaration();
     Node expressionStatement(YieldHandling yieldHandling,
                              InvokedPrediction invoked = PredictUninvoked);
 
     // Declaration parsing.  The main entrypoint is Parser::declarationList,
     // with sub-functionality split out into the remaining methods.
 
@@ -1234,25 +1248,16 @@ class Parser final : private JS::AutoGCR
     bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling,
                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                                      bool tryAnnexB, Directives inheritedDirectives,
                                      Directives* newDirectives);
     bool finishFunctionScopes();
     bool finishFunction();
     bool leaveInnerFunction(ParseContext* outerpc);
 
-    // Use when the current token is TOK_NAME and is known to be 'let'.
-    bool shouldParseLetDeclaration(bool* parseDeclOut);
-
-    // Use when the lookahead token is TOK_NAME and is known to be 'let'. If a
-    // let declaration should be parsed, the TOK_NAME token of 'let' is
-    // consumed. Otherwise, the current token remains the TOK_NAME token of
-    // 'let'.
-    bool peekShouldParseLetDeclaration(bool* parseDeclOut, TokenStream::Modifier modifier);
-
   public:
     enum FunctionCallBehavior {
         PermitAssignmentToFunctionCalls,
         ForbidAssignmentToFunctionCalls
     };
 
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -1,12 +1,14 @@
 setJitCompilerOption("baseline.warmup.trigger", 10);
 setJitCompilerOption("ion.warmup.trigger", 20);
 var i;
 
+var config = getBuildConfiguration();
+
 // Check that we are able to remove the operation inside recover test functions (denoted by "rop..."),
 // when we inline the first version of uceFault, and ensure that the bailout is correct
 // when uceFault is replaced (which cause an invalidation bailout)
 
 var uceFault = function (i) {
     if (i > 98)
         uceFault = function (i) { return true; };
     return false;
@@ -1281,16 +1283,39 @@ function rhypot_object_4args(i) {
     t2 = 3000;
     t3 = 4000;
     if (uceFault_hypot_object_4args(i) || uceFault_hypot_object_4args(i) )
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
     assertRecoveredOnBailout(x, false);
     return i;
 }
 
+var uceFault_random = eval(uneval(uceFault).replace('uceFault', 'uceFault_random'));
+function rrandom(i) {
+    // setRNGState() exists only in debug builds
+
+    if(config.debug) {
+        setRNGState(2, 0);
+        var x = Math.random();
+        if (uceFault_random(i) || uceFault_random(i)) {
+            setRNGState(2, 0);
+            assertEq(x, Math.random());
+        }
+        assertRecoveredOnBailout(x, true);
+    } else {
+        var x = Math.random();
+        if (uceFault_random(i) || uceFault_random(i)) {
+            Math.random();
+        }
+        assertRecoveredOnBailout(x, true);
+    }
+
+    return i;
+}
+
 var uceFault_sin_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_number'));
 function rsin_number(i) {
     var x = Math.sin(i);
     if (uceFault_sin_number(i) || uceFault_sin_number(i))
         assertEq(x, Math.sin(i));
     assertRecoveredOnBailout(x, true);
     return i;
 }
@@ -1439,16 +1464,17 @@ for (i = 0; i < 100; i++) {
     rtrunc_to_int32_object(i);
     rtrunc_to_int32_string(i);
     rhypot_number_2args(i);
     rhypot_number_3args(i);
     rhypot_number_4args(i);
     rhypot_object_2args(i);
     rhypot_object_3args(i);
     rhypot_object_4args(i);
+    rrandom(i);
     rsin_number(i);
     rsin_object(i);
     rlog_number(i);
     rlog_object(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/typedarray/sort.js
@@ -0,0 +1,24 @@
+setJitCompilerOption("ion.warmup.trigger", 40);
+
+const constructors = [
+    Int8Array,
+    Uint8Array,
+    Uint8ClampedArray,
+    Int16Array,
+    Uint16Array,
+    Int32Array,
+    Uint32Array,
+    Float32Array,
+    Float64Array ];
+
+// Ensure that when creating TypedArrays under JIT
+// the sort() method works as expected (bug 1295034).
+for (var ctor of constructors) {
+  for (var _ of Array(1024)) {
+    var testArray = new ctor(10);
+    testArray.sort();
+  }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11515,20 +11515,20 @@ CodeGenerator::visitAsmJSInterruptCheck(
     MOZ_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % ABIStackAlignment == 0);
     masm.call(wasm::SymbolicAddress::HandleExecutionInterrupt);
     masm.branchIfFalseBool(ReturnReg, wasm::JumpTarget::Throw);
 
     masm.bind(&rejoin);
 }
 
 void
-CodeGenerator::visitAsmThrowUnreachable(LAsmThrowUnreachable* lir)
+CodeGenerator::visitWasmTrap(LWasmTrap* lir)
 {
     MOZ_ASSERT(gen->compilingAsmJS());
-    masm.jump(wasm::JumpTarget::Unreachable);
+    masm.jump(wasm::JumpTarget(lir->mir()->trap()));
 }
 
 typedef bool (*RecompileFn)(JSContext*);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile, "Recompile");
 
 typedef bool (*ForcedRecompileFn)(JSContext*);
 static const VMFunction ForcedRecompileFnInfo =
     FunctionInfo<ForcedRecompileFn>(ForcedRecompile, "ForcedRecompile");
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -410,17 +410,17 @@ class CodeGenerator final : public CodeG
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
-    void visitAsmThrowUnreachable(LAsmThrowUnreachable* lir);
+    void visitWasmTrap(LWasmTrap* lir);
     void visitRecompileCheck(LRecompileCheck* ins);
     void visitRotate(LRotate* ins);
 
     void visitRandom(LRandom* ins);
     void visitSignExtend(LSignExtend* ins);
 
 #ifdef DEBUG
     void emitDebugForceBailing(LInstruction* lir);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2525,19 +2525,19 @@ LIRGenerator::visitInterruptCheck(MInter
 void
 LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins)
 {
     gen->setPerformsCall();
     add(new(alloc()) LAsmJSInterruptCheck, ins);
 }
 
 void
-LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins)
-{
-    add(new(alloc()) LAsmThrowUnreachable, ins);
+LIRGenerator::visitWasmTrap(MWasmTrap* ins)
+{
+    add(new(alloc()) LWasmTrap, ins);
 }
 
 void
 LIRGenerator::visitAsmReinterpret(MAsmReinterpret* ins)
 {
     if (ins->type() == MIRType::Int64)
         defineInt64(new(alloc()) LAsmReinterpretToI64(useRegisterAtStart(ins->input())), ins);
     else if (ins->input()->type() == MIRType::Int64)
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -186,17 +186,17 @@ class LIRGenerator : public LIRGenerator
     void visitConvertElementsToDoubles(MConvertElementsToDoubles* ins);
     void visitMaybeToDoubleElement(MMaybeToDoubleElement* ins);
     void visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins);
     void visitLoadSlot(MLoadSlot* ins);
     void visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins);
     void visitFunctionEnvironment(MFunctionEnvironment* ins);
     void visitInterruptCheck(MInterruptCheck* ins);
     void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins);
-    void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins);
+    void visitWasmTrap(MWasmTrap* ins);
     void visitAsmReinterpret(MAsmReinterpret* ins);
     void visitStoreSlot(MStoreSlot* ins);
     void visitFilterTypeSet(MFilterTypeSet* ins);
     void visitTypeBarrier(MTypeBarrier* ins);
     void visitMonitorTypes(MMonitorTypes* ins);
     void visitPostWriteBarrier(MPostWriteBarrier* ins);
     void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins);
     void visitArrayLength(MArrayLength* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6579,16 +6579,26 @@ class MRandom : public MNullaryInstructi
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 
     void computeRange(TempAllocator& alloc) override;
 
+    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+
+    bool canRecoverOnBailout() const override {
+#ifdef JS_MORE_DETERMINISTIC
+        return false;
+#else
+        return true;
+#endif
+    }
+
     ALLOW_CLONE(MRandom)
 };
 
 class MMathFunction
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>::Data
 {
   public:
@@ -7733,28 +7743,38 @@ class MInterruptCheck : public MNullaryI
 class MAsmJSInterruptCheck
   : public MNullaryInstruction
 {
   public:
     INSTRUCTION_HEADER(AsmJSInterruptCheck)
     TRIVIAL_NEW_WRAPPERS
 };
 
-// Directly jumps to the unreachable trap handler.
-class MAsmThrowUnreachable
+// Directly jumps to the indicated trap, leaving Wasm code and reporting a
+// runtime error.
+
+class MWasmTrap
   : public MAryControlInstruction<0, 0>,
     public NoTypePolicy::Data
 {
-  public:
-    INSTRUCTION_HEADER(AsmThrowUnreachable)
-    TRIVIAL_NEW_WRAPPERS
-
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
-    }
+    wasm::Trap trap_;
+
+    explicit MWasmTrap(wasm::Trap trap)
+      : trap_(trap)
+    {}
+
+  public:
+    INSTRUCTION_HEADER(WasmTrap)
+    TRIVIAL_NEW_WRAPPERS
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    wasm::Trap trap() const { return trap_; }
 };
 
 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
 // it to baseline to throw at the correct pc.
 class MLexicalCheck
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
 {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -257,28 +257,28 @@ namespace jit {
     _(Floor)                                                                \
     _(Ceil)                                                                 \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(AsmJSInterruptCheck)                                                  \
-    _(AsmThrowUnreachable)                                                  \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsConstructor)                                                        \
     _(IsCallable)                                                           \
     _(IsObject)                                                             \
     _(HasClass)                                                             \
     _(CopySign)                                                             \
     _(WasmBoundsCheck)                                                      \
     _(WasmLoad)                                                             \
     _(WasmStore)                                                            \
+    _(WasmTrap)                                                             \
     _(WasmTruncateToInt32)                                                  \
     _(AsmJSNeg)                                                             \
     _(AsmJSUnsignedToDouble)                                                \
     _(AsmJSUnsignedToFloat32)                                               \
     _(AsmJSLoadHeap)                                                        \
     _(AsmJSStoreHeap)                                                       \
     _(WasmLoadGlobalVar)                                                    \
     _(WasmStoreGlobalVar)                                                   \
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1001,16 +1001,34 @@ RMathFunction::recover(JSContext* cx, Sn
         return true;
       }
       default:
         MOZ_CRASH("Unknown math function.");
     }
 }
 
 bool
+MRandom::writeRecoverData(CompactBufferWriter& writer) const
+{
+    MOZ_ASSERT(this->canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_Random));
+    return true;
+}
+
+RRandom::RRandom(CompactBufferReader& reader)
+{}
+
+bool
+RRandom::recover(JSContext* cx, SnapshotIterator& iter) const
+{
+    iter.storeInstructionResult(DoubleValue(math_random_impl(cx)));
+    return true;
+}
+
+bool
 MStringSplit::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_StringSplit));
     return true;
 }
 
 RStringSplit::RStringSplit(CompactBufferReader& reader)
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -83,16 +83,17 @@ namespace jit {
     _(Pow)                                      \
     _(PowHalf)                                  \
     _(MinMax)                                   \
     _(Abs)                                      \
     _(Sqrt)                                     \
     _(Atan2)                                    \
     _(Hypot)                                    \
     _(MathFunction)                             \
+    _(Random)                                   \
     _(StringSplit)                              \
     _(RegExpMatcher)                            \
     _(RegExpSearcher)                           \
     _(RegExpTester)                             \
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
@@ -457,16 +458,23 @@ class RMathFunction final : public RInst
     uint8_t function_;
 
   public:
     RINSTRUCTION_HEADER_NUM_OP_(MathFunction, 1)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
+class RRandom final : public RInstruction
+{
+    RINSTRUCTION_HEADER_NUM_OP_(Random, 0)
+  public:
+    MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
+};
+
 class RStringSplit final : public RInstruction
 {
   public:
     RINSTRUCTION_HEADER_NUM_OP_(StringSplit, 3)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2286,21 +2286,17 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs
     memoryBarrier(mir->barrierAfter());
 }
 
 void
 CodeGeneratorARM::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
 {
     MWasmBoundsCheck* mir = ins->mir();
 
-    uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        masm.as_b(wasm::JumpTarget::OutOfBounds);
-        return;
-    }
+    MOZ_ASSERT(mir->offset() <= INT32_MAX);
 
     if (!mir->isRedundant()) {
         // No guarantee that heapBase + endOffset can be properly encoded in
         // the cmp immediate in ma_BoundsCheck, so use an explicit add instead.
         uint32_t endOffset = mir->endOffset();
 
         Register ptr = ToRegister(ins->ptr());
 
@@ -2343,21 +2339,17 @@ template <typename T>
 void
 CodeGeneratorARM::emitWasmLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        // This is unreachable because of bounds checks.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(offset <= INT32_MAX);
 
     Register ptr = ToRegister(lir->ptr());
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         masm.ma_add(Imm32(offset), ptrPlusOffset);
@@ -2414,21 +2406,17 @@ template<typename T>
 void
 CodeGeneratorARM::emitWasmUnalignedLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        // This is unreachable because of bounds checks.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(offset <= INT32_MAX);
 
     Register ptr = ToRegister(lir->ptrCopy());
     if (offset)
         masm.ma_add(Imm32(offset), ptr);
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
@@ -2498,21 +2486,17 @@ template <typename T>
 void
 CodeGeneratorARM::emitWasmStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        // This is unreachable because of bounds checks.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(offset <= INT32_MAX);
 
     Register ptr = ToRegister(lir->ptr());
     unsigned byteSize = mir->byteSize();
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -1661,20 +1661,17 @@ CodeGeneratorMIPSShared::visitWasmCallI6
 }
 
 void
 CodeGeneratorMIPSShared::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
 {
     MWasmBoundsCheck* mir = ins->mir();
 
     uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        masm.jump(wasm::JumpTarget::OutOfBounds);
-        return;
-    }
+    MOZ_ASSERT(offset <= INT32_MAX);
 
     uint32_t endOffset = mir->endOffset();
     Register ptr = ToRegister(ins->ptr());
 
     masm.move32(Imm32(endOffset), SecondScratchReg);
     masm.addPtr(ptr, SecondScratchReg);
 
     // Detect unsigned overflow.
@@ -1688,21 +1685,17 @@ CodeGeneratorMIPSShared::visitWasmBounds
 void
 CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        // This is unreachable because of bounds checks.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(offset <= INT32_MAX);
 
     Register ptr = ToRegister(lir->ptr());
 
     // Maybe add the offset.
     if (offset) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         masm.addPtr(Imm32(offset), ptrPlusOffset);
         ptr = ptrPlusOffset;
@@ -1740,21 +1733,17 @@ CodeGeneratorMIPSShared::visitWasmLoad(L
 void
 CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
 {
     const MWasmStore* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
-    if (offset > INT32_MAX) {
-        // This is unreachable because of bounds checks.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(offset <= INT32_MAX);
 
     Register ptr = ToRegister(lir->ptr());
 
     // Maybe add the offset.
     if (offset) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         masm.addPtr(Imm32(offset), ptrPlusOffset);
         ptr = ptrPlusOffset;
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -1062,36 +1062,24 @@ MacroAssemblerMIPSCompat::storePtr(Regis
 {
     movePtr(ImmPtr(dest.addr), ScratchRegister);
     storePtr(src, Address(ScratchRegister, 0));
 }
 
 void
 MacroAssemblerMIPSCompat::clampIntToUint8(Register reg)
 {
-    // look at (reg >> 8) if it is 0, then src shouldn't be clamped
-    // if it is <0, then we want to clamp to 0,
-    // otherwise, we wish to clamp to 255
-    Label done;
-    ma_move(ScratchRegister, reg);
-    asMasm().rshiftPtrArithmetic(Imm32(8), ScratchRegister);
-    ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump);
-    {
-        Label negative;
-        ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump);
-        {
-            ma_li(reg, Imm32(255));
-            ma_b(&done, ShortJump);
-        }
-        bind(&negative);
-        {
-            ma_move(reg, zero);
-        }
-    }
-    bind(&done);
+    // If reg is < 0, then we want to clamp to 0.
+    as_slti(ScratchRegister, reg, 0);
+    as_movn(reg, zero, ScratchRegister);
+
+    // If reg is >= 255, then we want to clamp to 255.
+    ma_li(SecondScratchReg, Imm32(255));
+    as_slti(ScratchRegister, reg, 255);
+    as_movz(reg, SecondScratchReg, ScratchRegister);
 }
 
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     MOZ_ASSERT(input != ScratchDoubleReg);
     Label positive, done;
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -1209,36 +1209,24 @@ MacroAssemblerMIPS64Compat::storePtr(Reg
 {
     movePtr(ImmPtr(dest.addr), ScratchRegister);
     storePtr(src, Address(ScratchRegister, 0));
 }
 
 void
 MacroAssemblerMIPS64Compat::clampIntToUint8(Register reg)
 {
-    // look at (reg >> 8) if it is 0, then src shouldn't be clamped
-    // if it is <0, then we want to clamp to 0,
-    // otherwise, we wish to clamp to 255
-    Label done;
-    ma_move(ScratchRegister, reg);
-    asMasm().rshiftPtrArithmetic(Imm32(8), ScratchRegister);
-    ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump);
-    {
-        Label negative;
-        ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump);
-        {
-            ma_li(reg, Imm32(255));
-            ma_b(&done, ShortJump);
-        }
-        bind(&negative);
-        {
-            ma_move(reg, zero);
-        }
-    }
-    bind(&done);
+    // If reg is < 0, then we want to clamp to 0.
+    as_slti(ScratchRegister, reg, 0);
+    as_movn(reg, zero, ScratchRegister);
+
+    // If reg is >= 255, then we want to clamp to 255.
+    ma_li(SecondScratchReg, Imm32(255));
+    as_slti(ScratchRegister, reg, 255);
+    as_movz(reg, SecondScratchReg, ScratchRegister);
 }
 
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     MOZ_ASSERT(input != ScratchDoubleReg);
     Label positive, done;
@@ -1325,25 +1313,23 @@ MacroAssemblerMIPS64Compat::unboxNonDoub
     computeScaledAddress(src, SecondScratchReg);
     loadPtr(Address(SecondScratchReg, src.offset), dest);
     ma_dext(dest, dest, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
 }
 
 void
 MacroAssemblerMIPS64Compat::unboxInt32(const ValueOperand& operand, Register dest)
 {
-    ma_dsll(dest, operand.valueReg(), Imm32(32));
-    ma_dsra(dest, dest, Imm32(32));
+    ma_sll(dest, operand.valueReg(), Imm32(0));
 }
 
 void
 MacroAssemblerMIPS64Compat::unboxInt32(Register src, Register dest)
 {
-    ma_dsll(dest, src, Imm32(32));
-    ma_dsra(dest, dest, Imm32(32));
+    ma_sll(dest, src, Imm32(0));
 }
 
 void
 MacroAssemblerMIPS64Compat::unboxInt32(const Address& src, Register dest)
 {
     load32(Address(src.base, src.offset), dest);
 }
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1399,23 +1399,27 @@ class LAsmJSInterruptCheck : public LIns
     LAsmJSInterruptCheck()
     { }
 
     bool isCall() const {
         return true;
     }
 };
 
-class LAsmThrowUnreachable : public LInstructionHelper<0, 0, 0>
-{
-  public:
-    LIR_HEADER(AsmThrowUnreachable);
-
-    LAsmThrowUnreachable()
+class LWasmTrap : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(WasmTrap);
+
+    LWasmTrap()
     { }
+
+    const MWasmTrap* mir() const {
+        return mir_->toWasmTrap();
+    }
 };
 
 template<size_t Defs, size_t Ops>
 class LAsmReinterpretBase : public LInstructionHelper<Defs, Ops, 0>
 {
     typedef LInstructionHelper<Defs, Ops, 0> Base;
 
   public:
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -364,17 +364,17 @@
     _(RoundF)                       \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(AsmJSInterruptCheck)          \
-    _(AsmThrowUnreachable)          \
+    _(WasmTrap)                     \
     _(AsmReinterpret)               \
     _(AsmReinterpretToI64)          \
     _(AsmReinterpretFromI64)        \
     _(Rotate)                       \
     _(RotateI64)                    \
     _(GetDOMProperty)               \
     _(GetDOMMemberV)                \
     _(GetDOMMemberT)                \
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -533,21 +533,17 @@ void
 CodeGeneratorX64::emitWasmLoad(T* ins)
 {
     const MWasmLoad* mir = ins->mir();
     bool isInt64 = mir->type() == MIRType::Int64;
 
     Scalar::Type accessType = mir->accessType();
     MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
-    if (mir->offset() > INT32_MAX) {
-        masm.jump(wasm::JumpTarget::OutOfBounds);
-        return;
-    }
+    MOZ_ASSERT(mir->offset() <= INT32_MAX);
 
     const LAllocation* ptr = ins->ptr();
     Operand srcAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
     uint32_t before = masm.size();
     if (isInt64)
@@ -578,21 +574,17 @@ template <typename T>
 void
 CodeGeneratorX64::emitWasmStore(T* ins)
 {
     const MWasmStore* mir = ins->mir();
 
     Scalar::Type accessType = mir->accessType();
     MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
-    if (mir->offset() > INT32_MAX) {
-        masm.jump(wasm::JumpTarget::OutOfBounds);
-        return;
-    }
+    MOZ_ASSERT(mir->offset() <= INT32_MAX);
 
     const LAllocation* value = ins->getOperand(ins->ValueIndex);
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
     uint32_t before = masm.size();
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -489,21 +489,19 @@ CodeGeneratorX86Shared::emitAsmJSBoundsC
 
     masm.append(wasm::BoundsCheck(cmpOffset));
 }
 
 void
 CodeGeneratorX86Shared::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
 {
     const MWasmBoundsCheck* mir = ins->mir();
+
     MOZ_ASSERT(gen->needsBoundsCheckBranch(mir));
-    if (mir->offset() > INT32_MAX) {
-        masm.jump(wasm::JumpTarget::OutOfBounds);
-        return;
-    }
+    MOZ_ASSERT(mir->offset() <= INT32_MAX);
 
     Register ptrReg = ToRegister(ins->ptr());
     maybeEmitWasmBoundsCheckBranch(mir, ptrReg, mir->isRedundant());
 }
 
 void
 CodeGeneratorX86Shared::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
 {
@@ -543,19 +541,18 @@ void
 CodeGeneratorX86Shared::maybeEmitWasmBoundsCheckBranch(const MWasmMemoryAccess* mir, Register ptr,
                                                        bool redundant)
 {
     if (!mir->needsBoundsCheck())
         return;
 
     MOZ_ASSERT(mir->endOffset() >= 1,
                "need to subtract 1 to use JAE, see also AssemblerX86Shared::UpdateBoundsCheck");
-    /*
-     * TODO: See 1287224 Unify MWasmBoundsCheck::redunant_ and needsBoundsCheck
-     */
+
+    // TODO: See 1287224 Unify MWasmBoundsCheck::redunant_ and needsBoundsCheck
     if (!redundant) {
         uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(1 - mir->endOffset())).offset();
         masm.j(Assembler::AboveOrEqual, wasm::JumpTarget::OutOfBounds);
         masm.append(wasm::BoundsCheck(cmpOffset));
     } else {
 #ifdef DEBUG
         Label ok;
         uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(1 - mir->endOffset())).offset();
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -495,22 +495,17 @@ template <typename T>
 void
 CodeGeneratorX86::emitWasmLoad(T* ins)
 {
     const MWasmLoad* mir = ins->mir();
 
     Scalar::Type accessType = mir->accessType();
     MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
-    if (mir->offset() > INT32_MAX) {
-        // This is unreachable because of the bounds check.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(mir->offset() <= INT32_MAX);
 
     const LAllocation* ptr = ins->ptr();
     Operand srcAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
     if (mir->type() == MIRType::Int64)
         loadI64(accessType, srcAddr, ToOutRegister64(ins));
@@ -534,22 +529,17 @@ template <typename T>
 void
 CodeGeneratorX86::emitWasmStore(T* ins)
 {
     const MWasmStore* mir = ins->mir();
 
     Scalar::Type accessType = mir->accessType();
     MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
-    if (mir->offset() > INT32_MAX) {
-        // This is unreachable because of the bounds check.
-        masm.breakpoint();
-        return;
-    }
+    MOZ_ASSERT(mir->offset() <= INT32_MAX);
 
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
     if (accessType == Scalar::Int64)
         storeI64(accessType, ins->getInt64Operand(LWasmStoreI64::ValueIndex), dstAddr);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -243,28 +243,30 @@ MSG_DEF(JSMSG_DEPRECATED_BLOCK_SCOPE_FUN
 MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME,   1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
 MSG_DEF(JSMSG_DUPLICATE_FORMAL,        1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
 MSG_DEF(JSMSG_DUPLICATE_LABEL,         0, JSEXN_SYNTAXERR, "duplicate label")
 MSG_DEF(JSMSG_DUPLICATE_PROPERTY,      1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")
 MSG_DEF(JSMSG_EMPTY_CONSEQUENT,        0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
 MSG_DEF(JSMSG_EQUAL_AS_ASSIGN,         0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
 MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level of a module")
 MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,     0, JSEXN_SYNTAXERR, "finally without try")
+MSG_DEF(JSMSG_FORBIDDEN_AS_STATEMENT,  1, JSEXN_SYNTAXERR, "{0} can't appear in single-statement context")
 MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
 MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR,  0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
 MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT,     2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER,    0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
 MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0, JSEXN_SYNTAXERR, "illegal character")
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
 MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,         0, JSEXN_SYNTAXERR, "label not found")
 MSG_DEF(JSMSG_LET_CLASS_BINDING,       0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class")
 MSG_DEF(JSMSG_LET_COMP_BINDING,        0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
 MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,   1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
 MSG_DEF(JSMSG_LEXICAL_DECL_LABEL,      1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
+MSG_DEF(JSMSG_GENERATOR_LABEL,         0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
 MSG_DEF(JSMSG_FUNCTION_LABEL,          0, JSEXN_SYNTAXERR, "functions cannot be labelled")
 MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL,   0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
 MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW,  0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,        1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
 MSG_DEF(JSMSG_MISSING_BINARY_DIGITS,   0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
 MSG_DEF(JSMSG_MISSING_EXPONENT,        0, JSEXN_SYNTAXERR, "missing exponent")
 MSG_DEF(JSMSG_MISSING_EXPR_AFTER_THROW,0, JSEXN_SYNTAXERR, "throw statement is missing an expression")
 MSG_DEF(JSMSG_MISSING_FORMAL,          0, JSEXN_SYNTAXERR, "missing formal parameter")
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -714,26 +714,29 @@ JSCompartment::ensureRandomNumberGenerat
 {
     if (randomNumberGenerator.isNothing()) {
         mozilla::Array<uint64_t, 2> seed;
         GenerateXorShift128PlusSeed(seed);
         randomNumberGenerator.emplace(seed[0], seed[1]);
     }
 }
 
+double
+js::math_random_impl(JSContext* cx)
+{
+    JSCompartment* comp = cx->compartment();
+    comp->ensureRandomNumberGenerator();
+    return comp->randomNumberGenerator.ref().nextDouble();
+}
+
 bool
 js::math_random(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-
-    JSCompartment* comp = cx->compartment();
-    comp->ensureRandomNumberGenerator();
-
-    double z = comp->randomNumberGenerator.ref().nextDouble();
-    args.rval().setDouble(z);
+    args.rval().setNumber(math_random_impl(cx));
     return true;
 }
 
 bool
 js::math_round_handle(JSContext* cx, HandleValue arg, MutableHandleValue res)
 {
     double d;
     if (!ToNumber(cx, arg, &d))
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -89,16 +89,19 @@ InitMathClass(JSContext* cx, HandleObjec
 // Fill |seed[0]| and |seed[1]| with random bits, suitable for
 // seeding a XorShift128+ random number generator.
 extern void
 GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
 
 extern uint64_t
 random_next(uint64_t* rngState, int bits);
 
+extern double
+math_random_impl(JSContext* cx);
+
 extern bool
 math_random(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
 math_abs_handle(JSContext* cx, js::HandleValue v, js::MutableHandleValue r);
 
 extern bool
 math_abs(JSContext* cx, unsigned argc, js::Value* vp);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2539,16 +2539,23 @@ js::SetPrototype(JSContext* cx, HandleOb
 {
     // The proxy trap subsystem fully handles prototype-setting for proxies
     // with dynamic [[Prototype]]s.
     if (obj->hasDynamicPrototype()) {
         MOZ_ASSERT(obj->is<ProxyObject>());
         return Proxy::setPrototype(cx, obj, proto, result);
     }
 
+    /*
+     * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
+     * Since the values in question are objects, we can just compare pointers.
+     */
+    if (proto == obj->staticPrototype())
+        return result.succeed();
+
     /* Disallow mutation of immutable [[Prototype]]s. */
     if (obj->staticPrototypeIsImmutable() && ImmutablePrototypesEnabled)
         return result.fail(JSMSG_CANT_SET_PROTO);
 
     /*
      * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
      * due to their complicated delegate-object shenanigans can't easily
      * have a mutable [[Prototype]].
@@ -2573,23 +2580,16 @@ js::SetPrototype(JSContext* cx, HandleOb
      * for flash-related security reasons.
      */
     if (!strcmp(obj->getClass()->name, "Location")) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
                              "incompatible Location object");
         return false;
     }
 
-    /*
-     * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
-     * Since the values in question are objects, we can just compare pointers.
-     */
-    if (proto == obj->staticPrototype())
-        return result.succeed();
-
     /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
     bool extensible;
     if (!IsExtensible(cx, obj, &extensible))
         return false;
     if (!extensible)
         return result.fail(JSMSG_CANT_SET_PROTO);
 
     // If this is a global object, resolve the Object class so that its
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/setPrototypeOf-same-value.js
@@ -0,0 +1,11 @@
+// Setting a "new" prototype to the current [[Prototype]] value should never fail
+
+var x = {}, t = Object.create(x);
+Object.preventExtensions(t);
+// Should not fail, because it is the same [[Prototype]] value
+Object.setPrototypeOf(t, x);
+
+// Object.prototype's [[Prototype]] is immutable, make sure we can still set null
+Object.setPrototypeOf(Object.prototype, null);
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Syntax/declaration-forbidden-in-label.js
@@ -0,0 +1,34 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary =
+  "Properly implement the spec's distinctions between StatementListItem and " +
+  "Statement grammar productions and their uses";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+assertThrowsInstanceOf(() => Function("a: let x;"), SyntaxError);
+assertThrowsInstanceOf(() => Function("b: const y = 3;"), SyntaxError);
+assertThrowsInstanceOf(() => Function("c: class z {};"), SyntaxError);
+
+assertThrowsInstanceOf(() => Function("'use strict'; d: function w() {};"), SyntaxError);
+
+// Annex B.3.2 allows this in non-strict mode code.
+Function("e: function x() {};");
+
+assertThrowsInstanceOf(() => Function("f: function* y() {}"), SyntaxError);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Syntax/let-as-label.js
@@ -0,0 +1,29 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary = "let can't be used as a label in strict mode code";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+Function("let: 42")
+assertThrowsInstanceOf(() => Function(" 'use strict'; let: 42"), SyntaxError);
+assertThrowsInstanceOf(() => Function(" 'use strict' \n let: 42"), SyntaxError);
+
+eval("let: 42")
+assertThrowsInstanceOf(() => eval(" 'use strict'; let: 42"), SyntaxError);
+assertThrowsInstanceOf(() => eval(" 'use strict' \n let: 42;"), SyntaxError);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Syntax/statement-versus-statementlistitem.js
@@ -0,0 +1,148 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs evaluate, parseModule
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary =
+  "Properly implement the spec's distinctions between StatementListItem and " +
+  "Statement grammar productions and their uses";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var counter = 0;
+
+// Test all the different contexts that expect a Statement.
+
+function contextAcceptsStatement(pat)
+{
+  var goodCode =
+`function f${counter++}()
+{
+  ${pat.replace("@@@", "let \n {} \n ;")}
+}
+`;
+
+  evaluate(goodCode);
+
+  var badCode =
+`function f${counter++}()
+{
+  ${pat.replace("@@@", "let {} \n ;")}
+}
+`;
+
+  try
+  {
+    evaluate(badCode);
+    throw new Error("didn't throw");
+  }
+  catch (e)
+  {
+    assertEq(e instanceof SyntaxError, true,
+             "didn't get a syntax error, instead got: " + e);
+  }
+}
+
+contextAcceptsStatement("if (0) @@@");
+contextAcceptsStatement("if (0) ; else @@@");
+contextAcceptsStatement("if (0) ; else if (0) @@@");
+contextAcceptsStatement("if (0) ; else if (0) ; else @@@");
+// Can't test do-while loops this way because the Statement isn't in trailing
+// position, so 'let' followed by newline *must* be followed by 'while'.
+contextAcceptsStatement("while (1) @@@");
+contextAcceptsStatement("for (1; 1; 0) @@@");
+contextAcceptsStatement("for (var x; 1; 0) @@@");
+contextAcceptsStatement("for (let x; 1; 0) @@@");
+contextAcceptsStatement("for (const x = 1; 1; 0) @@@");
+contextAcceptsStatement("for (x in 0) @@@");
+contextAcceptsStatement("for (var x in 0) @@@");
+contextAcceptsStatement("for (let x in 0) @@@");
+contextAcceptsStatement("for (const x in 0) @@@");
+contextAcceptsStatement("for (x of []) @@@");
+contextAcceptsStatement("for (var x of []) @@@");
+contextAcceptsStatement("for (let x of []) @@@");
+contextAcceptsStatement("for (const x of []) @@@");
+contextAcceptsStatement("with (1) @@@");
+contextAcceptsStatement("foo: @@@");
+
+// Test all the different contexts that expect a StatementListItem.
+
+function contextAcceptsStatementListItem(pat)
+{
+  var goodCode =
+`function f${counter++}()
+{
+  ${pat.replace("@@@", "let \n [] = [] ;")}
+}
+`;
+
+  evaluate(goodCode);
+
+  var moarGoodCode = pat.replace("@@@", "let \n [] = [] ;");
+  evaluate(moarGoodCode);
+
+  var goodModuleCode = pat.replace("@@@", "let \n [] = [] ;");
+  parseModule(goodModuleCode);
+
+  var badCode =
+`function f${counter++}()
+{
+  ${pat.replace("@@@", "let \n let = 3 ;")}
+}
+`;
+
+  try
+  {
+    evaluate(badCode);
+    throw new Error("didn't throw");
+  }
+  catch (e)
+  {
+    assertEq(e instanceof SyntaxError, true,
+             "didn't get a syntax error, instead got: " + e);
+  }
+}
+
+contextAcceptsStatementListItem("{ @@@ }");
+contextAcceptsStatementListItem("switch (1) { case 1: @@@ }");
+contextAcceptsStatementListItem("switch (1) { default: @@@ }");
+contextAcceptsStatementListItem("switch (1) { case 0: case 1: @@@ }");
+contextAcceptsStatementListItem("switch (1) { case 0: break; case 1: @@@ }");
+contextAcceptsStatementListItem("switch (1) { default: @@@ case 2: }");
+contextAcceptsStatementListItem("try { @@@ } catch (e) { }");
+contextAcceptsStatementListItem("try { @@@ } finally { }");
+contextAcceptsStatementListItem("try { @@@ } catch (e) { } finally { }");
+contextAcceptsStatementListItem("try { } catch (e) { @@@ }");
+contextAcceptsStatementListItem("try { } finally { @@@ }");
+contextAcceptsStatementListItem("try { } catch (e) { @@@ } finally { }");
+contextAcceptsStatementListItem("try { } catch (e) { } finally { @@@ }");
+contextAcceptsStatementListItem("_ => { @@@ }");
+contextAcceptsStatementListItem("function q() { @@@ }");
+contextAcceptsStatementListItem("function* q() { @@@ }");
+contextAcceptsStatementListItem("(function() { @@@ })");
+contextAcceptsStatementListItem("(function*() { @@@ })");
+contextAcceptsStatementListItem("({ *q() { @@@ } })");
+contextAcceptsStatementListItem("({ q() { @@@ } })");
+contextAcceptsStatementListItem("({ get q() { @@@ } })");
+contextAcceptsStatementListItem("({ set q(v) { @@@ } })");
+contextAcceptsStatementListItem("(class { f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static *f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static get f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static set f(v) { @@@ } })");
+contextAcceptsStatementListItem("(class { static() { @@@ } })");
+contextAcceptsStatementListItem("@@@");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Syntax/yield-as-identifier.js
@@ -0,0 +1,54 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary = "|yield| is sometimes a valid identifier";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function t(code)
+{
+  var strictSemi = " 'use strict'; " + code;
+  var strictASI = " 'use strict' \n " + code;
+
+  var creationFunctions = ["Function"];
+  if (typeof evaluate === "function")
+    creationFunctions.push("evaluate");
+  if (typeof parseModule === "function")
+    creationFunctions.push("parseModule");
+
+  for (var func of creationFunctions)
+  {
+    var g = newGlobal();
+    var f = g[func];
+
+    if (func === "parseModule")
+      assertThrowsInstanceOf(() => f(code), g.SyntaxError);
+    else
+      f(code);
+
+    assertThrowsInstanceOf(() => f(strictSemi), g.SyntaxError);
+    assertThrowsInstanceOf(() => f(strictASI), g.SyntaxError);
+  }
+}
+
+t("var yield = 3;");
+t("let yield = 3;");
+t("const yield = 3;");
+t("for (var yield = 3; ; ) break;");
+t("for (let yield = 3; ; ) break;");
+t("for (const yield = 3; ; ) break;");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2241,17 +2241,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("std_String_toLocaleLowerCase",        str_toLocaleLowerCase,        0,0),
     JS_FN("std_String_toLocaleUpperCase",        str_toLocaleUpperCase,        0,0),
 #if !EXPOSE_INTL_API
     JS_FN("std_String_localeCompare",            str_localeCompare,            1,0),
 #else
     JS_FN("std_String_normalize",                str_normalize,                0,0),
 #endif
     JS_FN("std_String_concat",                   str_concat,                   1,0),
-
+    
+    JS_FN("std_TypedArray_buffer",               js::TypedArray_bufferGetter,  1,0),
 
     JS_FN("std_WeakMap_has",                     WeakMap_has,                  1,0),
     JS_FN("std_WeakMap_get",                     WeakMap_get,                  2,0),
     JS_FN("std_WeakMap_set",                     WeakMap_set,                  2,0),
     JS_FN("std_WeakMap_delete",                  WeakMap_delete,               1,0),
 
     JS_FN("std_SIMD_Int8x16_extractLane",        simd_int8x16_extractLane,     2,0),
     JS_FN("std_SIMD_Int16x8_extractLane",        simd_int16x8_extractLane,     2,0),
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1321,18 +1321,18 @@ BufferGetterImpl(JSContext* cx, const Ca
     MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
     Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
     if (!TypedArrayObject::ensureHasBuffer(cx, tarray))
         return false;
     args.rval().set(TypedArrayObject::bufferValue(tarray));
     return true;
 }
 
-static bool
-TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp)
+/*static*/ bool
+js::TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<TypedArrayObject::is, BufferGetterImpl>(cx, args);
 }
 
 /* static */ const JSPropertySpec
 TypedArrayObject::protoAccessors[] = {
     JS_PSG("length", TypedArray_lengthGetter, 0),
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -289,16 +289,18 @@ class TypedArrayObject : public NativeOb
 
     /* Accessors and functions */
 
     static bool is(HandleValue v);
 
     static bool set(JSContext* cx, unsigned argc, Value* vp);
 };
 
+MOZ_MUST_USE bool TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp);
+
 extern TypedArrayObject*
 TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj, int32_t len);
 
 inline bool
 IsTypedArrayClass(const Class* clasp)
 {
     return &TypedArrayObject::classes[0] <= clasp &&
            clasp < &TypedArrayObject::classes[Scalar::MaxTypedArrayViewType];
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -418,17 +418,16 @@ public:
     mAnimatedGeometryRoot(nullptr),
     mScrollClip(nullptr),
     mReferenceFrame(nullptr),
     mLayer(nullptr),
     mSolidColor(NS_RGBA(0, 0, 0, 0)),
     mIsSolidColorInVisibleRegion(false),
     mFontSmoothingBackgroundColor(NS_RGBA(0,0,0,0)),
     mSingleItemFixedToViewport(false),
-    mIsCaret(false),
     mNeedComponentAlpha(false),
     mForceTransparentSurface(false),
     mHideAllLayersBelow(false),
     mOpaqueForAnimatedGeometryRootParent(false),
     mDisableFlattening(false),
     mBackfaceHidden(false),
     mImage(nullptr),
     mCommonClipCount(-1),
@@ -582,20 +581,16 @@ public:
    */
   nscolor mFontSmoothingBackgroundColor;
   /**
    * True if the layer contains exactly one item that returned true for
    * ShouldFixToViewport.
    */
   bool mSingleItemFixedToViewport;
   /**
-   * True if the layer contains exactly one item for the caret.
-   */
-  bool mIsCaret;
-  /**
    * True if there is any text visible in the layer that's over
    * transparent pixels in the layer.
    */
   bool mNeedComponentAlpha;
   /**
    * Set if the layer should be treated as transparent, even if its entire
    * area is covered by opaque display items. For example, this needs to
    * be set if something is going to "punch holes" in the layer by clearing
@@ -678,18 +673,16 @@ struct NewLayerEntry {
     : mAnimatedGeometryRoot(nullptr)
     , mScrollClip(nullptr)
     , mLayerContentsVisibleRect(0, 0, -1, -1)
     , mLayerState(LAYER_INACTIVE)
     , mHideAllLayersBelow(false)
     , mOpaqueForAnimatedGeometryRootParent(false)
     , mPropagateComponentAlphaFlattening(true)
     , mUntransformedVisibleRegion(false)
-    , mIsCaret(false)
-    , mIsPerspectiveItem(false)
   {}
   // mLayer is null if the previous entry is for a PaintedLayer that hasn't
   // been optimized to some other form (yet).
   RefPtr<Layer> mLayer;
   AnimatedGeometryRoot* mAnimatedGeometryRoot;
   const DisplayItemScrollClip* mScrollClip;
   // If non-null, this ScrollMetadata is set to the be the first ScrollMetadata
   // on the layer.
@@ -715,18 +708,16 @@ struct NewLayerEntry {
   bool mOpaqueForAnimatedGeometryRootParent;
 
   // If true, then the content flags for this layer should contribute
   // to our decision to flatten component alpha layers, false otherwise.
   bool mPropagateComponentAlphaFlattening;
   // mVisibleRegion is relative to the associated frame before
   // transform.
   bool mUntransformedVisibleRegion;
-  bool mIsCaret;
-  bool mIsPerspectiveItem;
 };
 
 class PaintedLayerDataTree;
 
 /**
  * This is tree node type for PaintedLayerDataTree.
  * Each node corresponds to a different animated geometry root, and contains
  * a stack of PaintedLayerDatas, in bottom-to-top order.
@@ -3082,17 +3073,16 @@ void ContainerState::FinishPaintedLayerD
       NS_ASSERTION(newLayerEntry->mLayer == data->mLayer,
                    "Painted layer at wrong index");
       // Store optimized layer in reserved slot
       newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex + 1];
       NS_ASSERTION(!newLayerEntry->mLayer, "Slot already occupied?");
       newLayerEntry->mLayer = layer;
       newLayerEntry->mAnimatedGeometryRoot = data->mAnimatedGeometryRoot;
       newLayerEntry->mScrollClip = data->mScrollClip;
-      newLayerEntry->mIsCaret = data->mIsCaret;
 
       // Hide the PaintedLayer. We leave it in the layer tree so that we
       // can find and recycle it later.
       ParentLayerIntRect emptyRect;
       data->mLayer->SetClipRect(Some(emptyRect));
       data->mLayer->SetVisibleRegion(LayerIntRegion());
       data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion().GetBounds());
       data->mLayer->SetEventRegions(EventRegions());
@@ -3531,23 +3521,21 @@ ContainerState::NewPaintedLayerData(nsDi
 {
   PaintedLayerData data;
   data.mAnimatedGeometryRoot = aAnimatedGeometryRoot;
   data.mScrollClip = aScrollClip;
   data.mAnimatedGeometryRootOffset = aTopLeft;
   data.mReferenceFrame = aItem->ReferenceFrame();
   data.mSingleItemFixedToViewport = aShouldFixToViewport;
   data.mBackfaceHidden = aItem->Frame()->In3DContextAndBackfaceIsHidden();
-  data.mIsCaret = aItem->GetType() == nsDisplayItem::TYPE_CARET;
 
   data.mNewChildLayersIndex = mNewChildLayers.Length();
   NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
   newLayerEntry->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
   newLayerEntry->mScrollClip = aScrollClip;
-  newLayerEntry->mIsCaret = data.mIsCaret;
   // newLayerEntry->mOpaqueRegion is filled in later from
   // paintedLayerData->mOpaqueRegion, if necessary.
 
   // Allocate another entry for this layer's optimization to ColorLayer/ImageLayer
   mNewChildLayers.AppendElement();
 
   return data;
 }
@@ -4160,27 +4148,23 @@ ContainerState::ProcessDisplayItems(nsDi
       NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, ownLayer) < 0,
                    "Layer already in list???");
 
       NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
       newLayerEntry->mLayer = ownLayer;
       newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
       newLayerEntry->mScrollClip = agrScrollClip;
       newLayerEntry->mLayerState = layerState;
-      if (itemType == nsDisplayItem::TYPE_PERSPECTIVE) {
-        newLayerEntry->mIsPerspectiveItem = true;
-      }
 
       // Don't attempt to flatten compnent alpha layers that are within
       // a forced active layer, or an active transform;
       if (itemType == nsDisplayItem::TYPE_TRANSFORM ||
           layerState == LAYER_ACTIVE_FORCE) {
         newLayerEntry->mPropagateComponentAlphaFlattening = false;
       }
-      newLayerEntry->mIsCaret = itemType == nsDisplayItem::TYPE_CARET;
       // nsDisplayTransform::BuildLayer must set layerContentsVisibleRect.
       // We rely on this to ensure 3D transforms compute a reasonable
       // layer visible region.
       NS_ASSERTION(itemType != nsDisplayItem::TYPE_TRANSFORM ||
                    layerContentsVisibleRect.width >= 0,
                    "Transform items must set layerContentsVisibleRect!");
       if (mLayerBuilder->IsBuildingRetainedLayers()) {
         newLayerEntry->mLayerContentsVisibleRect = layerContentsVisibleRect;
--- a/layout/base/RestyleManagerBase.h
+++ b/layout/base/RestyleManagerBase.h
@@ -4,23 +4,31 @@
  * 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_RestyleManagerBase_h
 #define mozilla_RestyleManagerBase_h
 
 #include "mozilla/OverflowChangedTracker.h"
 #include "nsChangeHint.h"
+#include "nsPresContext.h"
 
+class nsCString;
+class nsCSSFrameConstructor;
 class nsStyleChangeList;
 
 namespace mozilla {
 
+class EventStates;
+class RestyleManager;
 class ServoRestyleManager;
-class RestyleManager;
+
+namespace dom {
+class Element;
+}
 
 /**
  * Class for sharing data and logic common to both RestyleManager and
  * ServoRestyleManager.
  */
 class RestyleManagerBase
 {
 protected:
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "nsContentUtils.h"
+#include "nsPrintfCString.h"
 #include "nsStyleChangeList.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
   : RestyleManagerBase(aPresContext)
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -126,16 +126,21 @@ enum nsChangeHint {
    */
   nsChangeHint_RecomputePosition = 1 << 16,
 
   /**
    * Behaves like ReconstructFrame, but only if the frame has descendants
    * that are absolutely or fixed position. Use this hint when a style change
    * has changed whether the frame is a container for fixed-pos or abs-pos
    * elements, but reframing is otherwise not needed.
+   *
+   * Note that nsStyleContext::CalcStyleDifference adjusts results
+   * returned by style struct CalcDifference methods to return this hint
+   * only if there was a change to whether the element's overall style
+   * indicates that it establishes a containing block.
    */
   nsChangeHint_UpdateContainingBlock = 1 << 17,
 
   /**
    * This change hint has *no* change handling behavior.  However, it
    * exists to be a non-inherited hint, because when the border-style
    * changes, and it's inherited by a child, that might require a reflow
    * due to the border-width change on the child.
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1643,17 +1643,17 @@ nsDisplayList::ComputeVisibilityForSubli
   bool anyVisible = false;
 
   AutoTArray<nsDisplayItem*, 512> elements;
   MoveListTo(this, &elements);
 
   for (int32_t i = elements.Length() - 1; i >= 0; --i) {
     nsDisplayItem* item = elements[i];
 
-    if (item->mForceNotVisible) {
+    if (item->mForceNotVisible && !item->GetSameCoordinateSystemChildren()) {
       NS_ASSERTION(item->mVisibleRect.IsEmpty(),
         "invisible items should have empty vis rect");
     } else {
       nsRect bounds = item->GetClippedBounds(aBuilder);
 
       nsRegion itemVisible;
       itemVisible.And(*aVisibleRegion, bounds);
       item->mVisibleRect = itemVisible.GetBounds();
@@ -1789,17 +1789,17 @@ already_AddRefed<LayerManager> nsDisplay
   if (computeInvalidRect) {
     props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
   }
 
   // Clear any ScrollMetadata that may have been set on the root layer on a
   // previous paint. This paint will set new metrics if necessary, and if we
   // don't clear the old one here, we may be left with extra metrics.
   if (Layer* root = layerManager->GetRoot()) {
-      root->SetScrollMetadata(nsTArray<ScrollMetadata>());
+    root->SetScrollMetadata(nsTArray<ScrollMetadata>());
   }
 
   ContainerLayerParameters containerParameters
     (presShell->GetResolution(), presShell->GetResolution());
   RefPtr<ContainerLayer> root = layerBuilder->
     BuildContainerLayerFor(aBuilder, layerManager, frame, nullptr, this,
                            containerParameters, nullptr);
 
@@ -2314,19 +2314,22 @@ nsDisplayItem::ComputeVisibility(nsDispl
 {
   return !mVisibleRect.IsEmpty() &&
     !IsInvisibleInRect(aVisibleRegion->GetBounds());
 }
 
 bool
 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion) {
-  if (mForceNotVisible) {
+  if (mForceNotVisible && !GetSameCoordinateSystemChildren()) {
+    // mForceNotVisible wants to ensure that this display item doesn't render
+    // anything itself. If this item has contents, then we obviously want to
+    // render those, so we don't need this check in that case.
     NS_ASSERTION(mVisibleRect.IsEmpty(),
-      "invisible items should have empty vis rect");
+      "invisible items without children should have empty vis rect");
   } else {
     nsRect bounds = GetClippedBounds(aBuilder);
 
     nsRegion itemVisible;
     itemVisible.And(*aVisibleRegion, bounds);
     mVisibleRect = itemVisible.GetBounds();
   }
 
@@ -5083,18 +5086,18 @@ already_AddRefed<Layer>
 nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aContainerParameters) {
   RefPtr<Layer> layer =
     nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
 
   layer->SetIsFixedPosition(true);
 
-  nsPresContext* presContext = Frame()->PresContext();
-  nsIFrame* fixedFrame = mIsFixedBackground ? presContext->PresShell()->GetRootFrame() : Frame();
+  nsPresContext* presContext = mFrame->PresContext();
+  nsIFrame* fixedFrame = mIsFixedBackground ? presContext->PresShell()->GetRootFrame() : mFrame;
 
   const nsIFrame* viewportFrame = fixedFrame->GetParent();
   // anchorRect will be in the container's coordinate system (aLayer's parent layer).
   // This is the same as the display items' reference frame.
   nsRect anchorRect;
   if (viewportFrame) {
     // Fixed position frames are reflowed into the scroll-port size if one has
     // been set.
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -12,16 +12,17 @@
 
 #include "nsAttrValue.h"
 #include "nsFrame.h"
 #include "nsDisplayList.h"
 #include "FrameLayerBuilder.h"
 #include "nsPrintfCString.h"
 #include "DisplayItemScrollClip.h"
 
+#include <iostream>
 #include <stdio.h>
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
 #ifdef DEBUG
 class nsLayoutDebugger : public nsILayoutDebugger {
 public:
@@ -243,16 +244,35 @@ void
 nsFrame::PrintDisplayList(nsDisplayListBuilder* aBuilder,
                           const nsDisplayList& aList,
                           std::stringstream& aStream,
                           bool aDumpHtml)
 {
   PrintDisplayListTo(aBuilder, aList, aStream, 0, aDumpHtml);
 }
 
+/**
+ * The two functions below are intended to be called from a debugger.
+ */
+void
+PrintDisplayItemToStdout(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
+{
+  std::stringstream stream;
+  PrintDisplayItemTo(aBuilder, aItem, stream, 0, true, false);
+  std::cout << stream.str() << std::endl;
+}
+
+void
+PrintDisplayListToStdout(nsDisplayListBuilder* aBuilder, const nsDisplayList& aList)
+{
+  std::stringstream stream;
+  PrintDisplayListTo(aBuilder, aList, stream, 0, false);
+  std::cout << stream.str() << std::endl;
+}
+
 #ifdef MOZ_DUMP_PAINTING
 static void
 PrintDisplayListSetItem(nsDisplayListBuilder* aBuilder,
                         const char* aItemName,
                         const nsDisplayList& aList,
                         std::stringstream& aStream,
                         bool aDumpHtml)
 {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1180,19 +1180,18 @@ nsIFrame::Extend3DContext() const
     return false;
   }
 
   if (HasOpacity()) {
     return false;
   }
 
   const nsStyleEffects* effects = StyleEffects();
-  nsRect temp;
   return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
-         !GetClipPropClipRect(disp, effects, &temp, GetSize()) &&
+         !GetClipPropClipRect(disp, effects, GetSize()) &&
          !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
 }
 
 bool
 nsIFrame::Combines3DTransformWithAncestors() const
 {
   if (!GetParent() || !GetParent()->Extend3DContext()) {
     return false;
@@ -1930,70 +1929,45 @@ inline static bool IsSVGContentWithCSSCl
   // elements regardless of the value of the 'position' property. Here we obey
   // the CSS spec for outer-<svg> (since that's what we generally do), but
   // obey the SVG spec for other SVG elements to which 'clip' applies.
   return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
           aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
                                                    nsGkAtoms::foreignObject);
 }
 
-bool
+Maybe<nsRect>
 nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
                               const nsStyleEffects* aEffects,
-                              nsRect* aRect,
                               const nsSize& aSize) const
 {
-  NS_PRECONDITION(aRect, "Must have aRect out parameter");
-
   if (!(aEffects->mClipFlags & NS_STYLE_CLIP_RECT) ||
       !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
-    return false;
-  }
-
-  *aRect = aEffects->mClip;
+    return Nothing();
+  }
+
+  nsRect rect = aEffects->mClip;
   if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
                    StyleBoxDecorationBreak::Slice)) {
     // The clip applies to the joined boxes so it's relative the first
     // continuation.
     nscoord y = 0;
     for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
       y += f->GetRect().height;
     }
-    aRect->MoveBy(nsPoint(0, -y));
+    rect.MoveBy(nsPoint(0, -y));
   }
 
   if (NS_STYLE_CLIP_RIGHT_AUTO & aEffects->mClipFlags) {
-    aRect->width = aSize.width - aRect->x;
+    rect.width = aSize.width - rect.x;
   }
   if (NS_STYLE_CLIP_BOTTOM_AUTO & aEffects->mClipFlags) {
-    aRect->height = aSize.height - aRect->y;
-  }
-  return true;
-}
-
-/**
- * If the CSS 'clip' property applies to this frame, set it up
- * in aBuilder->ClipState() to clip all content descendants. Returns true
- * if the property applies, and if so also returns the clip rect (relative
- * to aFrame) in *aRect.
- */
-static bool
-ApplyClipPropClipping(nsDisplayListBuilder* aBuilder,
-                      const nsIFrame* aFrame,
-                      const nsStyleDisplay* aDisp,
-                      const nsStyleEffects* aEffects,
-                      nsRect* aRect,
-                      DisplayListClipState::AutoSaveRestore& aClipState)
-{
-  if (!aFrame->GetClipPropClipRect(aDisp, aEffects, aRect, aFrame->GetSize()))
-    return false;
-
-  nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame);
-  aClipState.ClipContentDescendants(clipRect);
-  return true;
+    rect.height = aSize.height - rect.y;
+  }
+  return Some(rect);
 }
 
 /**
  * If the CSS 'overflow' property applies to this frame, and is not
  * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
  * for that overflow in aBuilder->ClipState() to clip all containing-block
  * descendants.
  */
@@ -2186,47 +2160,49 @@ nsIFrame::BuildDisplayListForStackingCon
       return;
     }
   }
 
   if (disp->mWillChangeBitField != 0) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
+  bool extend3DContext = Extend3DContext();
   Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
-  if (Extend3DContext() && !Combines3DTransformWithAncestors()) {
+  if (extend3DContext && !Combines3DTransformWithAncestors()) {
     // Start a new preserves3d context to keep informations on
     // nsDisplayListBuilder.
     autoPreserves3DContext.emplace(aBuilder);
     // Save dirty rect on the builder to avoid being distorted for
     // multiple transforms along the chain.
     aBuilder->SetPreserves3DDirtyRect(aDirtyRect);
   }
 
   // For preserves3d, use the dirty rect already installed on the
   // builder, since aDirtyRect maybe distorted for transforms along
   // the chain.
   nsRect dirtyRect = aDirtyRect;
 
   bool inTransform = aBuilder->IsInTransform();
   bool isTransformed = IsTransformed();
+  bool hasPerspective = HasPerspective();
   // reset blend mode so we can keep track if this stacking context needs have
   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
   // so we keep track if the parent stacking context needs a container too.
   AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
   aBuilder->SetContainsBlendMode(false);
  
   nsRect dirtyRectOutsideTransform = dirtyRect;
   if (isTransformed) {
     const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
     if (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder,
                                                               this)) {
       dirtyRect = overflow;
     } else {
-      if (overflow.IsEmpty() && !Extend3DContext()) {
+      if (overflow.IsEmpty() && !extend3DContext) {
         return;
       }
 
       // If we're in preserve-3d then grab the dirty rect that was given to the root
       // and transform using the combined transform.
       if (Combines3DTransformWithAncestors()) {
         dirtyRect = aBuilder->GetPreserves3DDirtyRect(this);
       }
@@ -2264,20 +2240,69 @@ nsIFrame::BuildDisplayListForStackingCon
                         nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
                         nsLayoutUtils::SCROLLABLE_SAME_DOC |
                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
   bool useFixedPosition = nsLayoutUtils::IsFixedPosFrameInDisplayPort(this);
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingDisplayList(aBuilder, this, dirtyRect, true);
 
+  // Depending on the effects that are applied to this frame, we can create
+  // multiple container display items and wrap them around our contents.
+  // This enum lists all the potential container display items, in the order
+  // outside to inside.
+  enum class ContainerItemType : uint8_t {
+    eNone = 0,
+    eOwnLayerIfNeeded,
+    eBlendMode,
+    eFixedPosition,
+    eStickyPosition,
+    eOwnLayerForTransformWithRoundedClip,
+    ePerspective,
+    eTransform,
+    eSeparatorTransforms,
+    eOpacity,
+    eSVGEffects,
+    eBlendContainer
+  };
+
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
+  // If there is a current clip, then depending on the container items we
+  // create, different things can happen to it. Some container items simply
+  // propagate the clip to their children and aren't clipped themselves.
+  // But other container items, especially those that establish a different
+  // geometry for their contents (e.g. transforms), capture the clip on
+  // themselves and unset the clip for their contents. If we create more than
+  // one of those container items, the clip will be captured on the outermost
+  // one and the inner container items will be unclipped.
+  ContainerItemType clipCapturedBy = ContainerItemType::eNone;
+  if (useFixedPosition) {
+    clipCapturedBy = ContainerItemType::eFixedPosition;
+  } else if (useStickyPosition) {
+    clipCapturedBy = ContainerItemType::eStickyPosition;
+  } else if (isTransformed) {
+    if ((hasPerspective || extend3DContext) && clipState.SavedStateHasRoundedCorners()) {
+      // If we're creating an nsDisplayTransform item that is going to combine
+      // its transform with its children (preserve-3d or perspective), then we
+      // can't have an intermediate surface. Mask layers force an intermediate
+      // surface, so if we're going to need both then create a separate
+      // wrapping layer for the mask.
+      clipCapturedBy = ContainerItemType::eOwnLayerForTransformWithRoundedClip;
+    } else if (hasPerspective) {
+      clipCapturedBy = ContainerItemType::ePerspective;
+    } else {
+      clipCapturedBy = ContainerItemType::eTransform;
+    }
+  } else if (usingSVGEffects) {
+    clipCapturedBy = ContainerItemType::eSVGEffects;
+  }
+
   bool clearClip = false;
-  if (isTransformed || usingSVGEffects || useFixedPosition || useStickyPosition) {
+  if (clipCapturedBy != ContainerItemType::eNone) {
     // We don't need to pass ancestor clipping down to our children;
     // everything goes inside a display item's child list, and the display
     // item itself will be clipped.
     // For transforms we also need to clear ancestor clipping because it's
     // relative to the wrong display item reference frame anyway.
     clearClip = true;
   }
 
@@ -2288,25 +2313,26 @@ nsIFrame::BuildDisplayListForStackingCon
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
     nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex
       perspectiveIndex(aBuilder, this);
 
     CheckForApzAwareEventHandlers(aBuilder, this);
 
-    nsRect clipPropClip;
-    if (ApplyClipPropClipping(aBuilder, this, disp, effects, &clipPropClip,
-                              nestedClipState)) {
-      dirtyRect.IntersectRect(dirtyRect, clipPropClip);
-    }
-
-    // Extend3DContext() also guarantees that applyAbsPosClipping and usingSVGEffects are false
+    Maybe<nsRect> clipPropClip = GetClipPropClipRect(disp, effects, GetSize());
+    if (clipPropClip) {
+      dirtyRect.IntersectRect(dirtyRect, *clipPropClip);
+      nestedClipState.ClipContentDescendants(
+        *clipPropClip + aBuilder->ToReferenceFrame(this));
+    }
+
+    // extend3DContext also guarantees that applyAbsPosClipping and usingSVGEffects are false
     // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
-    if (Extend3DContext()) {
+    if (extend3DContext) {
       // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
       // going to be forced to descend into frames.
       aBuilder->MarkPreserve3DFramesForDisplayList(this);
     }
 
     MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
 
     nsDisplayLayerEventRegions* eventRegions = nullptr;
@@ -2404,28 +2430,25 @@ nsIFrame::BuildDisplayListForStackingCon
   if (aBuilder->ContainsBlendMode()) {
     DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
     blendContainerClipState.Clear();
     resultList.AppendNewToTop(
       nsDisplayBlendContainer::CreateForMixBlendMode(aBuilder, this, &resultList,
                                                      containerItemScrollClip));
   }
 
-  if (!isTransformed && !useFixedPosition && !useStickyPosition) {
-    // Restore saved clip state now so that any display items we create below
-    // are clipped properly.
-    clipState.ExitStackingContextContents(&containerItemScrollClip);
-  }
-
   /* If there are any SVG effects, wrap the list up in an SVG effects item
    * (which also handles CSS group opacity). Note that we create an SVG effects
    * item even if resultList is empty, since a filter can produce graphical
    * output even if the element being filtered wouldn't otherwise do so.
    */
   if (usingSVGEffects) {
+    if (clipCapturedBy == ContainerItemType::eSVGEffects) {
+      clipState.ExitStackingContextContents(&containerItemScrollClip);
+    }
     // Revert to the post-filter dirty rect.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects);
     /* List now emptied, so add the new list to the top. */
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList, useOpacity));
     // Also add the hoisted scroll info items. We need those for APZ scrolling
     // because nsDisplaySVGEffects items can't build active layers.
     aBuilder->ExitSVGEffectsContents();
@@ -2452,17 +2475,17 @@ nsIFrame::BuildDisplayListForStackingCon
    *
    * For the preserve-3d case we want to individually wrap every child in the list with
    * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
    * we can skip this step, as the computed transform will already include our own.
    *
    * We also traverse into sublists created by nsDisplayWrapList, so that we find all the
    * correct children.
    */
-  if (isTransformed && !resultList.IsEmpty() && Extend3DContext()) {
+  if (isTransformed && !resultList.IsEmpty() && extend3DContext) {
     // Install dummy nsDisplayTransform as a leaf containing
     // descendants not participating this 3D rendering context.
     nsDisplayList nonparticipants;
     nsDisplayList participants;
     int index = 1;
 
     while (nsDisplayItem* item = resultList.RemoveBottom()) {
       if (ItemParticipatesIn3DContext(this, item) && !item->GetClip().HasClip()) {
@@ -2481,29 +2504,19 @@ nsIFrame::BuildDisplayListForStackingCon
         nonparticipants.AppendToTop(item);
       }
     }
     WrapSeparatorTransform(aBuilder, this, dirtyRect,
                            &nonparticipants, &participants, index++);
     resultList.AppendToTop(&participants);
   }
 
-  /* If we're creating an nsDisplayTransform item that is going to combine its transform
-   * with its children (preserve-3d or perspective), then we can't have an intermediate
-   * surface. Mask layers force an intermediate surface, so if we're going to need both
-   * then create a separate wrapping layer for the mask.
-   */
-  bool needsLayerForMask = isTransformed && (Extend3DContext() || HasPerspective()) &&
-                           clipState.SavedStateHasRoundedCorners();
-  NS_ASSERTION(!Combines3DTransformWithAncestors() || !clipState.SavedStateHasRoundedCorners(),
-               "Can't support mask layers on intermediate preserve-3d frames");
-
   if (isTransformed && !resultList.IsEmpty()) {
-    // Restore clip state now so nsDisplayTransform is clipped properly.
-    if (!HasPerspective() && !useFixedPosition && !useStickyPosition && !needsLayerForMask) {
+    if (clipCapturedBy == ContainerItemType::eTransform) {
+      // Restore clip state now so nsDisplayTransform is clipped properly.
       clipState.ExitStackingContextContents(&containerItemScrollClip);
     }
     // Revert to the dirtyrect coming in from the parent, without our transform
     // taken into account.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
     // Revert to the outer reference frame and offset because all display
     // items we create from now on are outside the transform.
     nsPoint toOuterReferenceFrame;
@@ -2521,44 +2534,47 @@ nsIFrame::BuildDisplayListForStackingCon
         dirtyRectOutsideSVGEffects.Contains(GetVisualOverflowRectRelativeToSelf());
       nsDisplayTransform *transformItem =
         new (aBuilder) nsDisplayTransform(aBuilder, this,
                                           &resultList, dirtyRect, 0,
                                           isFullyVisible);
       resultList.AppendNewToTop(transformItem);
     }
 
-    if (HasPerspective()) {
-      if (!useFixedPosition && !useStickyPosition && !needsLayerForMask) {
+    if (hasPerspective) {
+      if (clipCapturedBy == ContainerItemType::ePerspective) {
         clipState.ExitStackingContextContents(&containerItemScrollClip);
       }
       resultList.AppendNewToTop(
         new (aBuilder) nsDisplayPerspective(
           aBuilder, this,
           GetContainingBlock()->GetContent()->GetPrimaryFrame(), &resultList));
     }
   }
 
-  if (useFixedPosition || useStickyPosition || needsLayerForMask) {
+  if (clipCapturedBy == ContainerItemType::eOwnLayerForTransformWithRoundedClip) {
     clipState.ExitStackingContextContents(&containerItemScrollClip);
-  }
-
-  if (needsLayerForMask) {
     resultList.AppendNewToTop(
       new (aBuilder) nsDisplayOwnLayer(aBuilder, this, &resultList, 0,
                                        mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
                                        0.0f, /* aForceActive = */ false));
   }
 
   /* If we have sticky positioning, wrap it in a sticky position item.
    */
   if (useFixedPosition) {
+    if (clipCapturedBy == ContainerItemType::eFixedPosition) {
+      clipState.ExitStackingContextContents(&containerItemScrollClip);
+    }
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayFixedPosition(aBuilder, this, &resultList));
   } else if (useStickyPosition) {
+    if (clipCapturedBy == ContainerItemType::eStickyPosition) {
+      clipState.ExitStackingContextContents(&containerItemScrollClip);
+    }
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList));
   }
 
   /* If there's blending, wrap up the list in a blend-mode item. Note
    * that opacity can be applied before blending as the blend color is
    * not affected by foreground opacity (only background alpha).
    */
@@ -2821,22 +2837,22 @@ nsIFrame::BuildDisplayListForChild(nsDis
       aBuilder->SetContainsBlendMode(true);
     }
     // True stacking context.
     // For stacking contexts, BuildDisplayListForStackingContext handles
     // clipping and MarkAbsoluteFramesForDisplayList.
     child->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
     aBuilder->DisplayCaret(child, dirty, &list);
   } else {
-    nsRect clipRect;
-    if (ApplyClipPropClipping(aBuilder, child, disp, effects, &clipRect,
-                              clipState)) {
-      // clipRect is in builder-reference-frame coordinates,
-      // dirty/clippedDirtyRect are in child coordinates
-      dirty.IntersectRect(dirty, clipRect);
+    Maybe<nsRect> clipPropClip =
+      child->GetClipPropClipRect(disp, effects, child->GetSize());
+    if (clipPropClip) {
+      dirty.IntersectRect(dirty, *clipPropClip);
+      clipState.ClipContentDescendants(
+        *clipPropClip + aBuilder->ToReferenceFrame(child));
     }
 
     child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
 
     if (aBuilder->IsBuildingLayerEventRegions()) {
       // If this frame has a different animated geometry root than its parent,
       // make sure we accumulate event regions for its layer.
       if (buildingForChild.IsAnimatedGeometryRoot()) {
@@ -7891,20 +7907,18 @@ UnionBorderBoxes(nsIFrame* aFrame, bool 
   if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
       fType == nsGkAtoms::scrollFrame ||
       fType == nsGkAtoms::listControlFrame ||
       fType == nsGkAtoms::svgOuterSVGFrame) {
     return u;
   }
 
   const nsStyleEffects* effects = aFrame->StyleEffects();
-  nsRect clipPropClipRect;
-  bool hasClipPropClip =
-    aFrame->GetClipPropClipRect(disp, effects, &clipPropClipRect,
-                                bounds.Size());
+  Maybe<nsRect> clipPropClipRect =
+    aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
 
   // Iterate over all children except pop-ups.
   const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
                                     nsIFrame::kSelectPopupList);
   for (nsIFrame::ChildListIterator childLists(aFrame);
        !childLists.IsDone(); childLists.Next()) {
     if (skip.Contains(childLists.CurrentID())) {
       continue;
@@ -7925,19 +7939,19 @@ UnionBorderBoxes(nsIFrame* aFrame, bool 
       bool validRect = true;
       nsRect childRect = UnionBorderBoxes(child, true, validRect) +
                          child->GetPosition();
 
       if (!validRect) {
         continue;
       }
 
-      if (hasClipPropClip) {
+      if (clipPropClipRect) {
         // Intersect with the clip before transforming.
-        childRect.IntersectRect(childRect, clipPropClipRect);
+        childRect.IntersectRect(childRect, *clipPropClipRect);
       }
 
       // Note that we transform each child separately according to
       // aFrame's transform, and then union, which gives a different
       // (smaller) result from unioning and then transforming the
       // union.  This doesn't match the way we handle overflow areas
       // with 2-D transforms, though it does match the way we handle
       // overflow areas in preserve-3d 3-D scenes.
@@ -8147,23 +8161,22 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
   ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
 
   // Nothing in here should affect scrollable overflow.
   aOverflowAreas.VisualOverflow() =
     ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize);
 
   // Absolute position clipping
   const nsStyleEffects* effects = StyleEffects();
-  nsRect clipPropClipRect;
-  bool hasClipPropClip =
-    GetClipPropClipRect(disp, effects, &clipPropClipRect, aNewSize);
-  if (hasClipPropClip) {
+  Maybe<nsRect> clipPropClipRect =
+    GetClipPropClipRect(disp, effects, aNewSize);
+  if (clipPropClipRect) {
     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
       nsRect& o = aOverflowAreas.Overflow(otype);
-      o.IntersectRect(o, clipPropClipRect);
+      o.IntersectRect(o, *clipPropClipRect);
     }
   }
 
   /* If we're transformed, transform the overflow rect by the current transformation. */
   bool hasTransform = IsTransformed();
   nsSize oldSize = mRect.Size();
   bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -722,23 +722,30 @@ nsHTMLScrollFrame::ReflowContents(Scroll
   // This just happens sometimes.
   TryLayout(aState, &kidDesiredSize,
             aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN,
             aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN,
             true);
 }
 
 void
-nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowInput& aState,
+nsHTMLScrollFrame::PlaceScrollArea(ScrollReflowInput& aState,
                                    const nsPoint& aScrollPosition)
 {
   nsIFrame *scrolledFrame = mHelper.mScrolledFrame;
   // Set the x,y of the scrolled frame to the correct value
   scrolledFrame->SetPosition(mHelper.mScrollPort.TopLeft() - aScrollPosition);
 
+  // Recompute our scrollable overflow, taking perspective children into
+  // account. Note that this only recomputes the overflow areas stored on the
+  // helper (which are used to compute scrollable length and scrollbar thumb
+  // sizes) but not the overflow areas stored on the frame. This seems to work
+  // for now, but it's possible that we may need to update both in the future.
+  AdjustForPerspective(aState.mContentsOverflowAreas.ScrollableOverflow());
+
   nsRect scrolledArea;
   // Preserve the width or height of empty rects
   nsSize portSize = mHelper.mScrollPort.Size();
   nsRect scrolledRect =
     mHelper.GetUnsnappedScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
                                              portSize);
   scrolledArea.UnionRectEdges(scrolledRect,
                               nsRect(nsPoint(0,0), portSize));
@@ -831,16 +838,150 @@ GetBrowserRoot(nsIContent* aContent)
           frameElement->NodeInfo()->Equals(nsGkAtoms::browser, kNameSpaceID_XUL))
         return frameElement;
     }
   }
 
   return nullptr;
 }
 
+// When we have perspective set on the outer scroll frame, and transformed
+// children (possibly with preserve-3d) then the effective transform on the
+// child depends on the offset to the scroll frame, which changes as we scroll.
+// This perspective transform can cause the element to move relative to the
+// scrolled inner frame, which would cause the scrollable length changes during
+// scrolling if we didn't account for it. Since we don't want scrollHeight/Width
+// and the size of scrollbar thumbs to change during scrolling, we compute the
+// scrollable overflow by determining the scroll position at which the child
+// becomes completely visible within the scrollport rather than using the union
+// of the overflow areas at their current position.
+void
+GetScrollableOverflowForPerspective(nsIFrame* aScrolledFrame,
+                                    nsIFrame* aCurrentFrame,
+                                    const nsRect aScrollPort,
+                                    nsPoint aOffset,
+                                    nsRect& aScrolledFrameOverflowArea)
+{
+  // Iterate over all children except pop-ups.
+  FrameChildListIDs skip = nsIFrame::kSelectPopupList | nsIFrame::kPopupList;
+  for (nsIFrame::ChildListIterator childLists(aCurrentFrame);
+       !childLists.IsDone(); childLists.Next()) {
+    if (skip.Contains(childLists.CurrentID())) {
+      continue;
+    }
+
+    for (nsIFrame* child : childLists.CurrentList()) {
+      nsPoint offset = aOffset;
+
+      // When we reach a direct child of the scroll, then we record the offset
+      // to convert from that frame's coordinate into the scroll frame's
+      // coordinates. Preserve-3d descendant frames use the same offset as their
+      // ancestors, since TransformRect already converts us into the coordinate
+      // space of the preserve-3d root.
+      if (aScrolledFrame == aCurrentFrame) {
+        offset = child->GetPosition();
+      }
+
+      if (child->Extend3DContext()) {
+        // If we're a preserve-3d frame, then recurse and include our
+        // descendants since overflow of preserve-3d frames is only included
+        // in the post-transform overflow area of the preserve-3d root frame.
+        GetScrollableOverflowForPerspective(aScrolledFrame, child, aScrollPort,
+                                            offset, aScrolledFrameOverflowArea);
+      }
+
+      // If we're transformed, then we want to consider the possibility that
+      // this frame might move relative to the scrolled frame when scrolling.
+      // For preserve-3d, leaf frames have correct overflow rects relative to
+      // themselves. preserve-3d 'nodes' (intermediate frames and the root) have
+      // only their untransformed children included in their overflow relative
+      // to self, which is what we want to include here.
+      if (child->IsTransformed()) {
+        // Compute the overflow rect for this leaf transform frame in the
+        // coordinate space of the scrolled frame.
+        nsPoint scrollPos = aScrolledFrame->GetPosition();
+        nsRect preScroll = nsDisplayTransform::TransformRect(
+          child->GetScrollableOverflowRectRelativeToSelf(), child);
+
+        // Temporarily override the scroll position of the scrolled frame by
+        // 10 CSS pixels, and then recompute what the overflow rect would be.
+        // This scroll position may not be valid, but that shouldn't matter
+        // for our calculations.
+        aScrolledFrame->SetPosition(scrollPos + nsPoint(600, 600));
+        nsRect postScroll = nsDisplayTransform::TransformRect(
+          child->GetScrollableOverflowRectRelativeToSelf(), child);
+        aScrolledFrame->SetPosition(scrollPos);
+
+        // Compute how many app units the overflow rects moves by when we adjust
+        // the scroll position by 1 app unit.
+        double rightDelta =
+          (postScroll.XMost() - preScroll.XMost() + 600.0) / 600.0;
+        double bottomDelta =
+          (postScroll.YMost() - preScroll.YMost() + 600.0) / 600.0;
+
+        // We can't ever have negative scrolling.
+        NS_ASSERTION(rightDelta > 0.0f && bottomDelta > 0.0f,
+                     "Scrolling can't be reversed!");
+
+        // Move preScroll into the coordinate space of the scrollport.
+        preScroll += offset + scrollPos;
+
+        // For each of the four edges of preScroll, figure out how far they
+        // extend beyond the scrollport. Ignore negative values since that means
+        // that side is already scrolled in to view and we don't need to add
+        // overflow to account for it.
+        nsMargin overhang(std::max(0, aScrollPort.Y() - preScroll.Y()),
+                          std::max(0, preScroll.XMost() - aScrollPort.XMost()),
+                          std::max(0, preScroll.YMost() - aScrollPort.YMost()),
+                          std::max(0, aScrollPort.X() - preScroll.X()));
+
+        // Scale according to rightDelta/bottomDelta to adjust for the different
+        // scroll rates.
+        overhang.top /= bottomDelta;
+        overhang.right /= rightDelta;
+        overhang.bottom /= bottomDelta;
+        overhang.left /= rightDelta;
+
+        // Take the minimum overflow rect that would allow the current scroll
+        // position, using the size of the scroll port and offset by the
+        // inverse of the scroll position.
+        nsRect overflow(0, 0, aScrollPort.width, aScrollPort.height);
+
+        // Expand it by our margins to get an overflow rect that would allow all
+        // edges of our transformed content to be scrolled into view.
+        overflow.Inflate(overhang);
+
+        // Merge it with the combined overflow
+        aScrolledFrameOverflowArea.UnionRect(aScrolledFrameOverflowArea,
+                                             overflow);
+      } else if (aCurrentFrame == aScrolledFrame) {
+        aScrolledFrameOverflowArea.UnionRect(
+          aScrolledFrameOverflowArea,
+          child->GetScrollableOverflowRectRelativeToParent());
+      }
+    }
+  }
+}
+
+void
+nsHTMLScrollFrame::AdjustForPerspective(nsRect& aScrollableOverflow)
+{
+  // If we have perspective that is being applied to our children, then
+  // the effective transform on the child depends on the relative position
+  // of the child to us and changes during scrolling.
+  if (!ChildrenHavePerspective()) {
+    return;
+  }
+  aScrollableOverflow.SetEmpty();
+  GetScrollableOverflowForPerspective(mHelper.mScrolledFrame,
+                                      mHelper.mScrolledFrame,
+                                      mHelper.mScrollPort,
+                                      nsPoint(), aScrollableOverflow);
+}
+
 void
 nsHTMLScrollFrame::Reflow(nsPresContext*           aPresContext,
                           ReflowOutput&     aDesiredSize,
                           const ReflowInput& aReflowInput,
                           nsReflowStatus&          aStatus)
 {
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
@@ -888,16 +1029,26 @@ nsHTMLScrollFrame::Reflow(nsPresContext*
     mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
   nsPoint oldScrollPosition = mHelper.GetScrollPosition();
 
   state.mComputedBorder = aReflowInput.ComputedPhysicalBorderPadding() -
     aReflowInput.ComputedPhysicalPadding();
 
   ReflowContents(&state, aDesiredSize);
 
+  aDesiredSize.Width() = state.mInsideBorderSize.width +
+    state.mComputedBorder.LeftRight();
+  aDesiredSize.Height() = state.mInsideBorderSize.height +
+    state.mComputedBorder.TopBottom();
+
+  // Set the size of the frame now since computing the perspective-correct
+  // overflow (within PlaceScrollArea) can rely on it.
+  SetSize(aDesiredSize.GetWritingMode(),
+          aDesiredSize.Size(aDesiredSize.GetWritingMode()));
+
   // Restore the old scroll position, for now, even if that's not valid anymore
   // because we changed size. We'll fix it up in a post-reflow callback, because
   // our current size may only be temporary (e.g. we're compute XUL desired sizes).
   PlaceScrollArea(state, oldScrollPosition);
   if (!mHelper.mPostedReflowCallback) {
     // Make sure we'll try scrolling to restored position
     PresContext()->PresShell()->PostReflowCallback(&mHelper);
     mHelper.mPostedReflowCallback = true;
@@ -927,21 +1078,16 @@ nsHTMLScrollFrame::Reflow(nsPresContext*
                state.mInsideBorderSize);
       mHelper.LayoutScrollbars(state.mBoxState, insideBorderArea,
                               oldScrollAreaBounds);
     } else {
       mHelper.mSkippedScrollbarLayout = true;
     }
   }
 
-  aDesiredSize.Width() = state.mInsideBorderSize.width +
-    state.mComputedBorder.LeftRight();
-  aDesiredSize.Height() = state.mInsideBorderSize.height +
-    state.mComputedBorder.TopBottom();
-
   aDesiredSize.SetOverflowAreasToDesiredBounds();
   if (mHelper.IsIgnoringViewportClipping()) {
     aDesiredSize.mOverflowAreas.UnionWith(
       state.mContentsOverflowAreas + mHelper.mScrolledFrame->GetPosition());
   }
 
   mHelper.UpdateSticky();
   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
@@ -2739,17 +2885,28 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     if (needFrameVisibilityUpdate) {
       presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
     }
   }
 
   if (mOuter->ChildrenHavePerspective()) {
     // The overflow areas of descendants may depend on the scroll position,
     // so ensure they get updated.
+
+    // First we recompute the overflow areas of the transformed children
+    // that use the perspective. FinishAndStoreOverflow only calls this
+    // if the size changes, so we need to do it manually.
     mOuter->RecomputePerspectiveChildrenOverflow(mOuter);
+
+    // Update the overflow for the scrolled frame to take any changes from the
+    // children into account.
+    mScrolledFrame->UpdateOverflow();
+
+    // Update the overflow for the outer so that we recompute scrollbars.
+    mOuter->UpdateOverflow();
   }
 
   ScheduleSyntheticMouseMove();
 
   { // scope the AutoScrollbarRepaintSuppression
     AutoScrollbarRepaintSuppression repaintSuppression(this, !schedulePaint);
     nsWeakFrame weakFrame(mOuter);
     UpdateScrollbarPosition();
@@ -4696,16 +4853,20 @@ nsXULScrollFrame::LayoutScrollArea(nsBox
   nsSize minSize = mHelper.mScrolledFrame->GetXULMinSize(aState);
 
   if (minSize.height > childRect.height)
     childRect.height = minSize.height;
 
   if (minSize.width > childRect.width)
     childRect.width = minSize.width;
 
+  // TODO: Handle transformed children that inherit perspective
+  // from this frame. See AdjustForPerspective for how we handle
+  // this for HTML scroll frames.
+
   aState.SetLayoutFlags(flags);
   ClampAndSetBounds(aState, childRect, aScrollPosition);
   mHelper.mScrolledFrame->XULLayout(aState);
 
   childRect = mHelper.mScrolledFrame->GetRect();
 
   if (childRect.width < mHelper.mScrollPort.width ||
       childRect.height < mHelper.mScrollPort.height)
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -682,17 +682,17 @@ public:
   bool ScrolledContentDependsOnHeight(ScrollReflowInput* aState);
   void ReflowScrolledFrame(ScrollReflowInput* aState,
                            bool aAssumeHScroll,
                            bool aAssumeVScroll,
                            ReflowOutput* aMetrics,
                            bool aFirstPass);
   void ReflowContents(ScrollReflowInput* aState,
                       const ReflowOutput& aDesiredSize);
-  void PlaceScrollArea(const ScrollReflowInput& aState,
+  void PlaceScrollArea(ScrollReflowInput& aState,
                        const nsPoint& aScrollPosition);
   nscoord GetIntrinsicVScrollbarWidth(nsRenderingContext *aRenderingContext);
 
   virtual bool GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
                               Sides aSkipSides, nscoord aRadii[8]) const override {
     return mHelper.GetBorderRadii(aFrameSize, aBorderArea, aSkipSides, aRadii);
   }
 
@@ -705,16 +705,21 @@ public:
                       ReflowOutput&     aDesiredSize,
                       const ReflowInput& aReflowInput,
                       nsReflowStatus&          aStatus) override;
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
     return mHelper.ComputeCustomOverflow(aOverflowAreas);
   }
 
+  // Recomputes the scrollable overflow area we store in the helper to take children
+  // that are affected by perpsective set on the outer frame and scroll at different
+  // rates.
+  void AdjustForPerspective(nsRect& aScrollableOverflow);
+
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   virtual void SetInitialChildList(ChildListID     aListID,
                                    nsFrameList&    aChildList) override;
   virtual void AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList) override;
   virtual void InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2873,30 +2873,28 @@ public:
   /**
    * Return true if and only if this frame obeys visibility:hidden.
    * if it does not, then nsContainerFrame will hide its view even though
    * this means children can't be made visible again.
    */
   virtual bool SupportsVisibilityHidden() { return true; }
 
   /**
-   * Returns true if the frame has a valid clip rect set via the 'clip'
-   * property, and the 'clip' property applies to this frame. The 'clip'
-   * property applies to HTML frames if they are absolutely positioned. The
-   * 'clip' property applies to SVG frames regardless of the value of the
-   * 'position' property.
+   * Returns the clip rect set via the 'clip' property, if the 'clip' property
+   * applies to this frame; otherwise returns Nothing(). The 'clip' property
+   * applies to HTML frames if they are absolutely positioned. The 'clip'
+   * property applies to SVG frames regardless of the value of the 'position'
+   * property.
    *
-   * If this method returns true, then we also set aRect to the computed clip
-   * rect, with coordinates relative to this frame's origin. aRect must not be
-   * null!
+   * The coordinates of the returned rectangle are relative to this frame's
+   * origin.
    */
-  bool GetClipPropClipRect(const nsStyleDisplay* aDisp,
-                           const nsStyleEffects* aEffects,
-                           nsRect* aRect,
-                           const nsSize& aSize) const;
+  Maybe<nsRect> GetClipPropClipRect(const nsStyleDisplay* aDisp,
+                                    const nsStyleEffects* aEffects,
+                                    const nsSize& aSize) const;
 
   /**
    * Check if this frame is focusable and in the current tab order.
    * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
    * For example, only the selected radio button in a group is in the 
    * tab order, unless the radio group has no selection in which case
    * all of the visible, non-disabled radio buttons in the group are 
    * in the tab order. On the other hand, all of the visible, non-disabled 
--- a/layout/generic/test/mochitest.ini
+++ b/layout/generic/test/mochitest.ini
@@ -96,16 +96,17 @@ skip-if = buildapp == 'b2g' #Bug 931116,
 skip-if = buildapp == 'b2g' # b2g(Target should not have scrolled - got 114.10000610351562, expected 115.39999389648438) b2g-debug(Target should not have scrolled - got 114.10000610351562, expected 115.39999389648438) b2g-desktop(Target should not have scrolled - got 114.10000610351562, expected 115.39999389648438)
 [test_bug831780.html]
 [test_bug841361.html]
 [test_bug904810.html]
 [test_bug938772.html]
 [test_bug970363.html]
 [test_bug1062406.html]
 [test_bug1174521.html]
+[test_bug1198135.html]
 [test_contained_plugin_transplant.html]
 skip-if = os=='win'
 [test_image_selection.html]
 [test_image_selection_2.html]
 [test_invalidate_during_plugin_paint.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(plugins not supported) b2g-debug(plugins not supported) b2g-desktop(plugins not supported)
 [test_intrinsic_size_on_loading.html]
 [test_movement_by_characters.html]
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/test_bug1198135.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1198135
+-->
+<html><head>
+<title>Test for Bug 1198135</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1198135">Mozilla Bug 1198135</a>
+<style>
+.example {
+	perspective: 100px;
+	height: 300px;
+	width: 300px;
+	overflow-x: hidden;
+	overflow-y: auto;
+	border: 1px solid blue;
+}
+
+.example__group {
+	position: relative;
+	transform-style: preserve-3d;
+    height: 300px;
+    top: 0;
+}
+.example__layer {
+	position: absolute;
+	top:0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+}
+.layer--a {
+	box-shadow: inset 0 0 0 1px red;
+}
+.layer--b {
+	box-shadow: inset 0 0 0 1px blue;
+	transform: translateZ(50px) scale(.45);
+}
+.layer--c {
+	box-shadow: inset 0 0 0 1px green;
+}
+.layer--d {
+	box-shadow: inset 00px 0 0px 1px orange;
+	transform: translateZ(50px) scale(.45);
+}
+.layer--e {
+	box-shadow: inset 00px 0 0px 1px orange;
+	transform: translateZ(50px) scale(.45) translateY(300px);
+}
+</style>
+</head>
+
+<body>
+
+<div class="example" id="first">
+  <div class="example__group">
+    <div class="example__layer layer--a"></div>
+    <div class="example__layer layer--b"></div>
+  </div>
+  <div class="example__group">
+    <div class="example__layer layer--c"></div>
+    <div class="example__layer layer--d"></div>
+  </div>
+</div>
+
+<div class="example" id="second">
+  <div class="example__group">
+    <div class="example__layer layer--a"></div>
+    <div class="example__layer layer--b"></div>
+  </div>
+  <div class="example__group">
+    <div class="example__layer layer--c"></div>
+    <div class="example__layer layer--e"></div>
+  </div>
+</div>
+
+<script>
+  is(document.getElementById("first").scrollHeight, 600, "Scroll height should be computed correctly");
+  // The true height is 727.5 and we don't always snap the same direction.
+  isfuzzy(document.getElementById("second").scrollHeight, 728, 1, "Scroll height should be computed correctly");
+</script>
+
+</body></html>
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -6026,47 +6026,48 @@ CSSParserImpl::ParsePseudoSelector(int32
   if (parsingPseudoElement &&
       !isPseudoElement &&
       !isAnonBox) {
     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
     UngetToken();
     return eSelectorParsingStatus_Error;
   }
 
+  if (aSelector.IsPseudoElement()) {
+    CSSPseudoElementType type = aSelector.PseudoType();
+    if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
+      // We only allow user action pseudo-classes on certain pseudo-elements.
+      REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
+      UngetToken();
+      return eSelectorParsingStatus_Error;
+    }
+    if (!isPseudoClass || !pseudoClassIsUserAction) {
+      // CSS 4 Selectors says that pseudo-elements can only be followed by
+      // a user action pseudo-class.
+      REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
+      UngetToken();
+      return eSelectorParsingStatus_Error;
+    }
+  }
+
   if (!parsingPseudoElement &&
       CSSPseudoClassType::negation == pseudoClassType) {
     if (aIsNegated) { // :not() can't be itself negated
       REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
       UngetToken();
       return eSelectorParsingStatus_Error;
     }
     // CSS 3 Negation pseudo-class takes one simple selector as argument
     nsSelectorParsingStatus parsingStatus =
       ParseNegatedSimpleSelector(aDataMask, aSelector);
     if (eSelectorParsingStatus_Continue != parsingStatus) {
       return parsingStatus;
     }
   }
   else if (!parsingPseudoElement && isPseudoClass) {
-    if (aSelector.IsPseudoElement()) {
-      CSSPseudoElementType type = aSelector.PseudoType();
-      if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
-        // We only allow user action pseudo-classes on certain pseudo-elements.
-        REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
-        UngetToken();
-        return eSelectorParsingStatus_Error;
-      }
-      if (!pseudoClassIsUserAction) {
-        // CSS 4 Selectors says that pseudo-elements can only be followed by
-        // a user action pseudo-class.
-        REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
-        UngetToken();
-        return eSelectorParsingStatus_Error;
-      }
-    }
     aDataMask |= SEL_MASK_PCLASS;
     if (eCSSToken_Function == mToken.mType) {
       nsSelectorParsingStatus parsingStatus;
       if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
         parsingStatus =
           ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
       }
       else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
@@ -6498,23 +6499,17 @@ CSSParserImpl::ParseSelector(nsCSSSelect
   nsAutoPtr<nsAtomList> pseudoElementArgs;
   CSSPseudoElementType pseudoElementType = CSSPseudoElementType::NotPseudo;
 
   int32_t dataMask = 0;
   nsSelectorParsingStatus parsingStatus =
     ParseTypeOrUniversalSelector(dataMask, *selector, false);
 
   while (parsingStatus == eSelectorParsingStatus_Continue) {
-    if (eCSSToken_ID == mToken.mType) { // #id
-      parsingStatus = ParseIDSelector(dataMask, *selector);
-    }
-    else if (mToken.IsSymbol('.')) {    // .class
-      parsingStatus = ParseClassSelector(dataMask, *selector);
-    }
-    else if (mToken.IsSymbol(':')) {    // :pseudo
+    if (mToken.IsSymbol(':')) {    // :pseudo
       parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
                                           getter_AddRefs(pseudoElement),
                                           getter_Transfers(pseudoElementArgs),
                                           &pseudoElementType);
       if (pseudoElement &&
           pseudoElementType != CSSPseudoElementType::AnonBox) {
         // Pseudo-elements other than anonymous boxes are represented with
         // a special ':' combinator.
@@ -6522,16 +6517,27 @@ CSSParserImpl::ParseSelector(nsCSSSelect
         aList->mWeight += selector->CalcWeight();
 
         selector = aList->AddSelector(':');
 
         selector->mLowercaseTag.swap(pseudoElement);
         selector->mClassList = pseudoElementArgs.forget();
         selector->SetPseudoType(pseudoElementType);
       }
+    } else if (selector->IsPseudoElement()) {
+      // Once we parsed a pseudo-element, we can only parse
+      // pseudo-classes (and only a limited set, which
+      // ParsePseudoSelector knows how to handle).
+      parsingStatus = eSelectorParsingStatus_Done;
+      UngetToken();
+      break;
+    } else if (eCSSToken_ID == mToken.mType) { // #id
+      parsingStatus = ParseIDSelector(dataMask, *selector);
+    } else if (mToken.IsSymbol('.')) {    // .class
+      parsingStatus = ParseClassSelector(dataMask, *selector);
     }
     else if (mToken.IsSymbol('[')) {    // [attribute
       parsingStatus = ParseAttributeSelector(dataMask, *selector);
       if (eSelectorParsingStatus_Error == parsingStatus) {
         SkipUntil(']');
       }
     }
     else {  // not a selector token, we're done
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1259,16 +1259,42 @@ nsStyleContext::CalcStyleDifferenceInter
       }
     }
 
     if (change) {
       hint |= nsChangeHint_RepaintFrame;
     }
   }
 
+  if (hint & nsChangeHint_UpdateContainingBlock) {
+    // If a struct returned nsChangeHint_UpdateContainingBlock, that
+    // means that one property's influence on whether we're a containing
+    // block for abs-pos or fixed-pos elements has changed.  However, we
+    // only need to return the hint if the overall computation of
+    // whether we establish a containing block has changed.
+
+    // This depends on data in nsStyleDisplay and nsStyleEffects, so we
+    // do it here.
+
+    // Note that it's perhaps good for this test to be last because it
+    // doesn't use Peek* functions to get the structs on the old
+    // context.  But this isn't a big concern because these struct
+    // getters should be called during frame construction anyway.
+    if (StyleDisplay()->IsAbsPosContainingBlockForAppropriateFrame(this) ==
+        aNewContext->StyleDisplay()->
+          IsAbsPosContainingBlockForAppropriateFrame(aNewContext) &&
+        StyleDisplay()->IsFixedPosContainingBlockForAppropriateFrame(this) ==
+        aNewContext->StyleDisplay()->
+          IsFixedPosContainingBlockForAppropriateFrame(aNewContext)) {
+      // While some styles that cause the frame to be a containing block
+      // has changed, the overall result hasn't.
+      hint &= ~nsChangeHint_UpdateContainingBlock;
+    }
+  }
+
   MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints),
              "Added a new hint without bumping AllHints?");
   return hint & ~nsChangeHint_NeutralChange;
 }
 
 nsChangeHint
 nsStyleContext::CalcStyleDifference(nsStyleContext* aNewContext,
                                     nsChangeHint aParentHintsNotHandledForDescendants,
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -3028,36 +3028,71 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   // value will be massaged to be something that makes sense for
   // SVG text.
   inline bool IsBlockInside(const nsIFrame* aContextFrame) const;
   inline bool IsBlockOutside(const nsIFrame* aContextFrame) const;
   inline bool IsInlineOutside(const nsIFrame* aContextFrame) const;
   inline bool IsOriginalDisplayInlineOutside(const nsIFrame* aContextFrame) const;
   inline uint8_t GetDisplay(const nsIFrame* aContextFrame) const;
   inline bool IsFloating(const nsIFrame* aContextFrame) const;
-  inline bool IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const;
   inline bool IsRelativelyPositioned(const nsIFrame* aContextFrame) const;
   inline bool IsAbsolutelyPositioned(const nsIFrame* aContextFrame) const;
 
   // These methods are defined in nsStyleStructInlines.h.
 
   /**
+   * Returns whether the element is a containing block for its
+   * absolutely positioned descendants.
+   * aContextFrame is the frame for which this is the nsStyleDisplay.
+   */
+  inline bool IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const;
+
+  /**
+   * The same as IsAbsPosContainingBlock, except skipping the tests that
+   * are based on the frame rather than the style context (thus
+   * potentially returning a false positive).
+   */
+  template<class StyleContextLike>
+  inline bool IsAbsPosContainingBlockForAppropriateFrame(
+                StyleContextLike* aStyleContext) const;
+
+  /**
    * Returns true when the element has the transform property
    * or a related property, and supports CSS transforms.
-   * aContextFrame is the frame for which this is the nsStylePosition.
+   * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool HasTransform(const nsIFrame* aContextFrame) const;
 
   /**
    * Returns true when the element is a containing block for its fixed-pos
    * descendants.
-   * aContextFrame is the frame for which this is the nsStylePosition.
+   * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const;
 
+  /**
+   * The same as IsFixedPosContainingBlock, except skipping the tests that
+   * are based on the frame rather than the style context (thus
+   * potentially returning a false positive).
+   */
+  template<class StyleContextLike>
+  inline bool IsFixedPosContainingBlockForAppropriateFrame(
+                StyleContextLike* aStyleContext) const;
+
+private:
+  // Helpers for above functions, which do some but not all of the tests
+  // for them (since transform must be tested separately for each).
+  template<class StyleContextLike>
+  inline bool HasAbsPosContainingBlockStyleInternal(
+                StyleContextLike* aStyleContext) const;
+  template<class StyleContextLike>
+  inline bool HasFixedPosContainingBlockStyleInternal(
+                StyleContextLike* aStyleContext) const;
+
+public:
   // Return the 'float' and 'clear' properties, with inline-{start,end} values
   // resolved to {left,right} according to the given writing mode. These are
   // defined in WritingModes.h.
   inline mozilla::StyleFloat PhysicalFloats(mozilla::WritingMode aWM) const;
   inline uint8_t PhysicalBreakType(mozilla::WritingMode aWM) const;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -139,40 +139,90 @@ nsStyleDisplay::IsFloating(const nsIFram
 // this function in comments.
 bool
 nsStyleDisplay::HasTransform(const nsIFrame* aContextFrame) const
 {
   NS_ASSERTION(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame");
   return HasTransformStyle() && aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms);
 }
 
+template<class StyleContextLike>
+bool
+nsStyleDisplay::HasFixedPosContainingBlockStyleInternal(
+                  StyleContextLike* aStyleContext) const
+{
+  // NOTE: Any CSS properties that influence the output of this function
+  // should have the CSS_PROPERTY_FIXPOS_CB set on them.
+  NS_ASSERTION(aStyleContext->StyleDisplay() == this,
+               "unexpected aStyleContext");
+  return IsContainPaint() ||
+         HasPerspectiveStyle() ||
+         (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) ||
+         aStyleContext->StyleEffects()->HasFilters();
+}
+
+template<class StyleContextLike>
+bool
+nsStyleDisplay::IsFixedPosContainingBlockForAppropriateFrame(
+                  StyleContextLike* aStyleContext) const
+{
+  // NOTE: Any CSS properties that influence the output of this function
+  // should have the CSS_PROPERTY_FIXPOS_CB set on them.
+  return HasFixedPosContainingBlockStyleInternal(aStyleContext) ||
+         HasTransformStyle();
+}
+
 bool
 nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the CSS_PROPERTY_FIXPOS_CB set on them.
-  NS_ASSERTION(aContextFrame->StyleDisplay() == this,
-               "unexpected aContextFrame");
-  return (IsContainPaint() || HasTransform(aContextFrame) ||
-          HasPerspectiveStyle() ||
-          (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) ||
-          aContextFrame->StyleEffects()->HasFilters()) &&
-      !aContextFrame->IsSVGText();
+  if (!HasFixedPosContainingBlockStyleInternal(aContextFrame->StyleContext()) &&
+      !HasTransform(aContextFrame)) {
+    return false;
+  }
+  return !aContextFrame->IsSVGText();
+}
+
+template<class StyleContextLike>
+bool
+nsStyleDisplay::HasAbsPosContainingBlockStyleInternal(
+                  StyleContextLike* aStyleContext) const
+{
+  // NOTE: Any CSS properties that influence the output of this function
+  // should have the CSS_PROPERTY_ABSPOS_CB set on them.
+  NS_ASSERTION(aStyleContext->StyleDisplay() == this,
+               "unexpected aStyleContext");
+  return IsAbsolutelyPositionedStyle() ||
+         IsRelativelyPositionedStyle() ||
+         (mWillChangeBitField & NS_STYLE_WILL_CHANGE_ABSPOS_CB);
+}
+
+template<class StyleContextLike>
+bool
+nsStyleDisplay::IsAbsPosContainingBlockForAppropriateFrame(StyleContextLike* aStyleContext) const
+{
+  // NOTE: Any CSS properties that influence the output of this function
+  // should have the CSS_PROPERTY_ABSPOS_CB set on them.
+  return HasAbsPosContainingBlockStyleInternal(aStyleContext) ||
+         IsFixedPosContainingBlockForAppropriateFrame(aStyleContext);
 }
 
 bool
 nsStyleDisplay::IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const
 {
-  NS_ASSERTION(aContextFrame->StyleDisplay() == this,
-               "unexpected aContextFrame");
-  return ((IsAbsolutelyPositionedStyle() ||
-           IsRelativelyPositionedStyle() ||
-           (mWillChangeBitField & NS_STYLE_WILL_CHANGE_ABSPOS_CB)) &&
-          !aContextFrame->IsSVGText()) ||
-         IsFixedPosContainingBlock(aContextFrame);
+  // NOTE: Any CSS properties that influence the output of this function
+  // should have the CSS_PROPERTY_ABSPOS_CB set on them.
+  nsStyleContext* sc = aContextFrame->StyleContext();
+  if (!HasAbsPosContainingBlockStyleInternal(sc) &&
+      !HasFixedPosContainingBlockStyleInternal(sc) &&
+      !HasTransform(aContextFrame)) {
+    return false;
+  }
+  return !aContextFrame->IsSVGText();
 }
 
 bool
 nsStyleDisplay::IsRelativelyPositioned(const nsIFrame* aContextFrame) const
 {
   NS_ASSERTION(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame");
   return IsRelativelyPositionedStyle() && !aContextFrame->IsSVGText();
 }
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -145,16 +145,17 @@ support-files = file_bug829816.css
 support-files = file_bug1055933_circle-xxl.png
 [test_bug1089417.html]
 support-files = file_bug1089417_iframe.html
 [test_bug1112014.html]
 [test_bug1203766.html]
 [test_bug1232829.html]
 [test_cascade.html]
 [test_ch_ex_no_infloops.html]
+[test_change_hint_optimizations.html]
 [test_clip-path_polygon.html]
 [test_compute_data_with_start_struct.html]
 skip-if = toolkit == 'android'
 [test_computed_style.html]
 [test_computed_style_no_pseudo.html]
 [test_computed_style_prefs.html]
 [test_condition_text.html]
 [test_condition_text_assignment.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_change_hint_optimizations.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for style change hint optimizations</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  SimpleTest.waitForExplicitFinish();
+
+  function runTests() {
+
+    /** Test for Bug 1251075 **/
+    function test_bug1251075_div(id) {
+      var utils = SpecialPowers.DOMWindowUtils;
+
+      var div = document.getElementById(id);
+      div.style.display = "";
+
+      var description = div.style.cssText;
+
+      div.firstElementChild.offsetTop;
+      var constructedBefore = utils.framesConstructed;
+
+      div.style.transform = "translateX(10px)";
+      div.firstElementChild.offsetTop;
+      is(utils.framesConstructed, constructedBefore,
+         "adding a transform style to an element with " + description +
+         " should not cause frame reconstruction even when the element " +
+         "has absolutely positioned descendants");
+
+      div.style.display = "none";
+    }
+
+    test_bug1251075_div("bug1251075_a");
+    test_bug1251075_div("bug1251075_b");
+
+
+    SimpleTest.finish();
+  }
+
+  </script>
+</head>
+<body onload="runTests()">
+<div id="bug1251075_a" style="will-change: transform; display:none">
+  <div style="position: absolute; top: 0; left: 0;"></div>
+  <div style="position: fixed; top: 0; left: 0;"></div>
+</div>
+<div id="bug1251075_b" style="filter: blur(3px); display:none">
+  <div style="position: absolute; top: 0; left: 0;"></div>
+  <div style="position: fixed; top: 0; left: 0;"></div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/layout/style/test/test_selectors.html
+++ b/layout/style/test/test_selectors.html
@@ -1239,16 +1239,29 @@ function run() {
     test_balanced_unparseable("div \\\n p");
     test_balanced_unparseable("div\\\n p");
     test_balanced_unparseable("div \\\np");
     test_balanced_unparseable("div\\\np");
 
     // Test that :-moz-placeholder is parsable.
     test_parseable(":-moz-placeholder");
 
+    // Test that things other than user-action pseudo-classes are
+    // rejected after pseudo-elements.  Some of these tests rely on
+    // using a pseudo-element that supports a user-action pseudo-class
+    // after it, so we need to use the prefixed ::-moz-color-swatch,
+    // which is one of the ones with
+    // CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (none of which are
+    // unprefixed).
+    test_parseable("::-moz-color-swatch:hover");
+    test_balanced_unparseable("::-moz-color-swatch:not(.foo)");
+    test_balanced_unparseable("::-moz-color-swatch:first-child");
+    test_balanced_unparseable("::-moz-color-swatch:hover#foo");
+    test_balanced_unparseable(".foo::after:not(.bar) ~ h3");
+
     run_deferred_tests();
 }
 
 var deferred_tests = [];
 
 function defer_clonedoc_tests(docurl, onloadfunc)
 {
     deferred_tests.push( { docurl: docurl, onloadfunc: onloadfunc } );
--- a/layout/svg/SVGImageContext.h
+++ b/layout/svg/SVGImageContext.h
@@ -50,28 +50,30 @@ public:
 
   bool IsPaintingForSVGImageElement() const {
     return mIsPaintingSVGImageElement;
   }
 
   bool operator==(const SVGImageContext& aOther) const {
     return mViewportSize == aOther.mViewportSize &&
            mPreserveAspectRatio == aOther.mPreserveAspectRatio &&
-           mGlobalOpacity == aOther.mGlobalOpacity;
+           mGlobalOpacity == aOther.mGlobalOpacity &&
+           mIsPaintingSVGImageElement == aOther.mIsPaintingSVGImageElement;
   }
 
   bool operator!=(const SVGImageContext& aOther) const {
     return !(*this == aOther);
   }
 
   uint32_t Hash() const {
     return HashGeneric(mViewportSize.width,
                        mViewportSize.height,
                        mPreserveAspectRatio.map(HashPAR).valueOr(0),
-                       HashBytes(&mGlobalOpacity, sizeof(gfxFloat)));
+                       HashBytes(&mGlobalOpacity, sizeof(gfxFloat)),
+                       mIsPaintingSVGImageElement);
   }
 
 private:
   static uint32_t HashPAR(const SVGPreserveAspectRatio& aPAR) {
     return aPAR.Hash();
   }
 
   CSSIntSize                    mViewportSize;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_help.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtcp_receiver_help.cc
@@ -57,16 +57,19 @@ void RTCPPacketInformation::AddApplicati
     uint8_t* oldData = applicationData;
     uint16_t oldLength = applicationLength;
 
     // Don't copy more than kRtcpAppCode_DATA_SIZE bytes.
     uint16_t copySize = size;
     if (size > kRtcpAppCode_DATA_SIZE) {
         copySize = kRtcpAppCode_DATA_SIZE;
     }
+    if (((uint32_t) applicationLength) + copySize > UINT16_MAX) {
+      return;
+    }
 
     applicationLength += copySize;
     applicationData = new uint8_t[applicationLength];
 
     if (oldData)
     {
         memcpy(applicationData, oldData, oldLength);
         memcpy(applicationData+oldLength, data, copySize);
--- a/mfbt/Maybe.h
+++ b/mfbt/Maybe.h
@@ -11,16 +11,17 @@
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 #include <new>  // for placement new
+#include <type_traits>
 
 namespace mozilla {
 
 struct Nothing { };
 
 /*
  * Maybe is a container class which contains either zero or one elements. It
  * serves two roles. It can represent values which are *semantically* optional,
@@ -96,25 +97,66 @@ public:
   Maybe(const Maybe& aOther)
     : mIsSome(false)
   {
     if (aOther.mIsSome) {
       emplace(*aOther);
     }
   }
 
+  /**
+   * Maybe<T*> can be copy-constructed from a Maybe<U*> if U* and T* are
+   * compatible, or from Maybe<decltype(nullptr)>.
+   */
+  template<typename U,
+           typename =
+             typename std::enable_if<std::is_pointer<T>::value &&
+                                     (std::is_same<U, decltype(nullptr)>::value ||
+                                      (std::is_pointer<U>::value &&
+                                       std::is_base_of<typename std::remove_pointer<T>::type,
+                                                       typename std::remove_pointer<U>::type>::value))>::type>
+  MOZ_IMPLICIT
+  Maybe(const Maybe<U>& aOther)
+    : mIsSome(false)
+  {
+    if (aOther.isSome()) {
+      emplace(*aOther);
+    }
+  }
+
   Maybe(Maybe&& aOther)
     : mIsSome(false)
   {
     if (aOther.mIsSome) {
       emplace(Move(*aOther));
       aOther.reset();
     }
   }
 
+  /**
+   * Maybe<T*> can be move-constructed from a Maybe<U*> if U* and T* are
+   * compatible, or from Maybe<decltype(nullptr)>.
+   */
+  template<typename U,
+           typename =
+             typename std::enable_if<std::is_pointer<T>::value &&
+                                     (std::is_same<U, decltype(nullptr)>::value ||
+                                      (std::is_pointer<U>::value &&
+                                       std::is_base_of<typename std::remove_pointer<T>::type,
+                                                       typename std::remove_pointer<U>::type>::value))>::type>
+  MOZ_IMPLICIT
+  Maybe(Maybe<U>&& aOther)
+    : mIsSome(false)
+  {
+    if (aOther.isSome()) {
+      emplace(Move(*aOther));
+      aOther.reset();
+    }
+  }
+
   Maybe& operator=(const Maybe& aOther)
   {
     if (&aOther != this) {
       if (aOther.mIsSome) {
         if (mIsSome) {
           // XXX(seth): The correct code for this branch, below, can't be used
           // due to a bug in Visual Studio 2010. See bug 1052940.
           /*
--- a/mfbt/WeakPtr.h
+++ b/mfbt/WeakPtr.h
@@ -71,16 +71,57 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/RefCounted.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TypeTraits.h"
 
 #include <string.h>
 
+// Weak referencing is not implemeted as thread safe.  When a WeakPtr
+// is created or dereferenced on thread A but the real object is just
+// being Released() on thread B, there is a possibility of a race
+// when the proxy object (detail::WeakReference) is notified about
+// the real object destruction just between when thread A is storing
+// the object pointer locally and is about to add a reference to it.
+//
+// Hence, a non-null weak proxy object is considered to have a single
+// "owning thread".  It means that each query for a weak reference,
+// its dereference, and destruction of the real object must all happen
+// on a single thread.  The following macros implement assertions for
+// checking these conditions.
+
+#if defined(DEBUG) || (defined(NIGHTLY_BUILD) && !defined(MOZ_PROFILING))
+
+#include <thread>
+#define MOZ_WEAKPTR_DECLARE_THREAD_SAFETY_CHECK \
+  std::thread::id _owningThread; \
+  bool _empty; // If it was initialized as a placeholder with mPtr = nullptr.
+#define MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK() \
+  do { \
+    _owningThread = std::this_thread::get_id(); \
+    _empty = !p; \
+  } while (false)
+#define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY() \
+  MOZ_DIAGNOSTIC_ASSERT(_empty || _owningThread == std::this_thread::get_id(), \
+                        "WeakPtr used on multiple threads")
+#define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(that) \
+  (that)->AssertThreadSafety();
+
+#define MOZ_WEAKPTR_THREAD_SAFETY_CHECKING 1
+
+#else
+
+#define MOZ_WEAKPTR_DECLARE_THREAD_SAFETY_CHECK
+#define MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK() do { } while (false)
+#define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY() do { } while (false)
+#define MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(that) do { } while (false)
+
+#endif
+
 namespace mozilla {
 
 template <typename T> class WeakPtr;
 template <typename T> class SupportsWeakPtr;
 
 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
 #define MOZ_DECLARE_WEAKREFERENCE_TYPENAME(T) \
   static const char* weakReferenceTypeName() { return "WeakReference<" #T ">"; }
@@ -91,36 +132,50 @@ template <typename T> class SupportsWeak
 namespace detail {
 
 // This can live beyond the lifetime of the class derived from
 // SupportsWeakPtr.
 template<class T>
 class WeakReference : public ::mozilla::RefCounted<WeakReference<T> >
 {
 public:
-  explicit WeakReference(T* p) : mPtr(p) {}
+  explicit WeakReference(T* p) : mPtr(p)
+  {
+    MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK();
+  }
 
-  T* get() const { return mPtr; }
+  T* get() const {
+    MOZ_WEAKPTR_ASSERT_THREAD_SAFETY();
+    return mPtr;
+  }
 
 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
   const char* typeName() const
   {
     // The first time this is called mPtr is null, so don't
     // invoke any methods on mPtr.
     return T::weakReferenceTypeName();
   }
   size_t typeSize() const { return sizeof(*this); }
 #endif
 
+#ifdef MOZ_WEAKPTR_THREAD_SAFETY_CHECKING
+  void AssertThreadSafety() { MOZ_WEAKPTR_ASSERT_THREAD_SAFETY(); }
+#endif
+
 private:
   friend class mozilla::SupportsWeakPtr<T>;
 
-  void detach() { mPtr = nullptr; }
+  void detach() {
+    MOZ_WEAKPTR_ASSERT_THREAD_SAFETY();
+    mPtr = nullptr;
+  }
 
   T* MOZ_NON_OWNING_REF mPtr;
+  MOZ_WEAKPTR_DECLARE_THREAD_SAFETY_CHECK
 };
 
 } // namespace detail
 
 template <typename T>
 class SupportsWeakPtr
 {
 protected:
@@ -133,16 +188,18 @@ protected:
     }
   }
 
 private:
   const WeakPtr<T>& SelfReferencingWeakPtr()
   {
     if (!mSelfReferencingWeakPtr) {
       mSelfReferencingWeakPtr.mRef = new detail::WeakReference<T>(static_cast<T*>(this));
+    } else {
+      MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mSelfReferencingWeakPtr.mRef);
     }
     return mSelfReferencingWeakPtr;
   }
 
   const WeakPtr<const T>& SelfReferencingWeakPtr() const
   {
     const WeakPtr<T>& p = const_cast<SupportsWeakPtr*>(this)->SelfReferencingWeakPtr();
     return reinterpret_cast<const WeakPtr<const T>&>(p);
@@ -158,38 +215,43 @@ template <typename T>
 class WeakPtr
 {
   typedef detail::WeakReference<T> WeakReference;
 
 public:
   WeakPtr& operator=(const WeakPtr& aOther)
   {
     mRef = aOther.mRef;
+    MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mRef);
     return *this;
   }
 
   WeakPtr(const WeakPtr& aOther)
   {
+    // The thread safety check is performed inside of the operator= method.
     *this = aOther;
   }
 
   WeakPtr& operator=(T* aOther)
   {
     if (aOther) {
       *this = aOther->SelfReferencingWeakPtr();
     } else if (!mRef || mRef->get()) {
       // Ensure that mRef is dereferenceable in the uninitialized state.
       mRef = new WeakReference(nullptr);
     }
+    // The thread safety check happens inside SelfReferencingWeakPtr
+    // or is initialized in the WeakReference constructor.
     return *this;
   }
 
   MOZ_IMPLICIT WeakPtr(T* aOther)
   {
     *this = aOther;
+    MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mRef);
   }
 
   // Ensure that mRef is dereferenceable in the uninitialized state.
   WeakPtr() : mRef(new WeakReference(nullptr)) {}
 
   operator T*() const { return mRef->get(); }
   T& operator*() const { return *mRef->get(); }
 
--- a/mfbt/tests/TestMaybe.cpp
+++ b/mfbt/tests/TestMaybe.cpp
@@ -756,22 +756,105 @@ TestVirtualFunction() {
   Maybe<MyDerivedClass> derived;
   derived.emplace();
   derived.reset();
 
   // If this compiles successfully, we've passed.
   return true;
 }
 
+static Maybe<int*>
+ReturnSomeNullptr()
+{
+  return Some(nullptr);
+}
+
+struct D
+{
+  explicit D(Maybe<int*>) {}
+};
+
+static bool
+TestSomeNullptrConversion()
+{
+  Maybe<int*> m1 = Some(nullptr);
+  MOZ_RELEASE_ASSERT(m1.isSome());
+  MOZ_RELEASE_ASSERT(m1);
+  MOZ_RELEASE_ASSERT(!*m1);
+
+  auto m2 = ReturnSomeNullptr();
+  MOZ_RELEASE_ASSERT(m2.isSome());
+  MOZ_RELEASE_ASSERT(m2);
+  MOZ_RELEASE_ASSERT(!*m2);
+
+  Maybe<decltype(nullptr)> m3 = Some(nullptr);
+  MOZ_RELEASE_ASSERT(m3.isSome());
+  MOZ_RELEASE_ASSERT(m3);
+  MOZ_RELEASE_ASSERT(*m3 == nullptr);
+
+  D d(Some(nullptr));
+
+  return true;
+}
+
+struct Base {};
+struct Derived : Base {};
+
+static Maybe<Base*>
+ReturnDerivedPointer()
+{
+  Derived* d = nullptr;
+  return Some(d);
+}
+
+struct ExplicitConstructorBasePointer
+{
+  explicit ExplicitConstructorBasePointer(Maybe<Base*>) {}
+};
+
+static bool
+TestSomePointerConversion()
+{
+  Base base;
+  Derived derived;
+
+  Maybe<Base*> m1 = Some(&derived);
+  MOZ_RELEASE_ASSERT(m1.isSome());
+  MOZ_RELEASE_ASSERT(m1);
+  MOZ_RELEASE_ASSERT(*m1 == &derived);
+
+  auto m2 = ReturnDerivedPointer();
+  MOZ_RELEASE_ASSERT(m2.isSome());
+  MOZ_RELEASE_ASSERT(m2);
+  MOZ_RELEASE_ASSERT(*m2 == nullptr);
+
+  Maybe<Base*> m3 = Some(&base);
+  MOZ_RELEASE_ASSERT(m3.isSome());
+  MOZ_RELEASE_ASSERT(m3);
+  MOZ_RELEASE_ASSERT(*m3 == &base);
+
+  auto s1 = Some(&derived);
+  Maybe<Base*> c1(s1);
+  MOZ_RELEASE_ASSERT(c1.isSome());
+  MOZ_RELEASE_ASSERT(c1);
+  MOZ_RELEASE_ASSERT(*c1 == &derived);
+
+  ExplicitConstructorBasePointer ecbp(Some(&derived));
+
+  return true;
+}
+
 int
 main()
 {
   RUN_TEST(TestBasicFeatures);
   RUN_TEST(TestCopyAndMove);
   RUN_TEST(TestFunctionalAccessors);
   RUN_TEST(TestApply);
   RUN_TEST(TestMap);
   RUN_TEST(TestToMaybe);
   RUN_TEST(TestComparisonOperators);
   RUN_TEST(TestVirtualFunction);
+  RUN_TEST(TestSomeNullptrConversion);
+  RUN_TEST(TestSomePointerConversion);
 
   return 0;
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -166,16 +166,19 @@ pref("dom.enable_resource_timing", true)
 pref("dom.enable_user_timing", true);
 
 // Enable printing performance marks/measures to log
 pref("dom.performance.enable_user_timing_logging", false);
 
 // Enable notification of performance timing
 pref("dom.performance.enable_notify_performance_timing", false);
 
+// Enable Permission API's .revoke() method
+pref("dom.permissions.revoke.enable", false);
+
 // Enable Performance Observer API
 #ifdef NIGHTLY_BUILD
 pref("dom.enable_performance_observer", true);
 #else
 pref("dom.enable_performance_observer", false);
 #endif
 
 // Whether the Gamepad API is enabled
@@ -512,20 +515,20 @@ pref("media.navigator.audio.full_duplex"
 #endif
 #endif
 
 #if !defined(ANDROID)
 pref("media.getusermedia.screensharing.enabled", true);
 #endif
 
 #ifdef RELEASE_BUILD
-pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com");
+pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com");
 #else
  // includes Mozilla's test domain: mozilla.github.io (not intended for release)
-pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com");
+pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org,*.circuit.com,*.yourcircuit.com,circuit.siemens.com,yourcircuit.siemens.com,circuitsandbox.net,*.unify.com,tandi.circuitsandbox.net,*.ericsson.net,*.cct.ericsson.net,*.opentok.com,*.conf.meetecho.com,meet.jit.si,*.meet.jit.si,web.stage.speakeasyapp.net,web.speakeasyapp.net,*.hipchat.me,*.beta-wspbx.com,*.wspbx.com,*.unifiedcloudit.com,*.smartboxuc.com,*.smartbox-uc.com,*.panterranetworks.com,pexipdemo.com,*.pexipdemo.com,pex.me,*.pex.me,*.rd.pexip.com,1click.io,*.1click.io,*.fuze.com,*.fuzemeeting.com,*.thinkingphones.com,gotomeeting.com,*.gotomeeting.com,gotowebinar.com,*.gotowebinar.com,gototraining.com,*.gototraining.com,citrix.com,*.citrix.com,expertcity.com,*.expertcity.com,citrixonline.com,*.citrixonline.com,g2m.me,*.g2m.me,gotomeet.me,*.gotomeet.me,gotomeet.at,*.gotomeet.at,miriadaxdes.miriadax.net,certificacion.miriadax.net,miriadax.net,*.wire.com,sylaps.com,*.sylaps.com");
 #endif
 // OS/X 10.6 and XP have screen/window sharing off by default due to various issues - Caveat emptor
 pref("media.getusermedia.screensharing.allow_on_old_platforms", false);
 
 pref("media.getusermedia.audiocapture.enabled", false);
 
 // TextTrack support
 pref("media.webvtt.enabled", true);
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -759,18 +759,16 @@ DllBlocklist_Initialize()
   if (sBlocklistInitAttempted) {
     return;
   }
   sBlocklistInitAttempted = true;
 
   if (GetModuleHandleA("user32.dll")) {
     sUser32BeforeBlocklist = true;
   }
-  // Catch any missing DELAYLOADS for user32.dll
-  MOZ_ASSERT(!sUser32BeforeBlocklist);
 
   NtDllIntercept.Init("ntdll.dll");
 
   ReentrancySentinel::InitializeStatics();
 
   // We specifically use a detour, because there are cases where external
   // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
   // nop space patches. (Bug 951827)
--- a/netwerk/protocol/http/HttpChannelParentListener.cpp
+++ b/netwerk/protocol/http/HttpChannelParentListener.cpp
@@ -66,18 +66,18 @@ HttpChannelParentListener::OnStartReques
     return NS_ERROR_UNEXPECTED;
 
   LOG(("HttpChannelParentListener::OnStartRequest [this=%p]\n", this));
   return mNextListener->OnStartRequest(aRequest, aContext);
 }
 
 NS_IMETHODIMP
 HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest,
-                                          nsISupports *aContext,
-                                          nsresult aStatusCode)
+                                         nsISupports *aContext,
+                                         nsresult aStatusCode)
 {
   MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
     "Cannot call OnStopRequest if suspended for diversion!");
 
   if (!mNextListener)
     return NS_ERROR_UNEXPECTED;
 
   LOG(("HttpChannelParentListener::OnStopRequest: [this=%p status=%ul]\n",
@@ -89,20 +89,20 @@ HttpChannelParentListener::OnStopRequest
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParentListener::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest,
-                                            nsISupports *aContext,
-                                            nsIInputStream *aInputStream,
-                                            uint64_t aOffset,
-                                            uint32_t aCount)
+                                           nsISupports *aContext,
+                                           nsIInputStream *aInputStream,
+                                           uint64_t aOffset,
+                                           uint32_t aCount)
 {
   MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
     "Cannot call OnDataAvailable if suspended for diversion!");
 
   if (!mNextListener)
     return NS_ERROR_UNEXPECTED;
 
   LOG(("HttpChannelParentListener::OnDataAvailable [this=%p]\n", this));
--- a/netwerk/protocol/http/nsHttpRequestHead.cpp
+++ b/netwerk/protocol/http/nsHttpRequestHead.cpp
@@ -14,23 +14,54 @@
 
 //-----------------------------------------------------------------------------
 // nsHttpRequestHead
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace net {
 
+#ifdef MOZ_CRASHREPORTER
+
+void nsHttpRequestHead::DbgReentrantMonitorAutoEnter::DbgCheck(bool aIn)
+{
+    nsHttpAtom header;
+    if (!mInstance.mAnnotated && mInstance.mHeaders.Count() &&
+        !mInstance.mHeaders.PeekHeaderAt(0, header)) {
+        nsAutoCString str;
+        str.Append(nsPrintfCString("%s %s", aIn ? "in" : "out", mFunc));
+        // Output the content of the array header and the first nsEntry.
+        const uint8_t* p = reinterpret_cast<uint8_t*>
+            (mInstance.mHeaders.mHeaders.Elements()) - sizeof(nsTArrayHeader);
+        for (int i = 0; i < 28; ++i, ++p) {
+            str.Append(nsPrintfCString(" %02x", *p));
+        }
+        CrashReporter::AnnotateCrashReport(
+            NS_LITERAL_CSTRING("InvalidHttpHeaderArray"), str);
+        // Make sure we annotate only when we found it is invalid at the first
+        // time.
+        mInstance.mAnnotated = true;
+    }
+}
+
+#define ReentrantMonitorAutoEnter DbgReentrantMonitorAutoEnter
+#define mon(x) mon(*this, __func__)
+
+#endif
+
 nsHttpRequestHead::nsHttpRequestHead()
     : mMethod(NS_LITERAL_CSTRING("GET"))
     , mVersion(NS_HTTP_VERSION_1_1)
     , mParsedMethod(kMethod_Get)
     , mHTTPS(false)
     , mReentrantMonitor("nsHttpRequestHead.mReentrantMonitor")
     , mInVisitHeaders(false)
+#ifdef MOZ_CRASHREPORTER
+    , mAnnotated(false)
+#endif
 {
     MOZ_COUNT_CTOR(nsHttpRequestHead);
 }
 
 nsHttpRequestHead::~nsHttpRequestHead()
 {
     MOZ_COUNT_DTOR(nsHttpRequestHead);
 }
@@ -44,32 +75,16 @@ nsHttpRequestHead::Headers() const
     curr.mReentrantMonitor.AssertCurrentThreadIn();
     return mHeaders;
 }
 
 void
 nsHttpRequestHead::SetHeaders(const nsHttpHeaderArray& aHeaders)
 {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-#ifdef MOZ_CRASHREPORTER
-    nsHttpAtom header;
-    if (mHeaders.Count() && !mHeaders.PeekHeaderAt(0, header)) {
-      nsAutoCString str;
-      const uint8_t* p = reinterpret_cast<uint8_t*>(mHeaders.mHeaders.Elements()) -
-                         sizeof(nsTArrayHeader);
-      for (int i = 0; i < 48; ++i, ++p) {
-        str.Append(nsPrintfCString("%02x ", *p));
-      }
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("HttpHeaderArray"), str);
-      if (header._val) {
-        CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("HttpHeaderArray[0].header"),
-                                           nsDependentCString(header._val));
-      }
-    }
-#endif
     mHeaders = aHeaders;
 }
 
 void
 nsHttpRequestHead::SetVersion(nsHttpVersion version)
 {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mVersion = version;
@@ -133,17 +148,17 @@ nsHttpRequestHead::Path(nsACString &aPat
 {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     aPath = mPath.IsEmpty() ? mRequestURI : mPath;
 }
 
 void
 nsHttpRequestHead::SetHTTPS(bool val)
 {
-    ReentrantMonitorAutoEnter monk(mReentrantMonitor);
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mHTTPS = val;
 }
 
 void
 nsHttpRequestHead::Origin(nsACString &aOrigin)
 {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     aOrigin = mOrigin;
@@ -379,10 +394,15 @@ nsHttpRequestHead::Flatten(nsACString &b
         buf.AppendLiteral("1.0");
     }
 
     buf.AppendLiteral("\r\n");
 
     mHeaders.Flatten(buf, pruneProxyHeaders, false);
 }
 
+#ifdef MOZ_CRASHREPORTER
+#undef ReentrantMonitorAutoEnter
+#undef mon
+#endif
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpRequestHead.h
+++ b/netwerk/protocol/http/nsHttpRequestHead.h
@@ -115,14 +115,41 @@ private:
     bool              mHTTPS;
 
     // We are using ReentrantMonitor instead of a Mutex because VisitHeader
     // function calls nsIHttpHeaderVisitor::VisitHeader while under lock.
     ReentrantMonitor  mReentrantMonitor;
 
     // During VisitHeader we sould not allow cal to SetHeader.
     bool mInVisitHeaders;
+
+#ifdef MOZ_CRASHREPORTER
+    class DbgReentrantMonitorAutoEnter : ReentrantMonitorAutoEnter
+    {
+    public:
+        explicit DbgReentrantMonitorAutoEnter(nsHttpRequestHead& aInstance,
+                                              const char* aFunc)
+            : ReentrantMonitorAutoEnter(aInstance.mReentrantMonitor),
+              mInstance(aInstance),
+              mFunc(aFunc)
+        {
+            DbgCheck(true);
+        }
+        ~DbgReentrantMonitorAutoEnter(void)
+        {
+            DbgCheck(false);
+        }
+
+    private:
+        void DbgCheck(bool aIn);
+
+        nsHttpRequestHead& mInstance;
+        const char* mFunc;
+    };
+
+    bool mAnnotated;
+#endif
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpRequestHead_h__
--- a/security/sandbox/mac/Sandbox.h
+++ b/security/sandbox/mac/Sandbox.h
@@ -36,24 +36,26 @@ typedef struct _MacSandboxPluginInfo {
 } MacSandboxPluginInfo;
 
 typedef struct _MacSandboxInfo {
   _MacSandboxInfo()
     : type(MacSandboxType_Default), level(0) {}
   _MacSandboxInfo(const struct _MacSandboxInfo& other)
     : type(other.type), level(other.level), pluginInfo(other.pluginInfo),
       appPath(other.appPath), appBinaryPath(other.appBinaryPath),
-      appDir(other.appDir), appTempDir(other.appTempDir) {}
+      appDir(other.appDir), appTempDir(other.appTempDir),
+      profileDir(other.profileDir) {}
   MacSandboxType type;
   int32_t level;
   MacSandboxPluginInfo pluginInfo;
   std::string appPath;
   std::string appBinaryPath;
   std::string appDir;
   std::string appTempDir;
+  std::string profileDir;
 } MacSandboxInfo;
 
 namespace mozilla {
 
 bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage);
 
 } // namespace mozilla
 
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -152,16 +152,17 @@ static const char contentSandboxRules[] 
   "(version 1)\n"
   "\n"
   "(define sandbox-level %d)\n"
   "(define macosMinorVersion %d)\n"
   "(define appPath \"%s\")\n"
   "(define appBinaryPath \"%s\")\n"
   "(define appDir \"%s\")\n"
   "(define appTempDir \"%s\")\n"
+  "(define profileDir \"%s\")\n"
   "(define home-path \"%s\")\n"
   "\n"
   "; Allow read access to standard system paths.\n"
   "(allow file-read*\n"
   "  (require-all (file-mode #o0004)\n"
   "    (require-any (subpath \"/Library/Filesystems/NetFSPlugins\")\n"
   "      (subpath \"/System\")\n"
   "      (subpath \"/private/var/db/dyld\")\n"
@@ -227,16 +228,19 @@ static const char contentSandboxRules[] 
   "\n"
   "  (define (home-regex home-relative-regex)\n"
   "    (resolving-regex (string-append \"^\" (regex-quote home-path) home-relative-regex)))\n"
   "  (define (home-subpath home-relative-subpath)\n"
   "    (resolving-subpath (string-append home-path home-relative-subpath)))\n"
   "  (define (home-literal home-relative-literal)\n"
   "    (resolving-literal (string-append home-path home-relative-literal)))\n"
   "\n"
+  "  (define (profile-subpath profile-relative-subpath)\n"
+  "    (resolving-subpath (string-append profileDir profile-relative-subpath)))\n"
+  "\n"
   "  (define (container-regex container-relative-regex)\n"
   "    (resolving-regex (string-append \"^\" (regex-quote container-path) container-relative-regex)))\n"
   "  (define (container-subpath container-relative-subpath)\n"
   "    (resolving-subpath (string-append container-path container-relative-subpath)))\n"
   "  (define (container-literal container-relative-literal)\n"
   "    (resolving-literal (string-append container-path container-relative-literal)))\n"
   "\n"
   "  (define (var-folders-regex var-folders-relative-regex)\n"
@@ -366,26 +370,27 @@ static const char contentSandboxRules[] 
   "  (allow file-read*\n"
   "      (var-folders2-regex \"/com\\.apple\\.IconServices/\")\n"
   "      (var-folders2-regex \"/[^/]+\\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\\.j(s|ar)$\"))\n"
   "\n"
   "  (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
   "  (allow file-read*\n"
   "      (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
   "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
-  "      (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n"
-  "      (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n"
+  "      (profile-subpath \"/extensions\")\n"
+  "      (profile-subpath \"/weave\"))\n"
   "\n"
-  "; the following rules should be removed when printing and \n"
+  "; the following rules should be removed when printing and\n"
   "; opening a file from disk are brokered through the main process\n"
   "  (if\n"
   "    (< sandbox-level 2)\n"
   "    (allow file*\n"
-  "        (require-not\n"
-  "            (home-subpath \"/Library\")))\n"
+  "        (require-all\n"
+  "            (require-not (home-subpath \"/Library\"))\n"
+  "            (require-not (subpath profileDir))))\n"
   "    (allow file*\n"
   "        (require-all\n"
   "            (subpath home-path)\n"
   "            (require-not\n"
   "                (home-subpath \"/Library\")))))\n"
   "\n"
   "; printing\n"
   "  (allow authorization-right-obtain\n"
@@ -492,16 +497,17 @@ bool StartMacSandbox(MacSandboxInfo aInf
     MOZ_ASSERT(aInfo.level >= 1);
     if (aInfo.level >= 1) {
       asprintf(&profile, contentSandboxRules, aInfo.level,
                OSXVersion::OSXVersionMinor(),
                aInfo.appPath.c_str(),
                aInfo.appBinaryPath.c_str(),
                aInfo.appDir.c_str(),
                aInfo.appTempDir.c_str(),
+               aInfo.profileDir.c_str(),
                getenv("HOME"));
     } else {
       fprintf(stderr,
         "Content sandbox disabled due to sandbox level setting\n");
       return false;
     }
   }
   else {
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -37700,16 +37700,22 @@
             "url": "/web-animations/timing-model/animations/set-the-target-effect-of-an-animation.html"
           }
         ],
         "web-animations/timing-model/animations/updating-the-finished-state.html": [
           {
             "path": "web-animations/timing-model/animations/updating-the-finished-state.html",
             "url": "/web-animations/timing-model/animations/updating-the-finished-state.html"
           }
+        ],
+        "workers/Worker_ErrorEvent_error.htm": [
+          {
+            "path": "workers/Worker_ErrorEvent_error.htm",
+            "url": "/workers/Worker_ErrorEvent_error.htm"
+          }
         ]
       }
     },
     "reftest_nodes": {
       "html/semantics/grouping-content/the-ol-element/reversed-1a.html": [
         {
           "path": "html/semantics/grouping-content/the-ol-element/reversed-1a.html",
           "references": [
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/send-after-setting-document-domain.htm.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[send-after-setting-document-domain.htm]
-  type: testharness
-  expected: ERROR
-  [loading documents from the origin document.domain was set to should throw]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/send-authentication-basic-cors-not-enabled.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[send-authentication-basic-cors-not-enabled.htm]
-  type: testharness
-  [XMLHttpRequest: send() - "Basic" authenticated CORS requests with user name and password passed to open() (asserts failure)]
-    expected: FAIL
-
--- a/testing/web-platform/meta/XMLHttpRequest/send-non-same-origin.sub.htm.ini
+++ b/testing/web-platform/meta/XMLHttpRequest/send-non-same-origin.sub.htm.ini
@@ -1,17 +1,4 @@
 [send-non-same-origin.sub.htm]
   type: testharness
-  [XMLHttpRequest: send() - non same-origin (mailto:test@example.org)]
-    expected: FAIL
-
   [XMLHttpRequest: send() - non same-origin (tel:+31600000000)]
     expected: FAIL
-
-  [XMLHttpRequest: send() - non same-origin (http://www2.web-platform.test:8000/)]
-    expected: FAIL
-
-  [XMLHttpRequest: send() - non same-origin (javascript:alert('FAIL'))]
-    expected: FAIL
-
-  [XMLHttpRequest: send() - non same-origin (folder.txt)]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/send-redirect-bogus-sync.htm.ini
+++ /dev/null
@@ -1,17 +0,0 @@
-[send-redirect-bogus-sync.htm]
-  type: testharness
-  [XMLHttpRequest: send() - Redirects (bogus Location header; sync) (301: foobar://abcd)]
-    expected: FAIL
-
-  [XMLHttpRequest: send() - Redirects (bogus Location header; sync) (302: http://z)]
-    expected: FAIL
-
-  [XMLHttpRequest: send() - Redirects (bogus Location header; sync) (302: mailto:someone@example.org)]
-    expected: FAIL
-
-  [XMLHttpRequest: send() - Redirects (bogus Location header; sync) (303: http://z)]
-    expected: FAIL
-
-  [XMLHttpRequest: send() - Redirects (bogus Location header; sync) (303: tel:1234567890)]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/send-redirect-infinite-sync.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[send-redirect-infinite-sync.htm]
-  type: testharness
-  [XMLHttpRequest: send() - Redirects (infinite loop; sync) (301)]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/xmlhttprequest-network-error-sync.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[xmlhttprequest-network-error-sync.htm]
-  type: testharness
-  [XMLHttpRequest: members during network errors (sync)]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/tests/XMLHttpRequest/event-error.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>XMLHttpRequest Test: event - error</title>
-<link rel="author" title="Intel" href="http://www.intel.com">
-<meta name="assert" content="Check if event onerror is fired When the request has failed.">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<div id="log"></div>
-
-<script>
-
-async_test(function (t) {
-  var client = new XMLHttpRequest();
-  client.onerror = t.step_func(function(e) {
-    assert_true(e instanceof ProgressEvent);
-    assert_equals(e.type, "error");
-    t.done();
-  });
-
-  client.open("GET", "http://nonexistent-origin.{{host}}:{{ports[http][0]}}");
-  client.send("null");
-}, document.title);
-
-</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/resources/send-after-setting-document-domain-window-1.htm
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+  <head>
+    <title>XMLHttpRequest: send() with document.domain set: loading documents from original origin after setting document.domain</title>
+    <script src="send-after-setting-document-domain-window-helper.js"></script>
+    <link rel="help" href="https://xhr.spec.whatwg.org/#the-open()-method" data-tested-assertations="following::ol[1]/li[2]/ol[1]/li[3]" />
+  </head>
+  <body>
+    <script>
+      run_test(function() {
+        document.domain = document.domain; // this is not a noop, it does actually change the security context
+        var client = new XMLHttpRequest();
+        client.open("GET", "status.py?content=hello", false);
+        client.send(null);
+        assert_equals(client.responseText, "hello");
+        document.domain = document.domain.replace(/^\w+\./, "");
+        client.open("GET", "status.py?content=hello2", false);
+        client.send(null);
+        assert_equals(client.responseText, "hello2");
+      }, "loading documents from original origin after setting document.domain");
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/resources/send-after-setting-document-domain-window-2.htm
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+  <head>
+    <title>XMLHttpRequest: send() with document.domain set: loading documents from the origin document.domain was set to should throw</title>
+    <script src="send-after-setting-document-domain-window-helper.js"></script>
+    <link rel="help" href="https://xhr.spec.whatwg.org/#the-open()-method" data-tested-assertations="following::ol[1]/li[2]/ol[1]/li[3]" />
+  </head>
+  <body>
+    <script>
+      run_test(function() {
+        document.domain = document.domain.replace(/^\w+\./, "");
+        var client = new XMLHttpRequest();
+        client.open("GET", location.protocol + "//" + document.domain + location.pathname.replace(/[^\/]*$/, "") + "status.py?content=hello3", false);
+        assert_throws("NetworkError", function() {
+          client.send(null);
+        });
+      }, "loading documents from the origin document.domain was set to should throw");
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/resources/send-after-setting-document-domain-window-helper.js
@@ -0,0 +1,29 @@
+function assert_equals(value, expected) {
+  if (value != expected) {
+    throw "Got wrong value.\nExpected '" + expected + "',\ngot '" + value + "'";
+  }
+}
+
+function assert_throws(expected_exc, func) {
+  try {
+    func.call(this);
+  } catch(e) {
+    var actual = e.name || e.type;
+    if (actual != expected_exc) {
+      throw "Got wrong exception.\nExpected '" + expected_exc + "',\ngot '" + actual + "'.";
+    }
+    return;
+  }
+  throw "Expected exception, but none was thrown";
+}
+
+function run_test(test, name) {
+  var result = {passed: true, message: null, name: name};
+  try {
+    test();
+  } catch(e) {
+    result.passed = false;
+    result.message = e + "";
+  }
+  opener.postMessage(result, "*");
+}
--- a/testing/web-platform/tests/XMLHttpRequest/send-after-setting-document-domain.htm
+++ b/testing/web-platform/tests/XMLHttpRequest/send-after-setting-document-domain.htm
@@ -1,38 +1,39 @@
 <!doctype html>
 <html>
   <head>
     <title>XMLHttpRequest: send() with document.domain set</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
-    <!-- The spec doesn't seem to explicitly cover this case (as of June 2013) -->
     <link rel="help" href="https://xhr.spec.whatwg.org/#the-open()-method" data-tested-assertations="following::ol[1]/li[2]/ol[1]/li[3]" />
   </head>
   <body>
     <div id="log"></div>
     <script>
-      // first make sure we actually run off a domain with at least three parts, in order to be able to shorten it..
-      if (location.hostname.split(/\./).length < 3) {
-        location.href = location.protocol+'//www2.'+location.host+location.pathname
-      }
+      var test_base_url = location.protocol+'//www2.'+location.host+"/XMLHttpRequest/resources/",
+          test_windows = [
+            window.open(test_base_url + "send-after-setting-document-domain-window-1.htm"),
+            window.open(test_base_url + "send-after-setting-document-domain-window-2.htm"),
+          ],
+          num_tests_left = test_windows.length;
 
-      test(function() {
-        document.domain = document.domain // this is not a noop, it does actually change the security context
-        var client = new XMLHttpRequest()
-        client.open("GET", "resources/status.py?content=hello", false)
-        client.send(null)
-        assert_equals(client.responseText, "hello")
-        document.domain = document.domain.replace(/^\w+\./, '')
-        client.open("GET", "resources/status.py?content=hello2", false)
-        client.send(null)
-        assert_equals(client.responseText, "hello2")
-      }, "loading documents from original origin after setting document.domain")
-      // try to load a document from the origin document.domain was set to
-      test(function () {
-        var client = new XMLHttpRequest()
-        client.open("GET", location.protocol + '//' + document.domain + location.pathname.replace(/[^\/]*$/, '') + "resources/status.py?content=hello3", false)
-        // AFAIK this should throw
-        assert_throws('NetworkError', function(){client.send(null)})
-      }, "loading documents from the origin document.domain was set to should throw")
+      async_test(function(wrapper_test) {
+        window.addEventListener("message", function(evt) {
+          // run a shadow test that just forwards the results
+          async_test(function(test) {
+            assert_true(evt.data.passed, evt.data.message);
+            test.done();
+          }, evt.data.name);
+
+          // after last result comes in, close all test
+          // windows and complete the wrapper test.
+          if (--num_tests_left == 0) {
+            for (var i=0; i<test_windows.length; ++i) {
+              test_windows[i].close();
+            }
+            wrapper_test.done();
+          }
+        }, false);
+      }, "All tests ran");
     </script>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/workers/Worker_ErrorEvent_error.htm
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+var t1 = async_test("Error handler outside the worker should not see the error value");
+var t2 = async_test("Error handlers inside a worker should see the error value");
+
+test(function() {
+  var worker = new Worker("support/ErrorEvent-error.js");
+  worker.onerror = t1.step_func_done(function(e) {
+    assert_true(/hello/.test(e.message));
+    assert_equals(e.error, null);
+  });
+
+  var messages = 0;
+  worker.onmessage = t2.step_func(function(e) {
+    ++messages;
+    var data = e.data;
+    assert_true(data.source == "onerror" ||
+                data.source == "event listener");
+    assert_equals(data.value, "hello");
+    if (messages == 2) {
+      t2.done();
+    }
+  });
+});
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/workers/support/ErrorEvent-error.js
@@ -0,0 +1,9 @@
+onerror = function(message, location, line, col, error) {
+  postMessage({ source: "onerror", value: error });
+}
+
+addEventListener("error", function(e) {
+  postMessage({ source: "event listener", value: e.error });
+});
+
+throw "hello";
--- a/toolkit/modules/SelectParentHelper.jsm
+++ b/toolkit/modules/SelectParentHelper.jsm
@@ -29,33 +29,38 @@ this.SelectParentHelper = {
     menulist.hidden = false;
     currentBrowser = browser;
     closedWithEnter = false;
     this._registerListeners(browser, menulist.menupopup);
 
     let win = browser.ownerDocument.defaultView;
 
     // Set the maximum height to show exactly MAX_ROWS items.
-    let firstItem = menulist.getItemAtIndex(0);
+    let menupopup = menulist.menupopup;
+    let firstItem = menupopup.firstChild;
+    while (firstItem && firstItem.hidden) {
+      firstItem = firstItem.nextSibling;
+    }
+
     if (firstItem) {
       let itemHeight = firstItem.getBoundingClientRect().height;
 
       // Include the padding and border on the popup.
-      let cs = win.getComputedStyle(menulist.menupopup);
+      let cs = win.getComputedStyle(menupopup);
       let bpHeight = parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth) +
                      parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
-      menulist.menupopup.style.maxHeight = (itemHeight * MAX_ROWS + bpHeight) + "px";
+      menupopup.style.maxHeight = (itemHeight * MAX_ROWS + bpHeight) + "px";
     }
 
     let constraintRect = browser.getBoundingClientRect();
     constraintRect = new win.DOMRect(constraintRect.left + win.mozInnerScreenX,
                                      constraintRect.top + win.mozInnerScreenY,
                                      constraintRect.width, constraintRect.height);
-    menulist.menupopup.setConstraintRect(constraintRect);
-    menulist.menupopup.openPopupAtScreenRect("after_start", rect.left, rect.top, rect.width, rect.height, false, false);
+    menupopup.setConstraintRect(constraintRect);
+    menupopup.openPopupAtScreenRect("after_start", rect.left, rect.top, rect.width, rect.height, false, false);
   },
 
   hide: function(menulist, browser) {
     if (currentBrowser == browser) {
       menulist.menupopup.hidePopup();
     }
   },
 
@@ -136,17 +141,17 @@ this.SelectParentHelper = {
     browser.ownerDocument.defaultView.removeEventListener("keydown", this, true);
     browser.ownerDocument.defaultView.removeEventListener("fullscreen", this, true);
     browser.messageManager.removeMessageListener("Forms:UpdateDropDown", this);
   },
 
 };
 
 function populateChildren(menulist, options, selectedIndex, zoom,
-                          isInGroup = false, isGroupDisabled = false, adjustedTextSize = -1) {
+                          parentElement = null, isGroupDisabled = false, adjustedTextSize = -1) {
   let element = menulist.menupopup;
 
   // -1 just means we haven't calculated it yet. When we recurse through this function
   // we will pass in adjustedTextSize to save on recalculations.
   if (adjustedTextSize == -1) {
     let win = element.ownerDocument.defaultView;
 
     // Grab the computed text size and multiply it by the remote browser's fullZoom to ensure
@@ -158,30 +163,30 @@ function populateChildren(menulist, opti
 
   for (let option of options) {
     let isOptGroup = (option.tagName == 'OPTGROUP');
     let item = element.ownerDocument.createElement(isOptGroup ? "menucaption" : "menuitem");
 
     item.setAttribute("label", option.textContent);
     item.style.direction = option.textDirection;
     item.style.fontSize = adjustedTextSize;
-    item.style.display = option.display;
+    item.hidden = option.display == "none" || (parentElement && parentElement.hidden);
     item.setAttribute("tooltiptext", option.tooltip);
 
     element.appendChild(item);
 
     // A disabled optgroup disables all of its child options.
     let isDisabled = isGroupDisabled || option.disabled;
     if (isDisabled) {
       item.setAttribute("disabled", "true");
     }
 
     if (isOptGroup) {
       populateChildren(menulist, option.children, selectedIndex, zoom,
-                       true, isDisabled, adjustedTextSize);
+                       item, isDisabled, adjustedTextSize);
     } else {
       if (option.index == selectedIndex) {
         // We expect the parent element of the popup to be a <xul:menulist> that
         // has the popuponly attribute set to "true". This is necessary in order
         // for a <xul:menupopup> to act like a proper <html:select> dropdown, as
         // the <xul:menulist> does things like remember state and set the
         // _moz-menuactive attribute on the selected <xul:menuitem>.
         menulist.selectedItem = item;
@@ -191,14 +196,14 @@ function populateChildren(menulist, opti
         // may have been removed from the selected item. Since that's normally only
         // set for the initially selected on popupshowing for the menulist, and we
         // don't want to close and re-open the popup, we manually set it here.
         menulist.menuBoxObject.activeChild = item;
       }
 
       item.setAttribute("value", option.index);
 
-      if (isInGroup) {
+      if (parentElement) {
         item.classList.add("contentSelectDropdown-ingroup")
       }
     }
   }
 }
--- a/toolkit/mozapps/update/common/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -78,18 +78,19 @@
 #define WRITE_ERROR_PATCH_FILE 64
 #define WRITE_ERROR_APPLY_DIR_PATH 65
 #define WRITE_ERROR_CALLBACK_PATH 66
 #define WRITE_ERROR_FILE_ACCESS_DENIED 67
 #define WRITE_ERROR_DIR_ACCESS_DENIED 68
 #define WRITE_ERROR_DELETE_BACKUP 69
 #define WRITE_ERROR_EXTRACT 70
 #define REMOVE_FILE_SPEC_ERROR 71
-#define INVALID_STAGED_PARENT_ERROR 72
+#define INVALID_APPLYTO_DIR_STAGED_ERROR 72
 #define LOCK_ERROR_PATCH_FILE 73
+#define INVALID_APPLYTO_DIR_ERROR 74
 
 // Error codes 80 through 99 are reserved for nsUpdateService.js
 
 // The following error codes are only used by updater.exe
 // when a fallback key exists for tests.
 #define FALLBACKKEY_UNKNOWN_ERROR 100
 #define FALLBACKKEY_REGPATH_ERROR 101
 #define FALLBACKKEY_NOKEY_ERROR 102
--- a/toolkit/mozapps/update/tests/chrome/utils.js
+++ b/toolkit/mozapps/update/tests/chrome/utils.js
@@ -616,17 +616,17 @@ function verifyTestsRan() {
 }
 
 /**
  * Creates a backup of files the tests need to modify so they can be restored to
  * the original file when the test has finished and then modifies the files.
  */
 function setupFiles() {
   // Backup the updater-settings.ini file if it exists by moving it.
-  let baseAppDir = getAppBaseDir();
+  let baseAppDir = getGREDir();
   let updateSettingsIni = baseAppDir.clone();
   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
   if (updateSettingsIni.exists()) {
     updateSettingsIni.moveTo(baseAppDir, FILE_UPDATE_SETTINGS_INI_BAK);
   }
   updateSettingsIni = baseAppDir.clone();
   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
   writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
@@ -771,17 +771,17 @@ function setupPrefs() {
   Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
 }
 
 /**
  * Restores files that were backed up for the tests and general file cleanup.
  */
 function resetFiles() {
   // Restore the backed up updater-settings.ini if it exists.
-  let baseAppDir = getAppBaseDir();
+  let baseAppDir = getGREDir();
   let updateSettingsIni = baseAppDir.clone();
   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI_BAK);
   if (updateSettingsIni.exists()) {
     updateSettingsIni.moveTo(baseAppDir, FILE_UPDATE_SETTINGS_INI);
   }
 
   // Not being able to remove the "updated" directory will not adversely affect
   // subsequent tests so wrap it in a try block and don't test whether its
--- a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js
+++ b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js
@@ -30,37 +30,43 @@ const STATE_PENDING         = "pending";
 const STATE_PENDING_SVC     = "pending-service";
 const STATE_APPLYING        = "applying";
 const STATE_APPLIED         = "applied";
 const STATE_APPLIED_SVC     = "applied-service";
 const STATE_SUCCEEDED       = "succeeded";
 const STATE_DOWNLOAD_FAILED = "download-failed";
 const STATE_FAILED          = "failed";
 
-const LOADSOURCE_ERROR_WRONG_SIZE = 2;
-const CRC_ERROR                   = 4;
-const READ_ERROR                  = 6;
-const WRITE_ERROR                 = 7;
-const MAR_CHANNEL_MISMATCH_ERROR  = 22;
-const VERSION_DOWNGRADE_ERROR     = 23;
+const LOADSOURCE_ERROR_WRONG_SIZE      = 2;
+const CRC_ERROR                        = 4;
+const READ_ERROR                       = 6;
+const WRITE_ERROR                      = 7;
+const MAR_CHANNEL_MISMATCH_ERROR       = 22;
+const VERSION_DOWNGRADE_ERROR          = 23;
+const INVALID_APPLYTO_DIR_STAGED_ERROR = 72;
+const INVALID_APPLYTO_DIR_ERROR        = 74;
 
 const STATE_FAILED_DELIMETER = ": ";
 
 const STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE =
   STATE_FAILED + STATE_FAILED_DELIMETER + LOADSOURCE_ERROR_WRONG_SIZE;
 const STATE_FAILED_CRC_ERROR =
   STATE_FAILED + STATE_FAILED_DELIMETER + CRC_ERROR;
 const STATE_FAILED_READ_ERROR =
   STATE_FAILED + STATE_FAILED_DELIMETER + READ_ERROR;
 const STATE_FAILED_WRITE_ERROR =
   STATE_FAILED + STATE_FAILED_DELIMETER + WRITE_ERROR;
 const STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR =
   STATE_FAILED + STATE_FAILED_DELIMETER + MAR_CHANNEL_MISMATCH_ERROR;
 const STATE_FAILED_VERSION_DOWNGRADE_ERROR =
   STATE_FAILED + STATE_FAILED_DELIMETER + VERSION_DOWNGRADE_ERROR;
+const STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR =
+  STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_FAILED_INVALID_APPLYTO_DIR_ERROR =
+  STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_APPLYTO_DIR_ERROR;
 
 /**
  * Constructs a string representing a remote update xml file.
  *
  * @param  aUpdates
  *         The string representing the update elements.
  * @return The string representing a remote update xml file.
  */
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -150,16 +150,18 @@ var gStatusResult;
 var gProcess;
 var gAppTimer;
 var gHandle;
 
 var gGREDirOrig;
 var gGREBinDirOrig;
 var gAppDirOrig;
 
+var gApplyToDirOverride;
+
 var gServiceLaunchedCallbackLog = null;
 var gServiceLaunchedCallbackArgs = null;
 
 // Variables are used instead of contants so tests can override these values if
 // necessary.
 var gCallbackBinFile = "callback_app" + BIN_SUFFIX;
 var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
 var gPostUpdateBinFile = "postup_app" + BIN_SUFFIX;
@@ -1133,16 +1135,28 @@ function getAppVersion() {
             MSG_SHOULD_EXIST + getMsgPath(iniFile.path));
   let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
                   getService(Ci.nsIINIParserFactory).
                   createINIParser(iniFile);
   return iniParser.getString("App", "Version");
 }
 
 /**
+ * Override the apply-to directory parameter to be passed to the updater.
+ * This ought to cause the updater to fail when using any value that isn't the
+ * default, automatically computed one.
+ *
+ * @param dir
+ *        Complete string to use as the apply-to directory parameter.
+ */
+function overrideApplyToDir(dir) {
+  gApplyToDirOverride = dir;
+}
+
+/**
  * Helper function for getting the relative path to the directory where the
  * application binary is located (e.g. <test_file_leafname>/dir.app/).
  *
  * Note: The dir.app subdirectory under <test_file_leafname> is needed for
  *       platforms other than Mac OS X so the tests can run in parallel due to
  *       update staging creating a lock file named moz_update_in_progress.lock in
  *       the parent directory of the installation directory.
  *
@@ -1701,20 +1715,20 @@ function runUpdateUsingUpdater(aExpected
 
   let callbackApp = getApplyDirFile(DIR_RESOURCES + gCallbackBinFile);
   callbackApp.permissions = PERMS_DIRECTORY;
 
   setAppBundleModTime();
 
   let args = [updatesDirPath, applyToDirPath];
   if (aSwitchApp) {
-    args[2] = stageDirPath;
+    args[2] = gApplyToDirOverride || stageDirPath;
     args[3] = "0/replace";
   } else {
-    args[2] = applyToDirPath;
+    args[2] = gApplyToDirOverride || applyToDirPath;
     args[3] = "0";
   }
   args = args.concat([callbackApp.parent.path, callbackApp.path]);
   args = args.concat(gCallbackArgs);
   debugDump("running the updater: " + updateBin.path + " " + args.join(" "));
 
   // See bug 1279108.
   // nsIProcess doesn't have an API to pass a separate environment to the
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marWrongApplyToDirFailure_win.js
@@ -0,0 +1,39 @@
+/* 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/.
+ */
+
+/* Test trying to use an apply-to directory different from the install
+ * directory, which should fail.
+ */
+
+function run_test() {
+  if (!setupTestCommon()) {
+    return;
+  }
+  gTestFiles = gTestFilesCompleteSuccess;
+  gTestDirs = gTestDirsCompleteSuccess;
+  setTestFilesAndDirsForFailure();
+  setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+  overrideApplyToDir(getApplyDirPath() + "/../NoSuchDir");
+  // If execv is used the updater process will turn into the callback process
+  // and the updater's return code will be that of the callback process.
+  runUpdateUsingUpdater(STATE_FAILED_INVALID_APPLYTO_DIR_ERROR, false,
+                        (USE_EXECV ? 0 : 1));
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+  standardInit();
+  checkPostUpdateRunningFile(false);
+  checkFilesAfterUpdateFailure(getApplyDirFile);
+  waitForFilesInUse();
+}
--- a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
@@ -99,8 +99,11 @@ reason = Windows only test and bug 12919
 skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
 reason = Windows only test and bug 1291985
 [marAppApplyUpdateSuccess.js]
 skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2') || toolkit == 'gonk'
 reason = bug 1291985 and bug 1164150
 [marAppApplyUpdateStageSuccess.js]
 skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2') || toolkit == 'gonk'
 reason = bug 1291985 and bug 1164150
+[marWrongApplyToDirFailure_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -54,16 +54,17 @@
 
 #include "updatelogging.h"
 #ifdef XP_MACOSX
 #include "updaterfileutils_osx.h"
 #endif // XP_MACOSX
 
 #include "mozilla/Compiler.h"
 #include "mozilla/Types.h"
+#include "mozilla/UniquePtr.h"
 
 // Amount of the progress bar to use in each of the 3 update stages,
 // should total 100.0.
 #define PROGRESS_PREPARE_SIZE 20.0f
 #define PROGRESS_EXECUTE_SIZE 75.0f
 #define PROGRESS_FINISH_SIZE   5.0f
 
 // Amount of time in ms to wait for the parent process to close
@@ -315,16 +316,17 @@ static bool sReplaceRequest = false;
 static bool sUsingService = false;
 static bool sIsOSUpdate = false;
 
 #ifdef XP_WIN
 // The current working directory specified in the command line.
 static NS_tchar* gDestPath;
 static NS_tchar gCallbackRelPath[MAXPATHLEN];
 static NS_tchar gCallbackBackupPath[MAXPATHLEN];
+static NS_tchar gDeleteDirPath[MAXPATHLEN];
 #endif
 
 static const NS_tchar kWhitespace[] = NS_T(" \t");
 static const NS_tchar kNL[] = NS_T("\r\n");
 static const NS_tchar kQuote[] = NS_T("\"");
 
 static inline size_t
 mmin(size_t a, size_t b)
@@ -375,44 +377,76 @@ mstrtok(const NS_tchar *delims, NS_tchar
 
 static bool
 EnvHasValue(const char *name)
 {
   const char *val = getenv(name);
   return (val && *val);
 }
 
-#ifdef XP_WIN
 /**
- * Coverts a relative update path to a full path for Windows.
+ * Coverts a relative update path to a full path.
  *
  * @param  relpath
  *         The relative path to convert to a full path.
  * @return valid filesystem full path or nullptr if memory allocation fails.
  */
 static NS_tchar*
 get_full_path(const NS_tchar *relpath)
 {
-  size_t lendestpath = NS_tstrlen(gDestPath);
+  NS_tchar *destpath = sStagedUpdate ? gWorkingDirPath : gInstallDirPath;
+  size_t lendestpath = NS_tstrlen(destpath);
   size_t lenrelpath = NS_tstrlen(relpath);
-  NS_tchar *s = (NS_tchar *) malloc((lendestpath + lenrelpath + 1) * sizeof(NS_tchar));
-  if (!s)
+  NS_tchar *s = new NS_tchar[lendestpath + lenrelpath + 2];
+  if (!s) {
     return nullptr;
+  }
 
   NS_tchar *c = s;
 
-  NS_tstrcpy(c, gDestPath);
+  NS_tstrcpy(c, destpath);
   c += lendestpath;
+  NS_tstrcat(c, NS_T("/"));
+  c++;
+
   NS_tstrcat(c, relpath);
   c += lenrelpath;
   *c = NS_T('\0');
-  c++;
   return s;
 }
+
+/**
+ * Converts a full update path into a relative path; reverses get_full_path.
+ *
+ * @param  fullpath
+ *         The absolute path to convert into a relative path.
+ * return pointer to the location within fullpath where the relative path starts
+ *        or fullpath itself if it already looks relative.
+ */
+static const NS_tchar*
+get_relative_path(const NS_tchar *fullpath)
+{
+  // If the path isn't absolute, just return it as-is.
+#ifdef XP_WIN
+  if (fullpath[1] != ':' && fullpath[2] != '\\') {
+#else
+  if (fullpath[0] != '/') {
 #endif
+    return fullpath;
+  }
+
+  NS_tchar *prefix = sStagedUpdate ? gWorkingDirPath : gInstallDirPath;
+
+  // If the path isn't long enough to be absolute, return it as-is.
+  if (NS_tstrlen(fullpath) <= NS_tstrlen(prefix)) {
+    return fullpath;
+  }
+
+  return fullpath + NS_tstrlen(prefix) + 1;
+}
 
 /**
  * Gets the platform specific path and performs simple checks to the path. If
  * the path checks don't pass nullptr will be returned.
  *
  * @param  line
  *         The line from the manifest that contains the path.
  * @param  isdir
@@ -963,81 +997,90 @@ static int backup_create(const NS_tchar 
   NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
                NS_T("%s") BACKUP_EXT, path);
 
   return rename_file(path, backup);
 }
 
 // Rename the backup of the specified file that was created by renaming it back
 // to the original file.
-static int backup_restore(const NS_tchar *path)
+static int backup_restore(const NS_tchar *path, const NS_tchar *relPath)
 {
   NS_tchar backup[MAXPATHLEN];
   NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
                NS_T("%s") BACKUP_EXT, path);
 
+  NS_tchar relBackup[MAXPATHLEN];
+  NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
+    NS_T("%s") BACKUP_EXT, relPath);
+
   if (NS_taccess(backup, F_OK)) {
-    LOG(("backup_restore: backup file doesn't exist: " LOG_S, backup));
+    LOG(("backup_restore: backup file doesn't exist: " LOG_S, relBackup));
     return OK;
   }
 
   return rename_file(backup, path);
 }
 
 // Discard the backup of the specified file that was created by renaming it.
-static int backup_discard(const NS_tchar *path)
+static int backup_discard(const NS_tchar *path, const NS_tchar *relPath)
 {
   NS_tchar backup[MAXPATHLEN];
   NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
                NS_T("%s") BACKUP_EXT, path);
 
+  NS_tchar relBackup[MAXPATHLEN];
+  NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
+    NS_T("%s") BACKUP_EXT, relPath);
+
   // Nothing to discard
   if (NS_taccess(backup, F_OK)) {
     return OK;
   }
 
   int rv = ensure_remove(backup);
 #if defined(XP_WIN)
   if (rv && !sStagedUpdate && !sReplaceRequest) {
-    LOG(("backup_discard: unable to remove: " LOG_S, backup));
+    LOG(("backup_discard: unable to remove: " LOG_S, relBackup));
     NS_tchar path[MAXPATHLEN];
-    GetTempFileNameW(DELETE_DIR, L"moz", 0, path);
+    GetTempFileNameW(gDeleteDirPath, L"moz", 0, path);
     if (rename_file(backup, path)) {
       LOG(("backup_discard: failed to rename file:" LOG_S ", dst:" LOG_S,
-           backup, path));
+           relBackup, relPath));
       return WRITE_ERROR_DELETE_BACKUP;
     }
     // The MoveFileEx call to remove the file on OS reboot will fail if the
     // process doesn't have write access to the HKEY_LOCAL_MACHINE registry key
     // but this is ok since the installer / uninstaller will delete the
     // directory containing the file along with its contents after an update is
     // applied, on reinstall, and on uninstall.
     if (MoveFileEx(path, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
       LOG(("backup_discard: file renamed and will be removed on OS " \
-           "reboot: " LOG_S, path));
+           "reboot: " LOG_S, relPath));
     } else {
       LOG(("backup_discard: failed to schedule OS reboot removal of " \
-           "file: " LOG_S, path));
+           "file: " LOG_S, relPath));
     }
   }
 #else
   if (rv)
     return WRITE_ERROR_DELETE_BACKUP;
 #endif
 
   return OK;
 }
 
 // Helper function for post-processing a temporary backup.
-static void backup_finish(const NS_tchar *path, int status)
+static void backup_finish(const NS_tchar *path, const NS_tchar *relPath,
+                          int status)
 {
   if (status == OK)
-    backup_discard(path);
+    backup_discard(path, relPath);
   else
-    backup_restore(path);
+    backup_restore(path, relPath);
 }
 
 //-----------------------------------------------------------------------------
 
 static int DoUpdate();
 
 class Action
 {
@@ -1065,71 +1108,81 @@ private:
   Action* mNext;
 
   friend class ActionList;
 };
 
 class RemoveFile : public Action
 {
 public:
-  RemoveFile() : mFile(nullptr), mSkip(0) { }
+  RemoveFile() : mSkip(0) { }
 
   int Parse(NS_tchar *line);
   int Prepare();
   int Execute();
   void Finish(int status);
 
 private:
-  const NS_tchar *mFile;
+  mozilla::UniquePtr<NS_tchar[]> mFile;
+  mozilla::UniquePtr<NS_tchar[]> mRelPath;
   int mSkip;
 };
 
 int
 RemoveFile::Parse(NS_tchar *line)
 {
   // format "<deadfile>"
 
-  mFile = get_valid_path(&line);
-  if (!mFile)
+  NS_tchar * validPath = get_valid_path(&line);
+  if (!validPath) {
     return PARSE_ERROR;
+  }
+
+  mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
+  NS_tstrcpy(mRelPath.get(), validPath);
+
+  mFile.reset(get_full_path(validPath));
+  if (!mFile) {
+    return PARSE_ERROR;
+  }
 
   return OK;
 }
 
 int
 RemoveFile::Prepare()
 {
   // Skip the file if it already doesn't exist.
-  int rv = NS_taccess(mFile, F_OK);
+  int rv = NS_taccess(mFile.get(), F_OK);
   if (rv) {
     mSkip = 1;
     mProgressCost = 0;
     return OK;
   }
 
-  LOG(("PREPARE REMOVEFILE " LOG_S, mFile));
+  LOG(("PREPARE REMOVEFILE " LOG_S, mRelPath.get()));
 
   // Make sure that we're actually a file...
   struct NS_tstat_t fileInfo;
-  rv = NS_tstat(mFile, &fileInfo);
+  rv = NS_tstat(mFile.get(), &fileInfo);
   if (rv) {
-    LOG(("failed to read file status info: " LOG_S ", err: %d", mFile,
+    LOG(("failed to read file status info: " LOG_S ", err: %d", mFile.get(),
          errno));
     return READ_ERROR;
   }
 
   if (!S_ISREG(fileInfo.st_mode)) {
-    LOG(("path present, but not a file: " LOG_S, mFile));
+    LOG(("path present, but not a file: " LOG_S, mFile.get()));
     return DELETE_ERROR_EXPECTED_FILE;
   }
 
-  NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile, NS_T('/'));
+  NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile.get(), NS_T('/'));
   if (slash) {
     *slash = NS_T('\0');
-    rv = NS_taccess(mFile, W_OK);
+    rv = NS_taccess(mFile.get(), W_OK);
     *slash = NS_T('/');
   } else {
     rv = NS_taccess(NS_T("."), W_OK);
   }
 
   if (rv) {
     LOG(("access failed: %d", errno));
     return WRITE_ERROR_FILE_ACCESS_DENIED;
@@ -1139,259 +1192,278 @@ RemoveFile::Prepare()
 }
 
 int
 RemoveFile::Execute()
 {
   if (mSkip)
     return OK;
 
-  LOG(("EXECUTE REMOVEFILE " LOG_S, mFile));
+  LOG(("EXECUTE REMOVEFILE " LOG_S, mRelPath.get()));
 
   // The file is checked for existence here and in Prepare since it might have
   // been removed by a separate instruction: bug 311099.
-  int rv = NS_taccess(mFile, F_OK);
+  int rv = NS_taccess(mFile.get(), F_OK);
   if (rv) {
     LOG(("file cannot be removed because it does not exist; skipping"));
     mSkip = 1;
     return OK;
   }
 
   // Rename the old file. It will be removed in Finish.
-  rv = backup_create(mFile);
+  rv = backup_create(mFile.get());
   if (rv) {
     LOG(("backup_create failed: %d", rv));
     return rv;
   }
 
   return OK;
 }
 
 void
 RemoveFile::Finish(int status)
 {
   if (mSkip)
     return;
 
-  LOG(("FINISH REMOVEFILE " LOG_S, mFile));
-
-  backup_finish(mFile, status);
+  LOG(("FINISH REMOVEFILE " LOG_S, mRelPath.get()));
+
+  backup_finish(mFile.get(), mRelPath.get(), status);
 }
 
 class RemoveDir : public Action
 {
 public:
-  RemoveDir() : mDir(nullptr), mSkip(0) { }
+  RemoveDir() : mSkip(0) { }
 
   virtual int Parse(NS_tchar *line);
   virtual int Prepare(); // check that the source dir exists
   virtual int Execute();
   virtual void Finish(int status);
 
 private:
-  const NS_tchar *mDir;
+  mozilla::UniquePtr<NS_tchar[]> mDir;
+  mozilla::UniquePtr<NS_tchar[]> mRelPath;
   int mSkip;
 };
 
 int
 RemoveDir::Parse(NS_tchar *line)
 {
   // format "<deaddir>/"
 
-  mDir = get_valid_path(&line, true);
-  if (!mDir)
+  NS_tchar * validPath = get_valid_path(&line, true);
+  if (!validPath) {
     return PARSE_ERROR;
+  }
+
+  mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
+  NS_tstrcpy(mRelPath.get(), validPath);
+
+  mDir.reset(get_full_path(validPath));
+  if (!mDir) {
+    return PARSE_ERROR;
+  }
 
   return OK;
 }
 
 int
 RemoveDir::Prepare()
 {
   // We expect the directory to exist if we are to remove it.
-  int rv = NS_taccess(mDir, F_OK);
+  int rv = NS_taccess(mDir.get(), F_OK);
   if (rv) {
     mSkip = 1;
     mProgressCost = 0;
     return OK;
   }
 
-  LOG(("PREPARE REMOVEDIR " LOG_S "/", mDir));
+  LOG(("PREPARE REMOVEDIR " LOG_S "/", mRelPath.get()));
 
   // Make sure that we're actually a dir.
   struct NS_tstat_t dirInfo;
-  rv = NS_tstat(mDir, &dirInfo);
+  rv = NS_tstat(mDir.get(), &dirInfo);
   if (rv) {
-    LOG(("failed to read directory status info: " LOG_S ", err: %d", mDir,
+    LOG(("failed to read directory status info: " LOG_S ", err: %d", mRelPath.get(),
          errno));
     return READ_ERROR;
   }
 
   if (!S_ISDIR(dirInfo.st_mode)) {
-    LOG(("path present, but not a directory: " LOG_S, mDir));
+    LOG(("path present, but not a directory: " LOG_S, mRelPath.get()));
     return DELETE_ERROR_EXPECTED_DIR;
   }
 
-  rv = NS_taccess(mDir, W_OK);
+  rv = NS_taccess(mDir.get(), W_OK);
   if (rv) {
     LOG(("access failed: %d, %d", rv, errno));
     return WRITE_ERROR_DIR_ACCESS_DENIED;
   }
 
   return OK;
 }
 
 int
 RemoveDir::Execute()
 {
   if (mSkip)
     return OK;
 
-  LOG(("EXECUTE REMOVEDIR " LOG_S "/", mDir));
+  LOG(("EXECUTE REMOVEDIR " LOG_S "/", mRelPath.get()));
 
   // The directory is checked for existence at every step since it might have
   // been removed by a separate instruction: bug 311099.
-  int rv = NS_taccess(mDir, F_OK);
+  int rv = NS_taccess(mDir.get(), F_OK);
   if (rv) {
     LOG(("directory no longer exists; skipping"));
     mSkip = 1;
   }
 
   return OK;
 }
 
 void
 RemoveDir::Finish(int status)
 {
   if (mSkip || status != OK)
     return;
 
-  LOG(("FINISH REMOVEDIR " LOG_S "/", mDir));
+  LOG(("FINISH REMOVEDIR " LOG_S "/", mRelPath.get()));
 
   // The directory is checked for existence at every step since it might have
   // been removed by a separate instruction: bug 311099.
-  int rv = NS_taccess(mDir, F_OK);
+  int rv = NS_taccess(mDir.get(), F_OK);
   if (rv) {
     LOG(("directory no longer exists; skipping"));
     return;
   }
 
 
   if (status == OK) {
-    if (NS_trmdir(mDir)) {
+    if (NS_trmdir(mDir.get())) {
       LOG(("non-fatal error removing directory: " LOG_S "/, rv: %d, err: %d",
-           mDir, rv, errno));
+           mRelPath.get(), rv, errno));
     }
   }
 }
 
 class AddFile : public Action
 {
 public:
-  AddFile() : mFile(nullptr)
-            , mAdded(false)
-            { }
+  AddFile() : mAdded(false) { }
 
   virtual int Parse(NS_tchar *line);
   virtual int Prepare();
   virtual int Execute();
   virtual void Finish(int status);
 
 private:
-  const NS_tchar *mFile;
+  mozilla::UniquePtr<NS_tchar[]> mFile;
+  mozilla::UniquePtr<NS_tchar[]> mRelPath;
   bool mAdded;
 };
 
 int
 AddFile::Parse(NS_tchar *line)
 {
   // format "<newfile>"
 
-  mFile = get_valid_path(&line);
-  if (!mFile)
+  NS_tchar * validPath = get_valid_path(&line);
+  if (!validPath) {
     return PARSE_ERROR;
+  }
+
+  mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
+  NS_tstrcpy(mRelPath.get(), validPath);
+
+  mFile.reset(get_full_path(validPath));
+  if (!mFile) {
+    return PARSE_ERROR;
+  }
 
   return OK;
 }
 
 int
 AddFile::Prepare()
 {
-  LOG(("PREPARE ADD " LOG_S, mFile));
+  LOG(("PREPARE ADD " LOG_S, mRelPath.get()));
 
   return OK;
 }
 
 int
 AddFile::Execute()
 {
-  LOG(("EXECUTE ADD " LOG_S, mFile));
+  LOG(("EXECUTE ADD " LOG_S, mRelPath.get()));
 
   int rv;
 
   // First make sure that we can actually get rid of any existing file.
-  rv = NS_taccess(mFile, F_OK);
+  rv = NS_taccess(mFile.get(), F_OK);
   if (rv == 0) {
-    rv = backup_create(mFile);
+    rv = backup_create(mFile.get());
     if (rv)
       return rv;
   } else {
-    rv = ensure_parent_dir(mFile);
+    rv = ensure_parent_dir(mFile.get());
     if (rv)
       return rv;
   }
 
 #ifdef XP_WIN
   char sourcefile[MAXPATHLEN];
-  if (!WideCharToMultiByte(CP_UTF8, 0, mFile, -1, sourcefile, MAXPATHLEN,
-                           nullptr, nullptr)) {
+  if (!WideCharToMultiByte(CP_UTF8, 0, mRelPath.get(), -1, sourcefile,
+                           MAXPATHLEN, nullptr, nullptr)) {
     LOG(("error converting wchar to utf8: %d", GetLastError()));
     return STRING_CONVERSION_ERROR;
   }
 
-  rv = gArchiveReader.ExtractFile(sourcefile, mFile);
+  rv = gArchiveReader.ExtractFile(sourcefile, mFile.get());
 #else
-  rv = gArchiveReader.ExtractFile(mFile, mFile);
+  rv = gArchiveReader.ExtractFile(mRelPath.get(), mFile.get());
 #endif
   if (!rv) {
     mAdded = true;
   }
   return rv;
 }
 
 void
 AddFile::Finish(int status)
 {
-  LOG(("FINISH ADD " LOG_S, mFile));
+  LOG(("FINISH ADD " LOG_S, mRelPath.get()));
   // When there is an update failure and a file has been added it is removed
   // here since there might not be a backup to replace it.
   if (status && mAdded)
-    NS_tremove(mFile);
-  backup_finish(mFile, status);
+    NS_tremove(mFile.get());
+  backup_finish(mFile.get(), mRelPath.get(), status);
 }
 
 class PatchFile : public Action
 {
 public:
-  PatchFile() : mPatchIndex(-1), buf(nullptr) { }
+  PatchFile() : mPatchFile(nullptr), mPatchIndex(-1), buf(nullptr) { }
 
   virtual ~PatchFile();
 
   virtual int Parse(NS_tchar *line);
   virtual int Prepare(); // should check for patch file and for checksum here
   virtual int Execute();
   virtual void Finish(int status);
 
 private:
   int LoadSourceFile(FILE* ofile);
 
   static int sPatchIndex;
 
   const NS_tchar *mPatchFile;
-  const NS_tchar *mFile;
+  mozilla::UniquePtr<NS_tchar[]> mFile;
+  mozilla::UniquePtr<NS_tchar[]> mFileRelPath;
   int mPatchIndex;
   MBSPatchHeader header;
   unsigned char *buf;
   NS_tchar spath[MAXPATHLEN];
   AutoFile mPatchStream;
 };
 
 int PatchFile::sPatchIndex = 0;
@@ -1420,17 +1492,17 @@ PatchFile::~PatchFile()
 
 int
 PatchFile::LoadSourceFile(FILE* ofile)
 {
   struct stat os;
   int rv = fstat(fileno((FILE *)ofile), &os);
   if (rv) {
     LOG(("LoadSourceFile: unable to stat destination file: " LOG_S ", " \
-         "err: %d", mFile, errno));
+         "err: %d", mFileRelPath.get(), errno));
     return READ_ERROR;
   }
 
   if (uint32_t(os.st_size) != header.slen) {
     LOG(("LoadSourceFile: destination file size %d does not match expected size %d",
          uint32_t(os.st_size), header.slen));
     return LOADSOURCE_ERROR_WRONG_SIZE;
   }
@@ -1442,17 +1514,17 @@ PatchFile::LoadSourceFile(FILE* ofile)
 
   size_t r = header.slen;
   unsigned char *rb = buf;
   while (r) {
     const size_t count = mmin(SSIZE_MAX, r);
     size_t c = fread(rb, 1, count, ofile);
     if (c != count) {
       LOG(("LoadSourceFile: error reading destination file: " LOG_S,
-           mFile));
+           mFileRelPath.get()));
       return READ_ERROR;
     }
 
     r -= c;
     rb += c;
   }
 
   // Verify that the contents of the source file correspond to what we expect.
@@ -1480,28 +1552,36 @@ PatchFile::Parse(NS_tchar *line)
   }
 
   // consume whitespace between args
   NS_tchar *q = mstrtok(kQuote, &line);
   if (!q) {
     return PARSE_ERROR;
   }
 
-  mFile = get_valid_path(&line);
+  NS_tchar * validPath = get_valid_path(&line);
+  if (!validPath) {
+    return PARSE_ERROR;
+  }
+
+  mFileRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
+  NS_tstrcpy(mFileRelPath.get(), validPath);
+
+  mFile.reset(get_full_path(validPath));
   if (!mFile) {
     return PARSE_ERROR;
   }
 
   return OK;
 }
 
 int
 PatchFile::Prepare()
 {
-  LOG(("PREPARE PATCH " LOG_S, mFile));
+  LOG(("PREPARE PATCH " LOG_S, mFileRelPath.get()));
 
   // extract the patch to a temporary file
   mPatchIndex = sPatchIndex++;
 
   NS_tsnprintf(spath, sizeof(spath)/sizeof(spath[0]),
                NS_T("%s/updating/%d.patch"), gWorkingDirPath, mPatchIndex);
 
   NS_tremove(spath);
@@ -1532,118 +1612,119 @@ PatchFile::Prepare()
 #endif
 
   return rv;
 }
 
 int
 PatchFile::Execute()
 {
-  LOG(("EXECUTE PATCH " LOG_S, mFile));
+  LOG(("EXECUTE PATCH " LOG_S, mFileRelPath.get()));
 
   fseek(mPatchStream, 0, SEEK_SET);
 
   int rv = MBS_ReadHeader(mPatchStream, &header);
   if (rv) {
     return rv;
   }
 
   FILE *origfile = nullptr;
 #ifdef XP_WIN
-  if (NS_tstrcmp(mFile, gCallbackRelPath) == 0) {
+  if (NS_tstrcmp(mFileRelPath.get(), gCallbackRelPath) == 0) {
     // Read from the copy of the callback when patching since the callback can't
     // be opened for reading to prevent the application from being launched.
     origfile = NS_tfopen(gCallbackBackupPath, NS_T("rb"));
   } else {
-    origfile = NS_tfopen(mFile, NS_T("rb"));
+    origfile = NS_tfopen(mFile.get(), NS_T("rb"));
   }
 #else
-  origfile = NS_tfopen(mFile, NS_T("rb"));
+  origfile = NS_tfopen(mFile.get(), NS_T("rb"));
 #endif
 
   if (!origfile) {
-    LOG(("unable to open destination file: " LOG_S ", err: %d", mFile,
-         errno));
+    LOG(("unable to open destination file: " LOG_S ", err: %d",
+         mFileRelPath.get(), errno));
     return READ_ERROR;
   }
 
   rv = LoadSourceFile(origfile);
   fclose(origfile);
   if (rv) {
     LOG(("LoadSourceFile failed"));
     return rv;
   }
 
   // Rename the destination file if it exists before proceeding so it can be
   // used to restore the file to its original state if there is an error.
   struct NS_tstat_t ss;
-  rv = NS_tstat(mFile, &ss);
+  rv = NS_tstat(mFile.get(), &ss);
   if (rv) {
-    LOG(("failed to read file status info: " LOG_S ", err: %d", mFile,
-         errno));
+    LOG(("failed to read file status info: " LOG_S ", err: %d",
+         mFileRelPath.get(), errno));
     return READ_ERROR;
   }
 
-  rv = backup_create(mFile);
+  rv = backup_create(mFile.get());
   if (rv) {
     return rv;
   }
 
 #if defined(HAVE_POSIX_FALLOCATE)
-  AutoFile ofile(ensure_open(mFile, NS_T("wb+"), ss.st_mode));
+  AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
   posix_fallocate(fileno((FILE *)ofile), 0, header.dlen);
 #elif defined(XP_WIN)
   bool shouldTruncate = true;
   // Creating the file, setting the size, and then closing the file handle
   // lessens fragmentation more than any other method tested. Other methods that
   // have been tested are:
   // 1. _chsize / _chsize_s reduced fragmentation though not completely.
   // 2. _get_osfhandle and then setting the size reduced fragmentation though
   //    not completely. There are also reports of _get_osfhandle failing on
   //    mingw.
-  HANDLE hfile = CreateFileW(mFile,
+  HANDLE hfile = CreateFileW(mFile.get(),
                              GENERIC_WRITE,
                              0,
                              nullptr,
                              CREATE_ALWAYS,
                              FILE_ATTRIBUTE_NORMAL,
                              nullptr);
 
   if (hfile != INVALID_HANDLE_VALUE) {
     if (SetFilePointer(hfile, header.dlen,
                        nullptr, FILE_BEGIN) != INVALID_SET_FILE_POINTER &&
         SetEndOfFile(hfile) != 0) {
       shouldTruncate = false;
     }
     CloseHandle(hfile);
   }
 
-  AutoFile ofile(ensure_open(mFile, shouldTruncate ? NS_T("wb+") : NS_T("rb+"),
+  AutoFile ofile(ensure_open(mFile.get(), shouldTruncate ? NS_T("wb+") : NS_T("rb+"),
                              ss.st_mode));
 #elif defined(XP_MACOSX)
-  AutoFile ofile(ensure_open(mFile, NS_T("wb+"), ss.st_mode));
+  AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
   // Modified code from FileUtils.cpp
   fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, header.dlen};
   // Try to get a continous chunk of disk space
   rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store);
   if (rv == -1) {
     // OK, perhaps we are too fragmented, allocate non-continuous
     store.fst_flags = F_ALLOCATEALL;
     rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store);
   }
 
   if (rv != -1) {
     ftruncate(fileno((FILE *)ofile), header.dlen);
   }
 #else
-  AutoFile ofile(ensure_open(mFile, NS_T("wb+"), ss.st_mode));
+  AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
 #endif
 
   if (ofile == nullptr) {
-    LOG(("unable to create new file: " LOG_S ", err: %d", mFile, errno));
+    LOG(("unable to create new file: " LOG_S ", err: %d", mFileRelPath.get(),
+         errno));
     return WRITE_ERROR_OPEN_PATCH_FILE;
   }
 
 #ifdef XP_WIN
   if (!shouldTruncate) {
     fseek(ofile, 0, SEEK_SET);
   }
 #endif
@@ -1665,57 +1746,57 @@ PatchFile::Execute()
   buf = nullptr;
 
   return rv;
 }
 
 void
 PatchFile::Finish(int status)
 {
-  LOG(("FINISH PATCH " LOG_S, mFile));
-
-  backup_finish(mFile, status);
+  LOG(("FINISH PATCH " LOG_S, mFileRelPath.get()));
+
+  backup_finish(mFile.get(), mFileRelPath.get(), status);
 }
 
 class AddIfFile : public AddFile
 {
 public:
-  AddIfFile() : mTestFile(nullptr) { }
-
   virtual int Parse(NS_tchar *line);
   virtual int Prepare();
   virtual int Execute();
   virtual void Finish(int status);
 
 protected:
-  const NS_tchar *mTestFile;
+  mozilla::UniquePtr<NS_tchar[]> mTestFile;
 };
 
 int
 AddIfFile::Parse(NS_tchar *line)
 {
   // format "<testfile>" "<newfile>"
 
-  mTestFile = get_valid_path(&line);
-  if (!mTestFile)
+  mTestFile.reset(get_full_path(get_valid_path(&line)));
+  if (!mTestFile) {
     return PARSE_ERROR;
+  }
 
   // consume whitespace between args
   NS_tchar *q = mstrtok(kQuote, &line);
-  if (!q)
+  if (!q) {
     return PARSE_ERROR;
+  }
 
   return AddFile::Parse(line);
 }
 
 int
 AddIfFile::Prepare()
 {
   // If the test file does not exist, then skip this action.
-  if (NS_taccess(mTestFile, F_OK)) {
+  if (NS_taccess(mTestFile.get(), F_OK)) {
     mTestFile = nullptr;
     return OK;
   }
 
   return AddFile::Prepare();
 }
 
 int
@@ -1734,49 +1815,49 @@ AddIfFile::Finish(int status)
     return;
 
   AddFile::Finish(status);
 }
 
 class AddIfNotFile : public AddFile
 {
 public:
-  AddIfNotFile() : mTestFile(NULL) { }
-
   virtual int Parse(NS_tchar *line);
   virtual int Prepare();
   virtual int Execute();
   virtual void Finish(int status);
 
 protected:
-  const NS_tchar *mTestFile;
+  mozilla::UniquePtr<NS_tchar[]> mTestFile;
 };
 
 int
 AddIfNotFile::Parse(NS_tchar *line)
 {
   // format "<testfile>" "<newfile>"
 
-  mTestFile = get_valid_path(&line);
-  if (!mTestFile)
+  mTestFile.reset(get_full_path(get_valid_path(&line)));
+  if (!mTestFile) {
     return PARSE_ERROR;
+  }
 
   // consume whitespace between args
   NS_tchar *q = mstrtok(kQuote, &line);
-  if (!q)
+  if (!q) {
     return PARSE_ERROR;
+  }
 
   return AddFile::Parse(line);
 }
 
 int
 AddIfNotFile::Prepare()
 {
   // If the test file exists, then skip this action.
-  if (!NS_taccess(mTestFile, F_OK)) {
+  if (!NS_taccess(mTestFile.get(), F_OK)) {
     mTestFile = NULL;
     return OK;
   }
 
   return AddFile::Prepare();
 }
 
 int
@@ -1795,49 +1876,49 @@ AddIfNotFile::Finish(int status)
     return;
 
   AddFile::Finish(status);
 }
 
 class PatchIfFile : public PatchFile
 {
 public:
-  PatchIfFile() : mTestFile(nullptr) { }
-
   virtual int Parse(NS_tchar *line);
   virtual int Prepare(); // should check for patch file and for checksum here
   virtual int Execute();
   virtual void Finish(int status);
 
 private:
-  const NS_tchar *mTestFile;
+  mozilla::UniquePtr<NS_tchar[]> mTestFile;
 };
 
 int
 PatchIfFile::Parse(NS_tchar *line)
 {
   // format "<testfile>" "<patchfile>" "<filetopatch>"
 
-  mTestFile = get_valid_path(&line);
-  if (!mTestFile)
+  mTestFile.reset(get_full_path(get_valid_path(&line)));
+  if (!mTestFile) {
     return PARSE_ERROR;
+  }
 
   // consume whitespace between args
   NS_tchar *q = mstrtok(kQuote, &line);
-  if (!q)
+  if (!q) {
     return PARSE_ERROR;
+  }
 
   return PatchFile::Parse(line);
 }
 
 int
 PatchIfFile::Prepare()
 {
   // If the test file does not exist, then skip this action.
-  if (NS_taccess(mTestFile, F_OK)) {
+  if (NS_taccess(mTestFile.get(), F_OK)) {
     mTestFile = nullptr;
     return OK;
   }
 
   return PatchFile::Prepare();
 }
 
 int
@@ -2510,18 +2591,17 @@ UpdateThreadFunc(void *param)
                    NS_T("%s/updating"), gWorkingDirPath);
       ensure_remove_recursive(updatingDir);
     }
   }
 
   if (rv && (sReplaceRequest || sStagedUpdate)) {
 #ifdef XP_WIN
     // On Windows, the current working directory of the process should be changed
-    // so that it's not locked. The working directory for staging an update was
-    // already changed earlier.
+    // so that it's not locked.
     if (sStagedUpdate) {
       NS_tchar sysDir[MAX_PATH + 1] = { L'\0' };
       if (GetSystemDirectoryW(sysDir, MAX_PATH + 1)) {
         NS_tchdir(sysDir);
       }
     }
 #endif
     ensure_remove_recursive(gWorkingDirPath);
@@ -2846,31 +2926,40 @@ int NS_main(int argc, NS_tchar **argv)
     LOG(("Performing a replace request"));
   }
 
   LOG(("PATCH DIRECTORY " LOG_S, gPatchDirPath));
   LOG(("INSTALLATION DIRECTORY " LOG_S, gInstallDirPath));
   LOG(("WORKING DIRECTORY " LOG_S, gWorkingDirPath));
 
 #if defined(XP_WIN)
-  if (sReplaceRequest || sStagedUpdate) {
-    NS_tchar stagedParent[MAX_PATH];
-    NS_tsnprintf(stagedParent, sizeof(stagedParent)/sizeof(stagedParent[0]),
+  if (_wcsnicmp(gWorkingDirPath, gInstallDirPath, MAX_PATH) != 0) {
+    if (!sStagedUpdate && !sReplaceRequest) {
+      WriteStatusFile(INVALID_APPLYTO_DIR_ERROR);
+      LOG(("Installation directory and working directory must be the same "
+           "for non-staged updates. Exiting."));
+      LogFinish();
+      return 1;
+    }
+
+    NS_tchar workingDirParent[MAX_PATH];
+    NS_tsnprintf(workingDirParent,
+                 sizeof(workingDirParent) / sizeof(workingDirParent[0]),
                  NS_T("%s"), gWorkingDirPath);
-    if (!PathRemoveFileSpecW(stagedParent)) {
+    if (!PathRemoveFileSpecW(workingDirParent)) {
       WriteStatusFile(REMOVE_FILE_SPEC_ERROR);
       LOG(("Error calling PathRemoveFileSpecW: %d", GetLastError()));
       LogFinish();
       return 1;
     }
 
-    if (_wcsnicmp(stagedParent, gInstallDirPath, MAX_PATH) != 0) {
-      WriteStatusFile(INVALID_STAGED_PARENT_ERROR);