Merge inbound to central, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 26 Apr 2017 17:30:31 -0700
changeset 403208 0b77ed3f26c5335503bc16e85b8c067382e7bb1e
parent 403156 745f2d85212d7c4bc82240c5a43730d9ecd32125 (current diff)
parent 403207 b1bd89334ee5a2f92aa087e2c1f28f19ded0d399 (diff)
child 403224 029cbd7f6e4382bfa57176cfa2c9725f8be92993
child 403306 674bad1624a5ac186288273126291f322f4f4e66
child 403365 36a3a336b4455d70899eda5224f8c01eb5b985be
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
0b77ed3f26c5 / 55.0a1 / 20170427100642 / files
nightly linux64
0b77ed3f26c5 / 55.0a1 / 20170427100655 / files
nightly mac
0b77ed3f26c5 / 55.0a1 / 20170427030231 / files
nightly win32
0b77ed3f26c5 / 55.0a1 / 20170427030231 / files
nightly win64
0b77ed3f26c5 / 55.0a1 / 20170427030231 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge CLOSED TREE MozReview-Commit-ID: 9HQViJrDT6S
dom/canvas/test/reftest/filters/reftest-stylo.list
dom/canvas/test/reftest/reftest-stylo.list
dom/encoding/test/reftest/reftest-stylo.list
dom/html/reftests/autofocus/reftest-stylo.list
dom/html/reftests/reftest-stylo.list
dom/html/reftests/toblob-todataurl/reftest-stylo.list
dom/imptests/html/html/dom/elements/global-attributes/reftest-stylo.list
dom/plugins/test/reftest/reftest-stylo.list
dom/tests/reftest/reftest-stylo.list
editor/reftests/reftest-stylo.list
editor/reftests/xul/reftest-stylo.list
gfx/layers/apz/test/reftest/reftest-stylo.list
gfx/tests/reftest/reftest-stylo.list
image/test/reftest/bmp/bmp-1bpp/reftest-stylo.list
image/test/reftest/bmp/bmp-24bpp/reftest-stylo.list
image/test/reftest/bmp/bmp-4bpp/reftest-stylo.list
image/test/reftest/bmp/bmp-8bpp/reftest-stylo.list
image/test/reftest/bmp/bmp-corrupted/reftest-stylo.list
image/test/reftest/bmp/bmpsuite/b/reftest-stylo.list
image/test/reftest/bmp/bmpsuite/g/reftest-stylo.list
image/test/reftest/bmp/bmpsuite/q/reftest-stylo.list
image/test/reftest/bmp/reftest-stylo.list
image/test/reftest/color-management/reftest-stylo.list
image/test/reftest/downscaling/reftest-stylo.list
image/test/reftest/encoders-lossless/reftest-stylo.list
image/test/reftest/gif/reftest-stylo.list
image/test/reftest/ico/cur/reftest-stylo.list
image/test/reftest/ico/ico-bmp-1bpp/reftest-stylo.list
image/test/reftest/ico/ico-bmp-24bpp/reftest-stylo.list
image/test/reftest/ico/ico-bmp-32bpp/reftest-stylo.list
image/test/reftest/ico/ico-bmp-4bpp/reftest-stylo.list
image/test/reftest/ico/ico-bmp-8bpp/reftest-stylo.list
image/test/reftest/ico/ico-bmp-corrupted/reftest-stylo.list
image/test/reftest/ico/ico-mixed/reftest-stylo.list
image/test/reftest/ico/ico-png/reftest-stylo.list
image/test/reftest/jpeg/reftest-stylo.list
image/test/reftest/pngsuite-ancillary/reftest-stylo.list
image/test/reftest/pngsuite-background/reftest-stylo.list
image/test/reftest/pngsuite-basic-i/reftest-stylo.list
image/test/reftest/pngsuite-basic-n/reftest-stylo.list
image/test/reftest/pngsuite-chunkorder/reftest-stylo.list
image/test/reftest/pngsuite-corrupted/reftest-stylo.list
image/test/reftest/pngsuite-filtering/reftest-stylo.list
image/test/reftest/pngsuite-gamma/reftest-stylo.list
image/test/reftest/pngsuite-oddsizes/reftest-stylo.list
image/test/reftest/pngsuite-palettes/reftest-stylo.list
image/test/reftest/pngsuite-transparency/reftest-stylo.list
image/test/reftest/pngsuite-zlib/reftest-stylo.list
image/test/reftest/reftest-stylo.list
js/src/builtin/TestingFunctions.cpp
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
js/src/jit/Ion.cpp
js/src/jsapi.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/xpcprivate.h
layout/reftests/abs-pos/reftest-stylo.list
layout/reftests/async-scrolling/reftest-stylo.list
layout/reftests/backgrounds/gradient/reftest-stylo.list
layout/reftests/backgrounds/reftest-stylo.list
layout/reftests/backgrounds/vector/reftest-stylo.list
layout/reftests/bidi/dirAuto/reftest-stylo.list
layout/reftests/bidi/numeral/reftest-stylo.list
layout/reftests/bidi/reftest-stylo.list
layout/reftests/border-dotted/reftest-stylo.list
layout/reftests/border-image/reftest-stylo.list
layout/reftests/border-radius/reftest-stylo.list
layout/reftests/box-ordinal/reftest-stylo.list
layout/reftests/box-properties/reftest-stylo.list
layout/reftests/box-shadow/reftest-stylo.list
layout/reftests/box/reftest-stylo.list
layout/reftests/bugs/reftest-stylo.list
layout/reftests/canvas/reftest-stylo.list
layout/reftests/columns/reftest-stylo.list
layout/reftests/counter-style/reftest-stylo.list
layout/reftests/counters/reftest-stylo.list
layout/reftests/css-animations/reftest-stylo.list
layout/reftests/css-blending/reftest-stylo.list
layout/reftests/css-break/reftest-stylo.list
layout/reftests/css-calc/reftest-stylo.list
layout/reftests/css-default/submit-button/reftest-stylo.list
layout/reftests/css-disabled/button/reftest-stylo.list
layout/reftests/css-disabled/fieldset/reftest-stylo.list
layout/reftests/css-disabled/input/reftest-stylo.list
layout/reftests/css-disabled/select/reftest-stylo.list
layout/reftests/css-disabled/textarea/reftest-stylo.list
layout/reftests/css-display/reftest-stylo.list
layout/reftests/css-enabled/button/reftest-stylo.list
layout/reftests/css-enabled/fieldset/reftest-stylo.list
layout/reftests/css-enabled/input/reftest-stylo.list
layout/reftests/css-enabled/optgroup/reftest-stylo.list
layout/reftests/css-enabled/select/reftest-stylo.list
layout/reftests/css-enabled/textarea/reftest-stylo.list
layout/reftests/css-gradients/reftest-stylo.list
layout/reftests/css-grid/reftest-stylo.list
layout/reftests/css-import/reftest-stylo.list
layout/reftests/css-invalid/button/reftest-stylo.list
layout/reftests/css-invalid/default-style/reftest-stylo.list
layout/reftests/css-invalid/fieldset/reftest-stylo.list
layout/reftests/css-invalid/form/reftest-stylo.list
layout/reftests/css-invalid/input/reftest-stylo.list
layout/reftests/css-invalid/select/reftest-stylo.list
layout/reftests/css-invalid/textarea/reftest-stylo.list
layout/reftests/css-mediaqueries/reftest-stylo.list
layout/reftests/css-optional/reftest-stylo.list
layout/reftests/css-parsing/reftest-stylo.list
layout/reftests/css-placeholder/input/reftest-stylo.list
layout/reftests/css-placeholder/reftest-stylo.list
layout/reftests/css-placeholder/textarea/reftest-stylo.list
layout/reftests/css-required/reftest-stylo.list
layout/reftests/css-ruby/reftest-stylo.list
layout/reftests/css-selectors/reftest-stylo.list
layout/reftests/css-sizing/reftest-stylo.list
layout/reftests/css-submit-invalid/button-submit/reftest-stylo.list
layout/reftests/css-submit-invalid/default-style/reftest-stylo.list
layout/reftests/css-submit-invalid/input-image/reftest-stylo.list
layout/reftests/css-submit-invalid/input-submit/reftest-stylo.list
layout/reftests/css-transitions/reftest-stylo.list
layout/reftests/css-ui-invalid/button/reftest-stylo.list
layout/reftests/css-ui-invalid/default-style/reftest-stylo.list
layout/reftests/css-ui-invalid/input/reftest-stylo.list
layout/reftests/css-ui-invalid/select/reftest-stylo.list
layout/reftests/css-ui-invalid/textarea/reftest-stylo.list
layout/reftests/css-ui-valid/button/reftest-stylo.list
layout/reftests/css-ui-valid/input/reftest-stylo.list
layout/reftests/css-ui-valid/select/reftest-stylo.list
layout/reftests/css-ui-valid/textarea/reftest-stylo.list
layout/reftests/css-valid/button/reftest-stylo.list
layout/reftests/css-valid/input/reftest-stylo.list
layout/reftests/css-valid/select/reftest-stylo.list
layout/reftests/css-valid/textarea/reftest-stylo.list
layout/reftests/css-valuesandunits/reftest-stylo.list
layout/reftests/css-variables/reftest-stylo.list
layout/reftests/cssom/reftest-stylo.list
layout/reftests/details-summary/reftest-stylo.list
layout/reftests/dom/reftest-stylo.list
layout/reftests/first-letter/reftest-stylo.list
layout/reftests/first-line/reftest-stylo.list
layout/reftests/flexbox/reftest-stylo.list
layout/reftests/floats/reftest-stylo.list
layout/reftests/font-face/reftest-stylo.list
layout/reftests/font-features/reftest-stylo.list
layout/reftests/font-inflation/reftest-stylo.list
layout/reftests/font-loading-api/reftest-stylo.list
layout/reftests/font-matching/reftest-stylo.list
layout/reftests/forms/button/reftest-stylo.list
layout/reftests/forms/fieldset/reftest-stylo.list
layout/reftests/forms/input/checkbox/reftest-stylo.list
layout/reftests/forms/input/color/reftest-stylo.list
layout/reftests/forms/input/datetime/reftest-stylo.list
layout/reftests/forms/input/email/reftest-stylo.list
layout/reftests/forms/input/file/reftest-stylo.list
layout/reftests/forms/input/number/reftest-stylo.list
layout/reftests/forms/input/percentage/reftest-stylo.list
layout/reftests/forms/input/radio/reftest-stylo.list
layout/reftests/forms/input/range/reftest-stylo.list
layout/reftests/forms/input/reftest-stylo.list
layout/reftests/forms/input/search/reftest-stylo.list
layout/reftests/forms/input/tel/reftest-stylo.list
layout/reftests/forms/input/text/reftest-stylo.list
layout/reftests/forms/input/url/reftest-stylo.list
layout/reftests/forms/legend/reftest-stylo.list
layout/reftests/forms/meter/default-style/reftest-stylo.list
layout/reftests/forms/meter/reftest-stylo.list
layout/reftests/forms/output/reftest-stylo.list
layout/reftests/forms/placeholder/reftest-stylo.list
layout/reftests/forms/progress/reftest-stylo.list
layout/reftests/forms/reftest-stylo.list
layout/reftests/forms/select/reftest-stylo.list
layout/reftests/forms/textarea/reftest-stylo.list
layout/reftests/forms/textbox/reftest-stylo.list
layout/reftests/generated-content/reftest-stylo.list
layout/reftests/ib-split/reftest-stylo.list
layout/reftests/image-element/reftest-stylo.list
layout/reftests/image-rect/reftest-stylo.list
layout/reftests/image-region/reftest-stylo.list
layout/reftests/image/reftest-stylo.list
layout/reftests/indic-shaping/reftest-stylo.list
layout/reftests/inline-borderpadding/reftest-stylo.list
layout/reftests/inline/reftest-stylo.list
layout/reftests/invalidation/reftest-stylo.list
layout/reftests/layers/reftest-stylo.list
layout/reftests/line-breaking/reftest-stylo.list
layout/reftests/list-item/reftest-stylo.list
layout/reftests/margin-collapsing/reftest-stylo.list
layout/reftests/marquee/reftest-stylo.list
layout/reftests/mathml/reftest-stylo.list
layout/reftests/mp4-video/reftest-stylo.list
layout/reftests/native-theme/reftest-stylo.list
layout/reftests/object/reftest-stylo.list
layout/reftests/ogg-video/reftest-stylo.list
layout/reftests/outline/reftest-stylo.list
layout/reftests/pagination/reftest-stylo.list
layout/reftests/percent-overflow-sizing/reftest-stylo.list
layout/reftests/pixel-rounding/reftest-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_border_abspos-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_border_parent-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_margin_abspos-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_margin_parent-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_padding_abspos-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_padding_parent-stylo.list
layout/reftests/position-dynamic-changes/horizontal/reftest_plain-stylo.list
layout/reftests/position-dynamic-changes/reftest-stylo.list
layout/reftests/position-dynamic-changes/relative/reftest-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_border_abspos-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_border_parent-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_margin_abspos-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_margin_parent-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_padding_abspos-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_padding_parent-stylo.list
layout/reftests/position-dynamic-changes/vertical/reftest_plain-stylo.list
layout/reftests/position-sticky/reftest-stylo.list
layout/reftests/printing/reftest-stylo.list
layout/reftests/reftest-sanity/default-preferences-tests-stylo.list
layout/reftests/reftest-sanity/reftest-stylo.list
layout/reftests/reftest-sanity/scripttests-stylo.list
layout/reftests/reftest-sanity/urlprefixtests-stylo.list
layout/reftests/reftest-stylo.list
layout/reftests/scoped-style/reftest-stylo.list
layout/reftests/scrolling/reftest-stylo.list
layout/reftests/selection/reftest-stylo.list
layout/reftests/svg/as-image/reftest-stylo.list
layout/reftests/svg/as-image/zoom/reftest-stylo.list
layout/reftests/svg/filters/css-filter-chains/reftest-stylo.list
layout/reftests/svg/filters/css-filters/reftest-stylo.list
layout/reftests/svg/filters/reftest-stylo.list
layout/reftests/svg/image/reftest-stylo.list
layout/reftests/svg/load-only/reftest-stylo.list
layout/reftests/svg/moz-only/reftest-stylo.list
layout/reftests/svg/reftest-stylo.list
layout/reftests/svg/sizing/reftest-stylo.list
layout/reftests/svg/smil/container/reftest-stylo.list
layout/reftests/svg/smil/event/reftest-stylo.list
layout/reftests/svg/smil/motion/reftest-stylo.list
layout/reftests/svg/smil/reftest-stylo.list
layout/reftests/svg/smil/seek/reftest-stylo.list
layout/reftests/svg/smil/sort/reftest-stylo.list
layout/reftests/svg/smil/style/reftest-stylo.list
layout/reftests/svg/smil/syncbase/reftest-stylo.list
layout/reftests/svg/smil/timed/reftest-stylo.list
layout/reftests/svg/smil/transform/reftest-stylo.list
layout/reftests/svg/svg-integration/clip-path/reftest-stylo.list
layout/reftests/svg/svg-integration/reftest-stylo.list
layout/reftests/svg/text/reftest-stylo.list
layout/reftests/tab-size/reftest-stylo.list
layout/reftests/table-anonymous-boxes/reftest-stylo.list
layout/reftests/table-background/reftest-stylo.list
layout/reftests/table-bordercollapse/reftest-stylo.list
layout/reftests/table-dom/reftest-stylo.list
layout/reftests/table-html/reftest-stylo.list
layout/reftests/table-overflow/reftest-stylo.list
layout/reftests/table-width/reftest-stylo.list
layout/reftests/text-decoration/reftest-stylo.list
layout/reftests/text-indent/reftest-stylo.list
layout/reftests/text-overflow/reftest-stylo.list
layout/reftests/text-shadow/reftest-stylo.list
layout/reftests/text-stroke/reftest-stylo.list
layout/reftests/text-svgglyphs/reftest-stylo.list
layout/reftests/text-transform/reftest-stylo.list
layout/reftests/text/reftest-stylo.list
layout/reftests/transform-3d/reftest-stylo.list
layout/reftests/transform/reftest-stylo.list
layout/reftests/unicode/reftest-stylo.list
layout/reftests/view-source/reftest-stylo.list
layout/reftests/w3c-css/received/reftest-stylo.list
layout/reftests/w3c-css/submitted/align3/reftest-stylo.list
layout/reftests/w3c-css/submitted/background/reftest-stylo.list
layout/reftests/w3c-css/submitted/color4/reftest-stylo.list
layout/reftests/w3c-css/submitted/conditional3/reftest-stylo.list
layout/reftests/w3c-css/submitted/contain/reftest-stylo.list
layout/reftests/w3c-css/submitted/css21/pagination/reftest-stylo.list
layout/reftests/w3c-css/submitted/flexbox/reftest-stylo.list
layout/reftests/w3c-css/submitted/images3/reftest-stylo.list
layout/reftests/w3c-css/submitted/lists-3/reftest-stylo.list
layout/reftests/w3c-css/submitted/masking/reftest-stylo.list
layout/reftests/w3c-css/submitted/multicol3/reftest-stylo.list
layout/reftests/w3c-css/submitted/reftest-stylo.list
layout/reftests/w3c-css/submitted/ruby/reftest-stylo.list
layout/reftests/w3c-css/submitted/selectors4/reftest-stylo.list
layout/reftests/w3c-css/submitted/shapes1/reftest-stylo.list
layout/reftests/w3c-css/submitted/text-decor-3/reftest-stylo.list
layout/reftests/w3c-css/submitted/ui3/reftest-stylo.list
layout/reftests/w3c-css/submitted/values3/reftest-stylo.list
layout/reftests/w3c-css/submitted/variables/reftest-stylo.list
layout/reftests/w3c-css/submitted/will-change/reftest-stylo.list
layout/reftests/w3c-css/submitted/writing-modes-3/reftest-stylo.list
layout/reftests/web-animations/reftest-stylo.list
layout/reftests/webcomponents/reftest-stylo.list
layout/reftests/webkit-box/reftest-stylo.list
layout/reftests/webkit-gradient/reftest-stylo.list
layout/reftests/webm-video/reftest-stylo.list
layout/reftests/writing-mode/abspos/reftest-stylo.list
layout/reftests/writing-mode/reftest-stylo.list
layout/reftests/writing-mode/tables/reftest-stylo.list
layout/reftests/xul-document-load/reftest-stylo.list
layout/reftests/xul/reftest-stylo.list
layout/reftests/z-index/reftest-stylo.list
layout/tables/reftests/reftest-stylo.list
layout/xul/grid/reftests/reftest-stylo.list
layout/xul/reftest/reftest-stylo.list
parser/htmlparser/tests/reftest/reftest-stylo.list
python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/reftests/reftest-stylo.list
python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest-stylo.list
python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/reftest-stylo.list
toolkit/content/tests/reftests/reftest-stylo.list
toolkit/crashreporter/nsExceptionHandler.cpp
widget/reftests/reftest-stylo.list
xpcom/base/nsDebug.h
--- a/browser/modules/test/browser/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -2,16 +2,17 @@
 support-files =
   head.js
 
 [browser_BrowserUITelemetry_buckets.js]
 [browser_BrowserUITelemetry_defaults.js]
 [browser_BrowserUITelemetry_sidebar.js]
 [browser_BrowserUITelemetry_syncedtabs.js]
 [browser_ContentSearch.js]
+skip-if = (os == "mac" || os == "linux") # Bug 1308343
 support-files =
   contentSearch.js
   contentSearchBadImage.xml
   contentSearchSuggestions.sjs
   contentSearchSuggestions.xml
   !/browser/components/search/test/head.js
   !/browser/components/search/test/testEngine.xml
 [browser_NetworkPrioritizer.js]
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -1431,18 +1431,17 @@ WriteHeapGraph(JSContext* cx,
   }
 
   return ok;
 }
 
 static unsigned long
 msSinceProcessCreation(const TimeStamp& now)
 {
-  bool ignored;
-  auto duration = now - TimeStamp::ProcessCreation(ignored);
+  auto duration = now - TimeStamp::ProcessCreation();
   return (unsigned long) duration.ToMilliseconds();
 }
 
 /* static */ already_AddRefed<nsIFile>
 HeapSnapshot::CreateUniqueCoreDumpFile(ErrorResult& rv,
                                        const TimeStamp& now,
                                        nsAString& outFilePath,
                                        nsAString& outSnapshotId)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -194,16 +194,17 @@
 #include "nsXULAppAPI.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsISecurityUITelemetry.h"
 #include "nsDSURIContentListener.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsDocShellTransferableHooks.h"
 #include "nsICommandManager.h"
 #include "nsIDOMNode.h"
+#include "nsIClassOfService.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIHttpChannel.h"
 #include "nsIIDNService.h"
 #include "nsIInputStreamChannel.h"
 #include "nsINestedURI.h"
 #include "nsISHContainer.h"
 #include "nsISHistory.h"
 #include "nsISecureBrowserUI.h"
@@ -3019,19 +3020,17 @@ nsDocShell::PopProfileTimelineMarkers(
   }
 
   return NS_OK;
 }
 
 nsresult
 nsDocShell::Now(DOMHighResTimeStamp* aWhen)
 {
-  bool ignore;
-  *aWhen =
-    (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+  *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetWindowDraggingAllowed(bool aValue)
 {
   RefPtr<nsDocShell> parent = GetParentDocshell();
   if (!aValue && mItemType == typeChrome && !parent) {
@@ -11342,16 +11341,27 @@ nsDocShell::DoURILoad(nsIURI* aURI,
     if (IsFrame() && win) {
       nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
       if (frameElement) {
         timedChannel->SetInitiatorType(frameElement->LocalName());
       }
     }
   }
 
+  // Mark the http channel as UrgentStart for top level document loading
+  // in active tab.
+  if (mIsActive || (mLoadType & (LOAD_CMD_NORMAL | LOAD_CMD_HISTORY))) {
+    if (httpChannel && isTopLevelDoc) {
+      nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
+      if (cos) {
+        cos->AddClassFlags(nsIClassOfService::UrgentStart);
+      }
+    }
+  }
+
   rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
 
   //
   // If the channel load failed, we failed and nsIWebProgress just ain't
   // gonna happen.
   //
   if (NS_SUCCEEDED(rv)) {
     if (aDocShell) {
--- a/docshell/base/timeline/AbstractTimelineMarker.cpp
+++ b/docshell/base/timeline/AbstractTimelineMarker.cpp
@@ -60,18 +60,17 @@ AbstractTimelineMarker::SetCurrentTime()
 {
   TimeStamp now = TimeStamp::Now();
   SetCustomTime(now);
 }
 
 void
 AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime)
 {
-  bool isInconsistent = false;
-  mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
+  mTime = (aTime - TimeStamp::ProcessCreation()).ToMilliseconds();
 }
 
 void
 AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime)
 {
   mTime = aTime;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1156,17 +1156,17 @@ nsOuterWindowProxy::className(JSContext 
     return "Window";
 }
 
 void
 nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) const
 {
   nsGlobalWindow* outerWindow = GetOuterWindow(proxy);
   if (outerWindow) {
-    outerWindow->ClearWrapper();
+    outerWindow->ClearWrapper(proxy);
 
     // Ideally we would use OnFinalize here, but it's possible that
     // EnsureScriptEnvironment will later be called on the window, and we don't
     // want to create a new script object in that case. Therefore, we need to
     // write a non-null value that will reliably crash when dereferenced.
     outerWindow->PoisonOuterWindowProxy(proxy);
   }
 }
@@ -1723,17 +1723,17 @@ nsGlobalWindow::~nsGlobalWindow()
                   static_cast<void*>(ToCanonicalSupports(outer)),
                   url.get());
   }
 #endif
 
   MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, ("DOMWINDOW %p destroyed", this));
 
   if (IsOuterWindow()) {
-    JSObject *proxy = GetWrapperPreserveColor();
+    JSObject *proxy = GetWrapperMaybeDead();
     if (proxy) {
       js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr));
     }
 
     // An outer window is destroyed with inner windows still possibly
     // alive, iterate through the inner windows and null out their
     // back pointer to this outer, and pull them out of the list of
     // inner windows.
@@ -3942,17 +3942,17 @@ nsGlobalWindow::DispatchDOMEvent(WidgetE
                                            aEvent, aDOMEvent, aPresContext,
                                            aEventStatus);
 }
 
 void
 nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
 {
   MOZ_ASSERT(IsOuterWindow());
-  if (aObject == GetWrapperPreserveColor()) {
+  if (aObject == GetWrapperMaybeDead()) {
     PoisonWrapper();
   }
 }
 
 nsresult
 nsGlobalWindow::SetArguments(nsIArray *aArguments)
 {
   MOZ_ASSERT(IsOuterWindow());
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -95,18 +95,17 @@ nsImageLoadingContent::nsImageLoadingCon
     mStateChangerDepth(0),
     mCurrentRequestRegistered(false),
     mPendingRequestRegistered(false)
 {
   if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
     mLoadingEnabled = false;
   }
 
-  bool isInconsistent;
-  mMostRecentRequestChange = TimeStamp::ProcessCreation(isInconsistent);
+  mMostRecentRequestChange = TimeStamp::ProcessCreation();
 }
 
 void
 nsImageLoadingContent::DestroyImageLoadingContent()
 {
   // Cancel our requests so they won't hold stale refs to us
   // NB: Don't ask to discard the images here.
   ClearCurrentRequest(NS_BINDING_ABORTED);
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -90,22 +90,35 @@ public:
 
   /**
    * Get the cached wrapper.
    *
    * This getter does not change the color of the JSObject meaning that the
    * object returned is not guaranteed to be kept alive past the next CC.
    *
    * This should only be called if you are certain that the return value won't
-   * be passed into a JS API function and that it won't be stored without being
+   * be passed into a JSAPI function and that it won't be stored without being
    * rooted (or otherwise signaling the stored value to the CC).
    */
-  JSObject* GetWrapperPreserveColor() const
+  JSObject* GetWrapperPreserveColor() const;
+
+  /**
+   * Get the cached wrapper.
+   *
+   * This getter does not check whether the wrapper is dead and in the process
+   * of being finalized.
+   *
+   * This should only be called if you really need to see the raw contents of
+   * this cache, for example as part of finalization. Don't store the result
+   * anywhere or pass it into JSAPI functions that may cause the value to
+   * escape.
+   */
+  JSObject* GetWrapperMaybeDead() const
   {
-    return GetWrapperJSObject();
+    return mWrapper;
   }
 
 #ifdef DEBUG
 private:
   static bool HasJSObjectMovedOp(JSObject* aWrapper);
 
 public:
 #endif
@@ -116,24 +129,33 @@ public:
     MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
     MOZ_ASSERT(HasJSObjectMovedOp(aWrapper),
                "Object has not provided the hook to update the wrapper if it is moved");
 
     SetWrapperJSObject(aWrapper);
   }
 
   /**
-   * Clear the wrapper. This should be called from the finalizer for the
-   * wrapper.
+   * Clear the cache.
    */
   void ClearWrapper()
   {
     MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
+    SetWrapperJSObject(nullptr);
+  }
 
-    SetWrapperJSObject(nullptr);
+  /**
+   * Clear the cache if it still contains a specific wrapper object. This should
+   * be called from the finalizer for the wrapper.
+   */
+  void ClearWrapper(JSObject* obj)
+  {
+    if (obj == mWrapper) {
+      ClearWrapper();
+    }
   }
 
   /**
    * Update the wrapper if the object it contains is moved.
    *
    * This method must be called from the objectMovedOp class extension hook for
    * any wrapper cached object.
    */
@@ -289,21 +311,16 @@ private:
   friend class nsWindowRoot;
   void SetIsNotDOMBinding()
   {
     MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING),
                "This flag should be set before creating any wrappers.");
     SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING);
   }
 
-  JSObject *GetWrapperJSObject() const
-  {
-    return mWrapper;
-  }
-
   void SetWrapperJSObject(JSObject* aWrapper);
 
   FlagsType GetWrapperFlags() const
   {
     return mFlags & kWrapperFlagsMask;
   }
 
   bool HasWrapperFlag(FlagsType aFlag) const
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -7,16 +7,31 @@
 #ifndef nsWrapperCacheInline_h___
 #define nsWrapperCacheInline_h___
 
 #include "nsWrapperCache.h"
 #include "js/GCAPI.h"
 #include "js/TracingAPI.h"
 
 inline JSObject*
+nsWrapperCache::GetWrapperPreserveColor() const
+{
+  JSObject* obj = mWrapper;
+  if (obj && js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
+    // The object has been found to be dead and is in the process of being
+    // finalized, so don't let the caller see it. As an optimisation, remove it
+    // from the cache so we don't have to do this check in future.
+    const_cast<nsWrapperCache*>(this)->ClearWrapper();
+    return nullptr;
+  }
+  MOZ_ASSERT(obj == mWrapper);
+  return obj;
+}
+
+inline JSObject*
 nsWrapperCache::GetWrapper() const
 {
     JSObject* obj = GetWrapperPreserveColor();
     if (obj) {
       JS::ExposeObjectToActiveJS(obj);
     }
     return obj;
 }
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -1,14 +1,16 @@
 [DEFAULT]
 support-files =
   audio.ogg
   empty.html
   file_audioLoop.html
   file_audioLoopInIframe.html
+  file_bug902350.html
+  file_bug902350_frame.html
   file_bug1011748_redirect.sjs
   file_bug1011748_OK.sjs
   file_bug1303838.html
   file_bug1303838_target.html
   file_bug1303838_with_iframe.html
   file_messagemanager_unload.html
   file_pluginAudio.html
   file_use_counter_outer.html
--- a/dom/base/test/browser_bug902350.js
+++ b/dom/base/test/browser_bug902350.js
@@ -1,15 +1,15 @@
 /*
  * Mixed Content Block frame navigates for target="_top" - Test for Bug 902350
  */
 
 
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
-const gHttpTestRoot = "https://example.com/tests/dom/base/test/";
+const gHttpTestRoot = "https://example.com/browser/dom/base/test/";
 var origBlockActive;
 var gTestBrowser = null;
 
 registerCleanupFunction(function() {
   // Set preferences back to their original values
   Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
 });
 
--- a/dom/base/test/file_bug902350.html
+++ b/dom/base/test/file_bug902350.html
@@ -6,14 +6,14 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 902350</title>
 </head>
 
 <body>
   <div id="framediv">
-    <iframe src="https://example.com/tests/dom/base/test/file_bug902350_frame.html" id="testing_frame"></iframe>
+    <iframe src="https://example.com/browser/dom/base/test/file_bug902350_frame.html" id="testing_frame"></iframe>
   </div>
 </body>
 </html>
 
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -116,18 +116,16 @@ support-files =
   file_bug708620-2.html
   file_bug708620.html
   file_bug769117.html
   file_bug782342.txt
   file_bug787778.sjs
   file_bug804395.jar
   file_bug869432.eventsource
   file_bug869432.eventsource^headers^
-  file_bug902350.html
-  file_bug902350_frame.html
   file_bug907892.html
   file_bug945152.jar
   file_bug1274806.html
   file_domwindowutils_animation.html
   file_general_document.html
   file_htmlserializer_1.html
   file_htmlserializer_1_bodyonly.html
   file_htmlserializer_1_format.html
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1313,28 +1313,30 @@ GetUseXBLScope(T* aParentObject)
 inline bool
 GetUseXBLScope(const ParentObject& aParentObject)
 {
   return aParentObject.mUseXBLScope;
 }
 
 template<class T>
 inline void
-ClearWrapper(T* p, nsWrapperCache* cache)
+ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj)
 {
-  cache->ClearWrapper();
+  JS::AutoAssertGCCallback inCallback;
+  cache->ClearWrapper(obj);
 }
 
 template<class T>
 inline void
-ClearWrapper(T* p, void*)
+ClearWrapper(T* p, void*, JSObject* obj)
 {
+  JS::AutoAssertGCCallback inCallback;
   nsWrapperCache* cache;
   CallQueryInterface(p, &cache);
-  ClearWrapper(p, cache);
+  ClearWrapper(p, cache, obj);
 }
 
 template<class T>
 inline void
 UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old)
 {
   JS::AutoAssertGCCallback inCallback;
   cache->UpdateWrapper(obj, old);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1647,40 +1647,41 @@ class CGAddPropertyHook(CGAbstractClassH
             // obviously can't preserve if we're not initialized.
             if (self && self->GetWrapperPreserveColor()) {
               PreserveWrapper(self);
             }
             return true;
             """)
 
 
-def finalizeHook(descriptor, hookName, freeOp):
+def finalizeHook(descriptor, hookName, freeOp, obj):
     finalize = ""
     if descriptor.wrapperCache:
-        finalize += "ClearWrapper(self, self);\n"
+        finalize += "ClearWrapper(self, self, %s);\n" % obj
     if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
         finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
     if descriptor.isGlobal():
-        finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
+        finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), %s);\n" % (freeOp, obj)
     finalize += ("AddForDeferredFinalization<%s>(self);\n" %
                  descriptor.nativeType)
     return CGIfWrapper(CGGeneric(finalize), "self")
 
 
 class CGClassFinalizeHook(CGAbstractClassHook):
     """
     A hook for finalize, used to release our native object.
     """
     def __init__(self, descriptor):
         args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
         CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
                                      'void', args)
 
     def generate_code(self):
-        return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
+        return finalizeHook(self.descriptor, self.name,
+                            self.args[0].name, self.args[1].name).define()
 
 
 class CGClassObjectMovedHook(CGAbstractClassHook):
     """
     A hook for objectMovedOp, used to update the wrapper cache when an object it
     is holding moves.
     """
     def __init__(self, descriptor):
@@ -12222,17 +12223,18 @@ class CGDOMJSProxyHandler_finalize(Class
         args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
         ClassMethod.__init__(self, "finalize", "void", args,
                              virtual=True, override=True, const=True)
         self.descriptor = descriptor
 
     def getBody(self):
         return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" %
                  (self.descriptor.nativeType, self.descriptor.nativeType)) +
-                finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
+                finalizeHook(self.descriptor, FINALIZE_HOOK_NAME,
+                             self.args[0].name, self.args[1].name).define())
 
 
 class CGDOMJSProxyHandler_getElements(ClassMethod):
     def __init__(self, descriptor):
         assert descriptor.supportsIndexedProperties()
 
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'proxy'),
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -41,16 +41,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
 NS_INTERFACE_MAP_END
 
 static void
 SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj)
 {
   SimpleGlobalObject* globalObject =
     static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
+  globalObject->ClearWrapper(obj);
   NS_RELEASE(globalObject);
 }
 
 static void
 SimpleGlobal_moved(JSObject *obj, const JSObject *old)
 {
   SimpleGlobalObject* globalObject =
     static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
--- a/dom/bindings/SimpleGlobalObject.h
+++ b/dom/bindings/SimpleGlobalObject.h
@@ -82,17 +82,17 @@ private:
   SimpleGlobalObject(JSObject *global, GlobalType type)
     : mType(type)
   {
     SetWrapper(global);
   }
 
   virtual ~SimpleGlobalObject()
   {
-    ClearWrapper();
+    MOZ_ASSERT(!GetWrapperMaybeDead());
   }
 
   const GlobalType mType;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -1468,16 +1468,21 @@ HTMLCanvasElement::InvalidateFromAsyncCa
   }
 
   element->InvalidateCanvasContent(nullptr);
 }
 
 void
 HTMLCanvasElement::StartVRPresentation()
 {
+  if (GetCurrentContextType() != CanvasContextType::WebGL1 &&
+      GetCurrentContextType() != CanvasContextType::WebGL2) {
+    return;
+  }
+
   WebGLContext* webgl = static_cast<WebGLContext*>(GetContextAtIndex(0));
   if (!webgl) {
     return;
   }
 
   if (!webgl->StartVRPresentation()) {
     return;
   }
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -424,34 +424,39 @@ NS_IMPL_ISUPPORTS(UploadLastDir::Content
 NS_IMETHODIMP
 UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
 {
   nsCOMPtr<nsIFile> localFile;
   nsAutoString prefStr;
 
   if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
     prefStr = Preferences::GetString("dom.input.fallbackUploadDir");
-    if (prefStr.IsEmpty()) {
-      // If no custom directory was set through the pref, default to
-      // "desktop" directory for each platform.
-      NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(localFile));
-    }
-  }
-
-  if (!localFile) {
-    if (prefStr.IsEmpty() && mResult) {
-      nsCOMPtr<nsIVariant> pref;
-      mResult->GetValue(getter_AddRefs(pref));
-      pref->GetAsAString(prefStr);
-    }
+  }
+
+  if (prefStr.IsEmpty() && mResult) {
+    nsCOMPtr<nsIVariant> pref;
+    mResult->GetValue(getter_AddRefs(pref));
+    pref->GetAsAString(prefStr);
+  }
+
+  if (!prefStr.IsEmpty()) {
     localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
-    localFile->InitWithPath(prefStr);
-  }
-
-  mFilePicker->SetDisplayDirectory(localFile);
+    if (localFile && NS_WARN_IF(NS_FAILED(localFile->InitWithPath(prefStr)))) {
+      localFile = nullptr;
+    }
+  }
+
+  if (localFile) {
+    mFilePicker->SetDisplayDirectory(localFile);
+  } else {
+    // If no custom directory was set through the pref, default to
+    // "desktop" directory for each platform.
+    mFilePicker->SetDisplaySpecialDirectory(NS_LITERAL_STRING(NS_OS_DESKTOP_DIR));
+  }
+
   mFilePicker->Open(mFpCallback);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref)
 {
   mResult = pref;
--- a/dom/html/test/test_filepicker_default_directory.html
+++ b/dom/html/test/test_filepicker_default_directory.html
@@ -42,25 +42,28 @@ var MockFilePicker = SpecialPowers.MockF
 MockFilePicker.init(window);
 
 // need to show the MockFilePicker so .displayDirectory gets set
 var f = document.getElementById("f");
 f.focus();
 
 var testIndex = 0;
 var tests = [
-  ["", defaultUploadDirectory.path],
-  [customUploadDirectory.path, customUploadDirectory.path]
+  ["", null, "Desk"],
+  [customUploadDirectory.path, customUploadDirectory.path, ""]
 ]
 
 MockFilePicker.showCallback = function(filepicker) {
-  info(SpecialPowers.wrap(MockFilePicker).displayDirectory.path);
+  if (tests[testIndex][1] === null) {
+    is(SpecialPowers.wrap(MockFilePicker).displayDirectory, null, "DisplayDirectory is null");
+  } else {
+    is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path, tests[testIndex][1], "DisplayDirectory matches the path");
+  }
 
-  is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path,
-  tests[testIndex][1]);
+  is(SpecialPowers.wrap(MockFilePicker).displaySpecialDirectory, tests[testIndex][2], "DisplaySpecialDirectory matches the path");
 
   if (++testIndex == tests.length) {
     MockFilePicker.cleanup();
     SimpleTest.finish();
   } else {
     launchNextTest();
   }
 }
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -255,16 +255,17 @@ FilePickerParent::CreateFilePicker()
 mozilla::ipc::IPCResult
 FilePickerParent::RecvOpen(const int16_t& aSelectedType,
                            const bool& aAddToRecentDocs,
                            const nsString& aDefaultFile,
                            const nsString& aDefaultExtension,
                            InfallibleTArray<nsString>&& aFilters,
                            InfallibleTArray<nsString>&& aFilterNames,
                            const nsString& aDisplayDirectory,
+                           const nsString& aDisplaySpecialDirectory,
                            const nsString& aOkButtonLabel)
 {
   if (!CreateFilePicker()) {
     Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
     return IPC_OK();
   }
 
   mFilePicker->SetAddToRecentDocs(aAddToRecentDocs);
@@ -279,16 +280,18 @@ FilePickerParent::RecvOpen(const int16_t
   mFilePicker->SetOkButtonLabel(aOkButtonLabel);
 
   if (!aDisplayDirectory.IsEmpty()) {
     nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
     if (localFile) {
       localFile->InitWithPath(aDisplayDirectory);
       mFilePicker->SetDisplayDirectory(localFile);
     }
+  } else if (!aDisplaySpecialDirectory.IsEmpty()) {
+    mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory);
   }
 
   mCallback = new FilePickerShownCallback(this);
 
   mFilePicker->Open(mCallback);
   return IPC_OK();
 }
 
--- a/dom/ipc/FilePickerParent.h
+++ b/dom/ipc/FilePickerParent.h
@@ -47,16 +47,17 @@ class FilePickerParent : public PFilePic
 
   virtual mozilla::ipc::IPCResult RecvOpen(const int16_t& aSelectedType,
                                            const bool& aAddToRecentDocs,
                                            const nsString& aDefaultFile,
                                            const nsString& aDefaultExtension,
                                            InfallibleTArray<nsString>&& aFilters,
                                            InfallibleTArray<nsString>&& aFilterNames,
                                            const nsString& aDisplayDirectory,
+                                           const nsString& aDisplaySpecialDirectory,
                                            const nsString& aOkButtonLabel) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   class FilePickerShownCallback : public nsIFilePickerShownCallback
   {
   public:
     explicit FilePickerShownCallback(FilePickerParent* aFilePickerParent)
--- a/dom/ipc/PFilePicker.ipdl
+++ b/dom/ipc/PFilePicker.ipdl
@@ -36,16 +36,17 @@ union MaybeInputData
 
 protocol PFilePicker
 {
   manager PBrowser;
 
 parent:
     async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile,
                nsString defaultExtension, nsString[] filters, nsString[] filterNames,
-               nsString displayDirectory, nsString okButtonLabel);
+               nsString displayDirectory, nsString displaySpecialDirectory,
+               nsString okButtonLabel);
 
 child:
     async __delete__(MaybeInputData data, int16_t result);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -14,16 +14,17 @@
 #include "nsIFileStreams.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIRunnable.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsISupportsPrimitives.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/BasePrincipal.h"
@@ -365,16 +366,33 @@ public:
   }
 
   NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl)
 
 private:
   ~DirectoryLockImpl();
 };
 
+class QuotaObject::StoragePressureRunnable final
+  : public Runnable
+{
+  const uint64_t mUsage;
+
+public:
+  explicit StoragePressureRunnable(uint64_t aUsage)
+    : mUsage(aUsage)
+  { }
+
+private:
+  ~StoragePressureRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
 class QuotaManager::CreateRunnable final
   : public BackgroundThreadObject
   , public Runnable
 {
   nsTArray<nsCOMPtr<nsIRunnable>> mCallbacks;
   nsString mBaseDirPath;
   RefPtr<QuotaManager> mManager;
   nsresult mResultCode;
@@ -2849,16 +2867,40 @@ ShutdownObserver::Observe(nsISupports* a
 
   return NS_OK;
 }
 
 /*******************************************************************************
  * Quota object
  ******************************************************************************/
 
+NS_IMETHODIMP
+QuotaObject::
+StoragePressureRunnable::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+  if (NS_WARN_IF(!obsSvc)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsISupportsPRUint64> wrapper =
+    do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
+  if (NS_WARN_IF(!wrapper)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  wrapper->SetData(mUsage);
+
+  obsSvc->NotifyObservers(wrapper, "QuotaManager::StoragePressure", u"");
+
+  return NS_OK;
+}
+
 void
 QuotaObject::AddRef()
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   if (!quotaManager) {
     NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
 
     ++mRefCnt;
@@ -2989,18 +3031,24 @@ QuotaObject::MaybeUpdateSize(int64_t aSi
     // This will block the thread without holding the lock while waitting.
 
     AutoTArray<RefPtr<DirectoryLockImpl>, 10> locks;
 
     uint64_t sizeToBeFreed =
       quotaManager->LockedCollectOriginsForEviction(delta, locks);
 
     if (!sizeToBeFreed) {
-      // XXX prompt for asking to delete persistent origins if there is any
-      // persistent origin.
+      MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+      // Notify pressure event.
+      RefPtr<StoragePressureRunnable> storagePressureRunnable =
+        new StoragePressureRunnable(quotaManager->mTemporaryStorageUsage);
+
+      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(storagePressureRunnable));
+
       return false;
     }
 
     NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
 
     {
       MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
 
--- a/dom/quota/QuotaObject.h
+++ b/dom/quota/QuotaObject.h
@@ -18,16 +18,18 @@ BEGIN_QUOTA_NAMESPACE
 class OriginInfo;
 class QuotaManager;
 
 class QuotaObject
 {
   friend class OriginInfo;
   friend class QuotaManager;
 
+  class StoragePressureRunnable;
+
 public:
   void
   AddRef();
 
   void
   Release();
 
   const nsAString&
--- a/dom/vr/test/mochitest.ini
+++ b/dom/vr/test/mochitest.ini
@@ -1,15 +1,17 @@
 [DEFAULT]
 support-files =
   VRSimulationDriver.js
   requestPresent.js
   runVRTest.js
   WebVRHelpers.js
 
+[test_vrDisplay_canvas2d.html]
+skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
 [test_vrDisplay_exitPresent.html]
 skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
 [test_vrDisplay_getFrameData.html]
 skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655
 [test_vrDisplay_onvrdisplaydeactivate_crosscontent.html]
 skip-if = true
 [test_vrDisplay_requestPresent.html]
 skip-if = true
new file mode 100644
--- /dev/null
+++ b/dom/vr/test/test_vrDisplay_canvas2d.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>VRDisplay Canvas2D</title>
+    <meta name="timeout" content="long"/>
+    <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="runVRTest.js"></script>
+  </head>
+  <body>
+    <script>
+      var vrDisplay;
+
+      function requestPresentTest() {
+        async_test(function (test) {
+          vrDisplay.requestAnimationFrame(callback);
+
+          function callback() {
+            vrDisplay.resetPose();
+            vrDisplay.getLayers();
+            vrDisplay.submitFrame();
+            vrDisplay.getEyeParameters("right");
+            test.done();
+          }
+        }, "Finish WebVR Canvas2D requestPresentTest.");
+      }
+
+      function startTest() {
+        promise_test((test) => {
+          var canvas = document.createElement('canvas');
+          (document.body || document.documentElement).appendChild(canvas);
+          var context = canvas.getContext('2d');
+          var img = document.createElement('img');
+          img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACw=";
+
+          return navigator.getVRDisplays().then((displays) => {
+            assert_equals(displays.length, 1, "displays.length must be one after attach.");
+            vrDisplay = displays[0];
+            var frameData = new VRFrameData();
+            return vrDisplay.requestPresent([{source: canvas}]).then(() => {
+              requestPresentTest();
+            });
+          });
+        }, "Finish running WebVR Canvas2D test.");
+      }
+
+      runVRTest(startTest);
+    </script>
+  </body>
+</html>
\ No newline at end of file
--- a/dom/vr/test/test_vrDisplay_exitPresent.html
+++ b/dom/vr/test/test_vrDisplay_exitPresent.html
@@ -13,17 +13,16 @@
       function testExitPresentOnOtherIframe(content) {
           return content.navigator.getVRDisplays().then((displays) => {
             content.vrDisplay = displays[0];
             return content.vrDisplay.exitPresent();
         });
       }
       var initVRPresentation = function(content) {
             return content.navigator.getVRDisplays().then((displays) => {
-            console.log("GetVRDisplay!!");
             content.vrDisplay = displays[0];
             content.canvas = content.document.createElement("canvas");
             content.canvas.id = "vrCanvas";
             return content.vrDisplay.requestPresent([{source:content.canvas}]);
           });
       }
       function startTest() {
         var ifr1 = document.getElementById("iframe1");
--- a/dom/vr/test/test_vrDisplay_getFrameData.html
+++ b/dom/vr/test/test_vrDisplay_getFrameData.html
@@ -107,17 +107,17 @@
                   assert_true(checkValueInFloat32Array(pose1.angularVelocity,
                                                        pose2.angularVelocity),
                               "pose.angularVelocity at a frame should be equal.");
 
                   assert_true(checkValueInFloat32Array(pose1.angularAcceleration,
                                                        pose2.angularAcceleration),
                               "pose.angularAcceleration at a frame should be equal.");
                   test.done();
-                };
+                }
               });
             }, "WebVR returns the same frameData within a frame fulfilled");
           }
 
           function insertNewFrameData() {
             var poseOrient = new Float32Array([-0.208, -0.017, 0.055, -0.930]);
             var posePos = new Float32Array([-0.261, 0.036, -0.150]);
             var poseAngVel = new Float32Array([0.018, -0.001, -0.003]);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -249,18 +249,17 @@ CrashStatsLogForwarder::UpdateStringsVec
   mIndex += 1;
   MOZ_ASSERT(mIndex >= 0);
 
   // index will count 0, 1, 2, ..., max-1, 1, 2, ..., max-1, 1, 2, ...
   int32_t index = mIndex ? (mIndex-1) % (mMaxCapacity-1) + 1 : 0;
   MOZ_ASSERT(index >= 0 && index < (int32_t)mMaxCapacity);
   MOZ_ASSERT(index <= mIndex && index <= (int32_t)mBuffer.size());
 
-  bool ignored;
-  double tStamp = (TimeStamp::NowLoRes()-TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits();
+  double tStamp = (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation()).ToSecondsSigDigits();
 
   // Checking for index >= mBuffer.size(), rather than index == mBuffer.size()
   // just out of paranoia, but we know index <= mBuffer.size().
   LoggingRecordEntry newEntry(mIndex,aString,tStamp);
   if (index >= static_cast<int32_t>(mBuffer.size())) {
     mBuffer.push_back(newEntry);
   } else {
     mBuffer[index] = newEntry;
--- a/image/imgLoader.h
+++ b/image/imgLoader.h
@@ -43,28 +43,26 @@ class imgCacheEntry
 public:
   imgCacheEntry(imgLoader* loader, imgRequest* request,
                 bool aForcePrincipalCheck);
   ~imgCacheEntry();
 
   nsrefcnt AddRef()
   {
     NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
-    MOZ_ASSERT(_mOwningThread.GetThread() == PR_GetCurrentThread(),
-      "imgCacheEntry addref isn't thread-safe!");
+    NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
     ++mRefCnt;
     NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this));
     return mRefCnt;
   }
 
   nsrefcnt Release()
   {
     NS_PRECONDITION(0 != mRefCnt, "dup release");
-    MOZ_ASSERT(_mOwningThread.GetThread() == PR_GetCurrentThread(),
-      "imgCacheEntry release isn't thread-safe!");
+    NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
     --mRefCnt;
     NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry");
     if (mRefCnt == 0) {
       mRefCnt = 1; /* stabilize */
       delete this;
       return 0;
     }
     return mRefCnt;
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -19,17 +19,17 @@
 #include "mozilla/ipc/ProtocolUtils.h"
 
 // ChannelImpl is used on the IPC thread, but constructed on a different thread,
 // so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly
 // different macro.
 #ifdef DEBUG
 #define ASSERT_OWNINGTHREAD(_class) \
   if (nsAutoOwningThread* owningThread = _mOwningThread.get()) {               \
-    NS_CheckThreadSafe(owningThread->GetThread(), #_class " not thread-safe"); \
+    owningThread->AssertOwnership(#_class " not thread-safe"); \
   }
 #else
 #define ASSERT_OWNINGTHREAD(_class) ((void)0)
 #endif
 
 namespace IPC {
 //------------------------------------------------------------------------------
 
--- a/ipc/mscom/InterceptorLog.cpp
+++ b/ipc/mscom/InterceptorLog.cpp
@@ -253,18 +253,17 @@ Logger::VariantToString(const VARIANT& a
     }
   }
 }
 
 /* static */ double
 Logger::GetElapsedTime()
 {
   TimeStamp ts = TimeStamp::Now();
-  bool inconsistent;
-  TimeDuration duration = ts - TimeStamp::ProcessCreation(inconsistent);
+  TimeDuration duration = ts - TimeStamp::ProcessCreation();
   return duration.ToMicroseconds();
 }
 
 void
 Logger::LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface)
 {
   if (FAILED(aResult)) {
     return;
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -496,16 +496,40 @@ JavaScriptShared::ConvertID(const JSIID&
     to->m3[3] = from.m3_3();
     to->m3[4] = from.m3_4();
     to->m3[5] = from.m3_5();
     to->m3[6] = from.m3_6();
     to->m3[7] = from.m3_7();
 }
 
 JSObject*
+JavaScriptShared::findCPOWById(const ObjectId& objId)
+{
+    JSObject* obj = findCPOWByIdPreserveColor(objId);
+    if (obj)
+        JS::ExposeObjectToActiveJS(obj);
+    return obj;
+}
+
+JSObject*
+JavaScriptShared::findCPOWByIdPreserveColor(const ObjectId& objId)
+{
+    JSObject* obj = cpows_.findPreserveColor(objId);
+    if (!obj)
+        return nullptr;
+
+    if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
+        cpows_.remove(objId);
+        return nullptr;
+    }
+
+    return obj;
+}
+
+JSObject*
 JavaScriptShared::findObjectById(JSContext* cx, const ObjectId& objId)
 {
     RootedObject obj(cx, objects_.find(objId));
     if (!obj) {
         JS_ReportErrorASCII(cx, "operation not possible on dead CPOW");
         return nullptr;
     }
 
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -168,24 +168,24 @@ class JavaScriptShared : public CPOWMana
     bool convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId id);
 
     virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) = 0;
     virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) = 0;
 
     static void ConvertID(const nsID& from, JSIID* to);
     static void ConvertID(const JSIID& from, nsID* to);
 
-    JSObject* findCPOWById(const ObjectId& objId) {
-        return cpows_.find(objId);
-    }
+    JSObject* findCPOWById(const ObjectId& objId);
+    JSObject* findCPOWByIdPreserveColor(const ObjectId& objId);
     JSObject* findObjectById(JSContext* cx, const ObjectId& objId);
 
 #ifdef DEBUG
     bool hasCPOW(const ObjectId& objId, const JSObject* obj) {
-        return cpows_.has(objId, obj);
+        MOZ_ASSERT(obj);
+        return findCPOWByIdPreserveColor(objId) == obj;
     }
 #endif
 
     static bool LoggingEnabled() { return sLoggingEnabled; }
     static bool StackLoggingEnabled() { return sStackLoggingEnabled; }
 
     friend class Logging;
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -914,19 +914,22 @@ CPOWProxyHandler::isConstructor(JSObject
 {
     AuxCPOWData* aux = AuxCPOWDataOf(proxy);
     return aux->isConstructor;
 }
 
 void
 WrapperOwner::drop(JSObject* obj)
 {
-    ObjectId objId = idOf(obj);
+    // The association may have already been swept from the table but if it's
+    // there then remove it.
+    ObjectId objId = idOfUnchecked(obj);
+    if (cpows_.findPreserveColor(objId) == obj)
+        cpows_.remove(objId);
 
-    cpows_.remove(objId);
     if (active())
         Unused << SendDropObject(objId);
     decref();
 }
 
 void
 WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
 {
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -651,37 +651,60 @@ ExposeGCThingToActiveJS(JS::GCCellPtr th
     if (IsIncrementalBarrierNeededOnTenuredGCThing(thing))
         JS::IncrementalReadBarrier(thing);
     else if (js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()))
         JS::UnmarkGrayGCThingRecursively(thing);
 
     MOZ_ASSERT(!js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()));
 }
 
+template <typename T>
+extern JS_PUBLIC_API(bool)
+EdgeNeedsSweepUnbarrieredSlow(T* thingp);
+
+static MOZ_ALWAYS_INLINE bool
+EdgeNeedsSweepUnbarriered(JSObject** objp)
+{
+    // This function does not handle updating nursery pointers. Raw JSObject
+    // pointers should be updated separately or replaced with
+    // JS::Heap<JSObject*> which handles this automatically.
+    MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
+    if (IsInsideNursery(reinterpret_cast<Cell*>(*objp)))
+        return false;
+
+    auto zone = JS::shadow::Zone::asShadowZone(detail::GetGCThingZone(uintptr_t(*objp)));
+    if (!zone->isGCSweepingOrCompacting())
+        return false;
+
+    return EdgeNeedsSweepUnbarrieredSlow(objp);
+}
+
 } /* namespace gc */
 } /* namespace js */
 
 namespace JS {
 
 /*
  * This should be called when an object that is marked gray is exposed to the JS
  * engine (by handing it to running JS code or writing it into live JS
  * data). During incremental GC, since the gray bits haven't been computed yet,
  * we conservatively mark the object black.
  */
 static MOZ_ALWAYS_INLINE void
 ExposeObjectToActiveJS(JSObject* obj)
 {
     MOZ_ASSERT(obj);
+    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&obj));
     js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
 }
 
 static MOZ_ALWAYS_INLINE void
 ExposeScriptToActiveJS(JSScript* script)
 {
+    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&script));
     js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
 }
 
 /*
  * Internal to Firefox.
  *
  * Note: this is not related to the PokeGC in nsJSEnvironment.
  */
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -96,29 +96,39 @@ const uint32_t DefaultNurseryBytes = 16 
 
 /* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
 const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
 
 namespace shadow {
 
 struct Zone
 {
+    enum GCState : uint8_t {
+        NoGC,
+        Mark,
+        MarkGray,
+        Sweep,
+        Finished,
+        Compact
+    };
+
   protected:
     JSRuntime* const runtime_;
     JSTracer* const barrierTracer_;     // A pointer to the JSRuntime's |gcMarker|.
-
-  public:
     bool needsIncrementalBarrier_;
+    GCState gcState_;
 
     Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
       : runtime_(runtime),
         barrierTracer_(barrierTracerArg),
-        needsIncrementalBarrier_(false)
+        needsIncrementalBarrier_(false),
+        gcState_(NoGC)
     {}
 
+  public:
     bool needsIncrementalBarrier() const {
         return needsIncrementalBarrier_;
     }
 
     JSTracer* barrierTracer() {
         MOZ_ASSERT(needsIncrementalBarrier_);
         MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
         return barrierTracer_;
@@ -130,16 +140,25 @@ struct Zone
     }
 
     // Note: Unrestricted access to the zone's runtime from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     JSRuntime* runtimeFromAnyThread() const {
         return runtime_;
     }
 
+    GCState gcState() const { return gcState_; }
+    bool wasGCStarted() const { return gcState_ != NoGC; }
+    bool isGCMarkingBlack() { return gcState_ == Mark; }
+    bool isGCMarkingGray() { return gcState_ == MarkGray; }
+    bool isGCSweeping() { return gcState_ == Sweep; }
+    bool isGCFinished() { return gcState_ == Finished; }
+    bool isGCCompacting() { return gcState_ == Compact; }
+    bool isGCSweepingOrCompacting() { return gcState_ == Sweep || gcState_ == Compact; }
+
     static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) {
         return reinterpret_cast<JS::shadow::Zone*>(zone);
     }
 };
 
 } /* namespace shadow */
 
 /**
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -203,14 +203,11 @@ SetContextProfilingStack(JSContext* cx, 
                          uint32_t max);
 
 JS_FRIEND_API(void)
 EnableContextProfilingStack(JSContext* cx, bool enabled);
 
 JS_FRIEND_API(void)
 RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
 
-JS_FRIEND_API(jsbytecode*)
-ProfilingGetPC(JSContext* cx, JSScript* script, void* ip);
-
 } // namespace js
 
 #endif  /* js_ProfilingStack_h */
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -955,16 +955,20 @@ IsOptimizedPlaceholderMagicValue(const V
         return true;
     }
     return false;
 }
 
 static MOZ_ALWAYS_INLINE void
 ExposeValueToActiveJS(const Value& v)
 {
+#ifdef DEBUG
+    Value tmp = v;
+    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp));
+#endif
     if (v.isGCThing())
         js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
 }
 
 /************************************************************************/
 
 static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value
 NullValue()
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -25,18 +25,17 @@
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 static double
 MillisecondsSinceStartup()
 {
     auto now = mozilla::TimeStamp::Now();
-    bool ignored;
-    return (now - mozilla::TimeStamp::ProcessCreation(ignored)).ToMilliseconds();
+    return (now - mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
 }
 
 enum PromiseHandler {
     PromiseHandlerIdentity = 0,
     PromiseHandlerThrower,
     PromiseHandlerAsyncFunctionAwaitFulfilled,
     PromiseHandlerAsyncFunctionAwaitRejected,
     PromiseHandlerAsyncGeneratorAwaitFulfilled,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4313,19 +4313,18 @@ AflLoop(JSContext* cx, unsigned argc, Va
     return true;
 }
 #endif
 
 static bool
 TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    bool ignore;
-    double when = (mozilla::TimeStamp::Now()
-                   - mozilla::TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+    double when = (mozilla::TimeStamp::Now() -
+                   mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
     args.rval().setNumber(when);
     return true;
 }
 
 static bool
 GetErrorNotes(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/devtools/rootAnalysis/README.md
+++ b/js/src/devtools/rootAnalysis/README.md
@@ -1,14 +1,67 @@
 # Spidermonkey JSAPI rooting analysis
 
 This directory contains scripts for running Brian Hackett's static GC rooting
-analysis on a JS source directory.
+and thread heap write safety analyses on a JS source directory.
+
+The easiest way to get this running is to not try to do the instrumented
+compilation locally. Instead, grab the relevant files from a try server push
+and analyze them locally.
+
+Local Analysis of Downloaded Intermediate Files
+
+1. Do a try push with "--upload-xdbs" appended to the try: ..." line.
+
+2. Create an empty directory to run the analysis.
+
+3. When the try job is complete, download the resulting src_body.xdb.bz2, src_comp.xdb.bz2,
+and file_source.xdb.bz2 files into your directory.
+
+4. Build an optimized JS shell with ctypes. Note that this does not need to
+match the source you are analyzing in any way; in fact, you pretty much never
+need to update this once you've built it. (Though I reserve the right to use
+any new JS features implemented in Spidermonkey in the future...)
+
+    mkdir <objdir>
+    cd <objdir>
+    <srcpath>/js/src/configure --disable-debug --enable-optimize --enable-ctypes
+    make -j6 -s
+
+5. Clone and build sixgill:
 
-To use it on SpiderMonkey:
+    hg clone https://hg.mozilla.org/users/sfink_mozilla.com/sixgill
+    cd sixgill
+    ./release.sh --build
+
+If you are on osx, the sixgill build will fail horribly. Let it, then do
+
+    make bin/xdb.so CXX=clang++
+
+6. Make a defaults.py file containing the following, with your own paths filled in:
+
+    js = "<objdir>/dist/bin/js"
+    sixgill_bin = "<sixgill-dir>/bin"
+
+7a. For the rooting analysis, run
+
+    python <srcdir>/js/src/devtools/rootAnalysis/analyze.py gcTypes
+
+7b. For the heap write analysis, run
+
+    python <srcdir>/js/src/devtools/rootAnalysis/analyze.py heapwrites
+
+Also, you may wish to run with -v (aka --verbose) to see the exact commands
+executed that you can cut & paste if needed. (I use them to run under the JS
+debugger when I'm working on the analysis.)
+
+----
+
+Or if you *do* want to run the full analysis locally, then you may face the
+dragons. To use it on SpiderMonkey:
 
 1.  Be on Fedora/CentOS/RedHat Linux x86_64, or a Docker image of one of those.
 
     Specifically, the prebuilt GCC **won't work on Ubuntu**
     without the `CFLAGS` and `CXXFLAGS` settings from
     <http://trac.wildfiregames.com/wiki/StaticRootingAnalysis>.
 
 2.  Have the Gecko build prerequisites installed.
--- a/js/src/devtools/rootAnalysis/analyze.py
+++ b/js/src/devtools/rootAnalysis/analyze.py
@@ -11,16 +11,26 @@ Runs the static rooting analysis
 
 from subprocess import Popen
 import subprocess
 import os
 import argparse
 import sys
 import re
 
+# Python 2/3 version independence polyfills
+
+anystring_t = str if sys.version_info[0] > 2 else basestring
+
+try:
+    execfile
+except:
+    def execfile(thefile, globals):
+        exec(compile(open(thefile).read(), filename=thefile, mode="exec"), globals)
+
 def env(config):
     e = dict(os.environ)
     e['PATH'] = ':'.join(p for p in (config.get('gcc_bin'), config.get('sixgill_bin'), e['PATH']) if p)
     e['XDB'] = '%(sixgill_bin)s/xdb.so' % config
     e['SOURCE'] = config['source']
     e['ANALYZED_OBJDIR'] = config['objdir']
     return e
 
@@ -56,17 +66,17 @@ def print_command(command, outfile=None,
                     outputs.append('%s="%s${%s}%s"' % (key,
                                                        value[:start],
                                                        key,
                                                        value[end:]))
                 else:
                     outputs.append("%s='%s'" % (key, value))
             output = ' '.join(outputs) + " " + output
 
-    print output
+    print(output)
 
 def generate_hazards(config, outfilename):
     jobs = []
     for i in range(int(config['jobs'])):
         command = fill(('%(js)s',
                         '%(analysis_scriptdir)s/analyzeRoots.js',
                         '%(gcFunctions_list)s',
                         '%(gcEdges)s',
@@ -93,17 +103,17 @@ def generate_hazards(config, outfilename
 
     with open(outfilename, 'w') as output:
         command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(int(config['jobs'])) ]
         if config['verbose']:
             print_command(command, outfile=outfilename)
         subprocess.call(command, stdout=output)
 
 JOBS = { 'dbs':
-             (('%(ANALYSIS_SCRIPTDIR)s/run_complete',
+             (('%(analysis_scriptdir)s/run_complete',
                '--foreground',
                '--no-logs',
                '--build-root=%(objdir)s',
                '--wrap-dir=%(sixgill)s/scripts/wrap_gcc',
                '--work-dir=work',
                '-b', '%(sixgill_bin)s',
                '--buildcommand=%(buildcommand)s',
                '.'),
@@ -155,17 +165,17 @@ def out_indexes(command):
 def run_job(name, config):
     cmdspec, outfiles = JOBS[name]
     print("Running " + name + " to generate " + str(outfiles))
     if hasattr(cmdspec, '__call__'):
         cmdspec(config, outfiles)
     else:
         temp_map = {}
         cmdspec = fill(cmdspec, config)
-        if isinstance(outfiles, basestring):
+        if isinstance(outfiles, anystring_t):
             stdout_filename = '%s.tmp' % name
             temp_map[stdout_filename] = outfiles
             if config['verbose']:
                 print_command(cmdspec, outfile=outfiles, env=env(config))
         else:
             stdout_filename = None
             pc = list(cmdspec)
             outfile = 0
@@ -190,19 +200,19 @@ def run_job(name, config):
                 subprocess.check_call(command, stdout=output, env=env(config))
         for (temp, final) in temp_map.items():
             try:
                 os.rename(temp, final)
             except OSError:
                 print("Error renaming %s -> %s" % (temp, final))
                 raise
 
-config = { 'ANALYSIS_SCRIPTDIR': os.path.dirname(__file__) }
+config = { 'analysis_scriptdir': os.path.dirname(__file__) }
 
-defaults = [ '%s/defaults.py' % config['ANALYSIS_SCRIPTDIR'],
+defaults = [ '%s/defaults.py' % config['analysis_scriptdir'],
              '%s/defaults.py' % os.getcwd() ]
 
 parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.')
 parser.add_argument('step', metavar='STEP', type=str, nargs='?',
                     help='run starting from this step')
 parser.add_argument('--source', metavar='SOURCE', type=str, nargs='?',
                     help='source code to analyze')
 parser.add_argument('--objdir', metavar='DIR', type=str, nargs='?',
@@ -255,19 +265,24 @@ elif 'BUILD' in os.environ:
 else:
     data['buildcommand'] = 'make -j4 -s'
 
 if 'ANALYZED_OBJDIR' in os.environ:
     data['objdir'] = os.environ['ANALYZED_OBJDIR']
 
 if 'SOURCE' in os.environ:
     data['source'] = os.environ['SOURCE']
-if not data.get('source') and data.get('sixgill_bin'):
-    path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp'])
-    data['source'] = path.replace("\n", "").replace("/js/src/jsapi.cpp", "")
+
+if data.get('sixgill_bin'):
+    if not data.get('source'):
+        path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp']).decode()
+        data['source'] = path.replace("\n", "").replace("/js/src/jsapi.cpp", "")
+    if not data.get('objdir'):
+        path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.h']).decode()
+        data['objdir'] = path.replace("\n", "").replace("/jsapi.h", "")
 
 steps = [ 'dbs',
           'gcTypes',
           'callgraph',
           'gcFunctions',
           'allFunctions',
           'hazards',
           'explain',
@@ -279,17 +294,17 @@ if args.list:
         if outfilename:
             print("%s -> %s" % (step, outfilename))
         else:
             print(step)
     sys.exit(0)
 
 for step in steps:
     command, outfiles = JOBS[step]
-    if isinstance(outfiles, basestring):
+    if isinstance(outfiles, anystring_t):
         data[step] = outfiles
     else:
         outfile = 0
         for (i, name) in out_indexes(command):
             data[name] = outfiles[outfile]
             outfile += 1
         assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files (%d) and params (%d)' % (step, outfile, len(outfiles))
 
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -1070,41 +1070,54 @@ function isSafeVariable(entry, variable)
         // Hopefully the construction code doesn't leak pointers to the object
         // to places where other threads might access it.
         if (isDirectCall(edge, /operator new/) ||
             isDirectCall(edge, /nsCSSValue::Array::Create/))
         {
             return true;
         }
 
-        // References to the contents of an array are threadsafe if the array
-        // itself is threadsafe.
-        if ((isDirectCall(edge, /operator\[\]/) ||
-             isDirectCall(edge, /nsStyleContent::ContentAt/)) &&
-            isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
-        {
-            return true;
-        }
-
-        // Watch for the coerced result of a getter_AddRefs call.
-        if (isDirectCall(edge, /operator /)) {
-            var otherEdge = expressionValueEdge(edge.PEdgeCallInstance.Exp);
-            if (otherEdge &&
-                isDirectCall(otherEdge, /getter_AddRefs/) &&
-                isEdgeSafeArgument(entry, otherEdge.PEdgeCallArguments.Exp[0]))
+        if ("PEdgeCallInstance" in edge) {
+            // References to the contents of an array are threadsafe if the array
+            // itself is threadsafe.
+            if ((isDirectCall(edge, /operator\[\]/) ||
+                 isDirectCall(edge, /nsStyleContent::ContentAt/)) &&
+                isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
             {
                 return true;
             }
-        }
+
+            // Watch for the coerced result of a getter_AddRefs call.
+            if (isDirectCall(edge, /operator /)) {
+                var otherEdge = expressionValueEdge(edge.PEdgeCallInstance.Exp);
+                if (otherEdge &&
+                    isDirectCall(otherEdge, /getter_AddRefs/) &&
+                    isEdgeSafeArgument(entry, otherEdge.PEdgeCallArguments.Exp[0]))
+                {
+                    return true;
+                }
+            }
 
-        // Coercion via AsAString preserves safety.
-        if (isDirectCall(edge, /AsAString/) &&
-            isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
-        {
-            return true;
+            // Placement-new returns a pointer that is as safe as the pointer
+            // passed to it. Exp[0] is the size, Exp[1] is the pointer/address.
+            // Note that the invocation of the constructor is a separate call,
+            // and so need not be considered here.
+            if (isDirectCall(edge, /operator new/) &&
+                edge.PEdgeCallInstance.Exp.length == 2 &&
+                isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp[1]))
+            {
+                return true;
+            }
+
+            // Coercion via AsAString preserves safety.
+            if (isDirectCall(edge, /AsAString/) &&
+                isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
+            {
+                return true;
+            }
         }
 
         // Watch out for variables which were assigned arguments.
         var rhsVariable = variableAssignRhs(edge);
         if (rhsVariable)
             return isSafeVariable(entry, rhsVariable);
     }
 
--- a/js/src/devtools/rootAnalysis/utility.js
+++ b/js/src/devtools/rootAnalysis/utility.js
@@ -221,21 +221,23 @@ function xdbLibrary()
     } catch (e) {
         // lookup_key is for development use only and is not strictly necessary.
     }
     return api;
 }
 
 function cLibrary()
 {
+    var libPossibilities = ['libc.so.6', 'libc.so', 'libc.dylib'];
     var lib;
-    try {
-        lib = ctypes.open("libc.so.6");
-    } catch(e) {
-        lib = ctypes.open("libc.so");
+    for (const name of libPossibilities) {
+        try {
+            lib = ctypes.open("libc.so.6");
+        } catch(e) {
+        }
     }
 
     return {
         fopen: lib.declare("fopen", ctypes.default_abi, ctypes.void_t.ptr, ctypes.char.ptr, ctypes.char.ptr),
         getline: lib.declare("getline", ctypes.default_abi, ctypes.ssize_t, ctypes.char.ptr.ptr, ctypes.size_t.ptr, ctypes.void_t.ptr),
         fclose: lib.declare("fclose", ctypes.default_abi, ctypes.int, ctypes.void_t.ptr),
         free: lib.declare("free", ctypes.default_abi, ctypes.void_t, ctypes.void_t.ptr),
     };
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -458,17 +458,17 @@ UsedNameTracker::rewind(RewindToken toke
         r.front().value().resetToScope(token.scriptId, token.scopeId);
 }
 
 FunctionBox::FunctionBox(JSContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead,
                          JSFunction* fun, uint32_t toStringStart,
                          Directives directives, bool extraWarnings,
                          GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
   : ObjectBox(fun, traceListHead),
-    SharedContext(cx, Kind::ObjectBox, directives, extraWarnings),
+    SharedContext(cx, Kind::FunctionBox, directives, extraWarnings),
     enclosingScope_(nullptr),
     namedLambdaBindings_(nullptr),
     functionScopeBindings_(nullptr),
     extraVarScopeBindings_(nullptr),
     functionNode(nullptr),
     bufStart(0),
     bufEnd(0),
     startLine(1),
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -226,17 +226,17 @@ class SharedContext
     JSContext* const context;
     AnyContextFlags anyCxFlags;
     bool strictScript;
     bool localStrict;
     bool extraWarnings;
 
   protected:
     enum class Kind {
-        ObjectBox,
+        FunctionBox,
         Global,
         Eval,
         Module
     };
 
     Kind kind_;
 
     ThisBinding thisBinding_;
@@ -266,25 +266,23 @@ class SharedContext
         inWith_(false),
         needsThisTDZChecks_(false)
     { }
 
     // If this is the outermost SharedContext, the Scope that encloses
     // it. Otherwise nullptr.
     virtual Scope* compilationEnclosingScope() const = 0;
 
-    virtual ObjectBox* toObjectBox() { return nullptr; }
-    bool isObjectBox() { return toObjectBox(); }
-    bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
+    bool isFunctionBox() const { return kind_ == Kind::FunctionBox; }
     inline FunctionBox* asFunctionBox();
-    bool isModuleContext() { return kind_ == Kind::Module; }
+    bool isModuleContext() const { return kind_ == Kind::Module; }
     inline ModuleSharedContext* asModuleContext();
-    bool isGlobalContext() { return kind_ == Kind::Global; }
+    bool isGlobalContext() const { return kind_ == Kind::Global; }
     inline GlobalSharedContext* asGlobalContext();
-    bool isEvalContext() { return kind_ == Kind::Eval; }
+    bool isEvalContext() const { return kind_ == Kind::Eval; }
     inline EvalSharedContext* asEvalContext();
 
     ThisBinding thisBinding()          const { return thisBinding_; }
 
     bool allowNewTarget()              const { return allowNewTarget_; }
     bool allowSuperProperty()          const { return allowSuperProperty_; }
     bool allowSuperCall()              const { return allowSuperCall_; }
     bool inWith()                      const { return inWith_; }
@@ -449,17 +447,16 @@ class FunctionBox : public ObjectBox, pu
         MOZ_ASSERT(context->keepAtoms);
         return MutableHandle<VarScope::Data*>::fromMarkedLocation(&extraVarScopeBindings_);
     }
 
     void initFromLazyFunction();
     void initStandaloneFunction(Scope* enclosingScope);
     void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
 
-    ObjectBox* toObjectBox() override { return this; }
     JSFunction* function() const { return &object->as<JSFunction>(); }
 
     Scope* compilationEnclosingScope() const override {
         // This method is used to distinguish the outermost SharedContext. If
         // a FunctionBox is the outermost SharedContext, it must be a lazy
         // function.
         MOZ_ASSERT_IF(function()->isInterpretedLazy(),
                       enclosingScope_ == function()->lazyScript()->enclosingScope());
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -976,18 +976,18 @@ class GCRuntime
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
     void markAllWeakReferences(gcstats::Phase phase);
     void markAllGrayReferences(gcstats::Phase phase);
 
-    void beginSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
-    void groupZonesForSweeping(AutoLockForExclusiveAccess& lock);
+    void beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
+    void groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool findInterZoneEdges();
     void getNextSweepGroup();
     void endMarkingSweepGroup();
     void beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock);
     bool shouldReleaseObservedTypes();
     void endSweepingSweepGroup();
     IncrementalProgress performSweepActions(SliceBudget& sliceBudget,
                                             AutoLockForExclusiveAccess& lock);
@@ -1438,17 +1438,18 @@ GCRuntime::upcomingZealousGC() {
 inline bool
 GCRuntime::needZealousGC() {
     if (nextScheduled > 0 && --nextScheduled == 0) {
         if (hasZealMode(ZealMode::Alloc) ||
             hasZealMode(ZealMode::GenerationalGC) ||
             hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
             hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
             hasZealMode(ZealMode::IncrementalMultipleSlices) ||
-            hasZealMode(ZealMode::Compact))
+            hasZealMode(ZealMode::Compact) ||
+            hasZealMode(ZealMode::IncrementalSweepThenFinish))
         {
             nextScheduled = zealFrequency;
         }
         return true;
     }
     return false;
 }
 #else
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -3148,18 +3148,18 @@ IsAboutToBeFinalizedInternal(T** thingp)
     T* thing = *thingp;
     JSRuntime* rt = thing->runtimeFromAnyThread();
 
     /* Permanent atoms are never finalized by non-owning runtimes. */
     if (ThingIsPermanentAtomOrWellKnownSymbol(thing) && TlsContext.get()->runtime() != rt)
         return false;
 
     if (IsInsideNursery(thing)) {
-        MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
-        return !Nursery::getForwardedPointer(reinterpret_cast<JSObject**>(thingp));
+        return JS::CurrentThreadIsHeapMinorCollecting() &&
+               !Nursery::getForwardedPointer(reinterpret_cast<JSObject**>(thingp));
     }
 
     Zone* zone = thing->asTenured().zoneFromAnyThread();
     if (zone->isGCSweeping()) {
         return IsAboutToBeFinalizedDuringSweep(thing->asTenured());
     } else if (zone->isGCCompacting() && IsForwarded(thing)) {
         *thingp = Forwarded(thing);
         return false;
@@ -3225,25 +3225,33 @@ IsAboutToBeFinalized(ReadBarrieredBase<T
 
 template <typename T>
 JS_PUBLIC_API(bool)
 EdgeNeedsSweep(JS::Heap<T>* thingp)
 {
     return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeGet()));
 }
 
+template <typename T>
+JS_PUBLIC_API(bool)
+EdgeNeedsSweepUnbarrieredSlow(T* thingp)
+{
+    return IsAboutToBeFinalizedInternal(ConvertToBase(thingp));
+}
+
 // Instantiate a copy of the Tracing templates for each derived type.
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
     template bool IsMarkedUnbarriered<type>(JSRuntime*, type*);                \
     template bool IsMarked<type>(JSRuntime*, WriteBarrieredBase<type>*); \
     template bool IsAboutToBeFinalizedUnbarriered<type>(type*); \
     template bool IsAboutToBeFinalized<type>(WriteBarrieredBase<type>*); \
     template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*);
 #define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \
-    template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*);
+    template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*); \
+    template JS_PUBLIC_API(bool) EdgeNeedsSweepUnbarrieredSlow<type>(type*);
 FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
 #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
 
 } /* namespace gc */
 } /* namespace js */
 
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -273,18 +273,17 @@ js::gc::GCRuntime::traceRuntimeForMinorG
     // despite having called FinishRoots already. This is because FinishRoots
     // does not clear the crossCompartmentWrapper map. It cannot do this
     // because Proxy's trace for CrossCompartmentWrappers asserts presence in
     // the map. And we can reach its trace function despite having finished the
     // roots via the edges stored by the pre-barrier verifier when we finish
     // the verifier for the last time.
     gcstats::AutoPhase ap(stats(), gcstats::PHASE_MARK_ROOTS);
 
-    // FIXME: As per bug 1298816 comment 12, we should be able to remove this.
-    jit::JitRuntime::TraceJitcodeGlobalTable(trc);
+    jit::JitRuntime::TraceJitcodeGlobalTableForMinorGC(trc);
 
     traceRuntimeCommon(trc, TraceRuntime, lock);
 }
 
 void
 js::TraceRuntime(JSTracer* trc)
 {
     MOZ_ASSERT(!trc->isMarkingTracer());
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -742,18 +742,17 @@ Statistics::formatJsonSliceDescription(u
 {
     TimeDuration duration = slice.duration();
     lldiv_t durationParts = SplitDurationMS(duration);
     TimeDuration when = slice.start - slices[0].start;
     lldiv_t whenParts = SplitDurationMS(when);
     char budgetDescription[200];
     slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
     int64_t pageFaults = slice.endFaults - slice.startFaults;
-    bool ignore;
-    TimeStamp originTime = TimeStamp::ProcessCreation(ignore);
+    TimeStamp originTime = TimeStamp::ProcessCreation();
 
     const char* format =
         "\"slice\":%d,"
         "\"pause\":%llu.%03llu,"
         "\"when\":%llu.%03llu,"
         "\"reason\":\"%s\","
         "\"initial_state\":\"%s\","
         "\"final_state\":\"%s\","
@@ -1024,19 +1023,18 @@ LongestPhaseSelfTime(const Statistics::P
 void
 Statistics::printStats()
 {
     if (aborted) {
         fprintf(fp, "OOM during GC statistics collection. The report is unavailable for this GC.\n");
     } else {
         UniqueChars msg = formatDetailedMessage();
         if (msg) {
-            bool ignoredInconsistency;
             double secSinceStart =
-                (slices[0].start - TimeStamp::ProcessCreation(ignoredInconsistency)).ToSeconds();
+                (slices[0].start - TimeStamp::ProcessCreation()).ToSeconds();
             fprintf(fp, "GC(T+%.3fs) %s\n", secSinceStart, msg.get());
         }
     }
     fflush(fp);
 }
 
 void
 Statistics::beginGC(JSGCInvocationKind kind)
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -652,17 +652,18 @@ CheckGrayMarkingTracer::checkCell(Cell* 
         return;
 
     TenuredCell* tenuredCell = &cell->asTenured();
     TenuredCell* tenuredParent = &parent->asTenured();
     if (tenuredParent->isMarked(BLACK) && !tenuredParent->isMarked(GRAY) &&
         tenuredCell->isMarked(GRAY))
     {
         failures++;
-        fprintf(stderr, "Found black to gray edge %p\n", cell);
+        fprintf(stderr, "Found black to gray edge to %s %p\n",
+                GCTraceKindToAscii(cell->getTraceKind()), cell);
         dumpCellPath();
     }
 }
 
 bool
 CheckGrayMarkingTracer::check(AutoLockForExclusiveAccess& lock)
 {
     if (!traceHeap(lock))
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -50,17 +50,16 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup*
     baseShapes_(group, this, BaseShapeSet()),
     initialShapes_(group, this, InitialShapeSet()),
     data(group, nullptr),
     isSystem(group, false),
 #ifdef DEBUG
     gcLastSweepGroupIndex(group, 0),
 #endif
     jitZone_(group, nullptr),
-    gcState_(NoGC),
     gcScheduled_(false),
     gcPreserveCode_(group, false),
     jitUsingBarriers_(group, false),
     keepShapeTables_(group, false),
     listNext_(group, NotOnList)
 {
     /* Ensure that there are no vtables to mess us up here. */
     MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -210,24 +210,16 @@ struct Zone : public JS::shadow::Zone,
 
     void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
     bool isPreservingCode() const { return gcPreserveCode_; }
 
     bool canCollect();
 
     void notifyObservingDebuggers();
 
-    enum GCState {
-        NoGC,
-        Mark,
-        MarkGray,
-        Sweep,
-        Finished,
-        Compact
-    };
     void setGCState(GCState state) {
         MOZ_ASSERT(CurrentThreadIsHeapBusy());
         MOZ_ASSERT_IF(state != NoGC, canCollect());
         gcState_ = state;
         if (state == Finished)
             notifyObservingDebuggers();
     }
 
@@ -252,25 +244,16 @@ struct Zone : public JS::shadow::Zone,
 
     bool isGCMarking() {
         if (CurrentThreadIsHeapCollecting())
             return gcState_ == Mark || gcState_ == MarkGray;
         else
             return needsIncrementalBarrier();
     }
 
-    GCState gcState() const { return gcState_; }
-    bool wasGCStarted() const { return gcState_ != NoGC; }
-    bool isGCMarkingBlack() { return gcState_ == Mark; }
-    bool isGCMarkingGray() { return gcState_ == MarkGray; }
-    bool isGCSweeping() { return gcState_ == Sweep; }
-    bool isGCFinished() { return gcState_ == Finished; }
-    bool isGCCompacting() { return gcState_ == Compact; }
-    bool isGCSweepingOrCompacting() { return gcState_ == Sweep || gcState_ == Compact; }
-
     // Get a number that is incremented whenever this zone is collected, and
     // possibly at other times too.
     uint64_t gcNumber();
 
     bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); }
     bool compileBarriers(bool needsIncrementalBarrier) const {
         return needsIncrementalBarrier ||
                runtimeFromActiveCooperatingThread()->hasZealMode(js::gc::ZealMode::VerifierPre);
@@ -616,17 +599,16 @@ struct Zone : public JS::shadow::Zone,
     }
     void setKeepShapeTables(bool b) {
         keepShapeTables_ = b;
     }
 
   private:
     js::ZoneGroupData<js::jit::JitZone*> jitZone_;
 
-    js::UnprotectedData<GCState> gcState_;
     js::ActiveThreadData<bool> gcScheduled_;
     js::ZoneGroupData<bool> gcPreserveCode_;
     js::ZoneGroupData<bool> jitUsingBarriers_;
     js::ZoneGroupData<bool> keepShapeTables_;
 
     // Allow zones to be linked into a list
     friend class js::gc::ZoneList;
     static Zone * const NotOnList;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug1359612.js
@@ -0,0 +1,9 @@
+load(libdir + 'asm.js');
+
+asmLink(asmCompile('stdlib', 'foreign', USE_ASM + `
+  var ff = foreign.ff;
+  function f() {
+      ff(+1);
+  }
+  return f
+`), this, { ff: Math.log1p });
--- a/js/src/jit-test/tests/gc/incremental-state.js
+++ b/js/src/jit-test/tests/gc/incremental-state.js
@@ -22,42 +22,45 @@ gcslice(1);
 assertEq(gcstate(), "Mark");
 gcslice(1000000);
 assertEq(gcstate(), "Mark");
 gcslice(1000000);
 while (gcstate() == "Finalize") { gcslice(1); }
 while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
 
-// Zeal mode 8: Incremental GC in two main slices:
+// Zeal mode 8: Incremental GC in two slices:
 //   1) mark roots
 //   2) mark and sweep
-//   *) finalize.
 gczeal(8, 0);
 gcslice(1);
 assertEq(gcstate(), "Mark");
 gcslice(1);
-while (gcstate() == "Finalize") { gcslice(1); }
-while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
 
-// Zeal mode 9: Incremental GC in two main slices:
+// Zeal mode 9: Incremental GC in two slices:
 //   1) mark roots and marking
 //   2) new marking and sweeping
-//   *) finalize.
 gczeal(9, 0);
 gcslice(1);
 assertEq(gcstate(), "Mark");
 gcslice(1);
-while (gcstate() == "Finalize") { gcslice(1); }
-while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
 
 // Zeal mode 10: Incremental GC in multiple slices (always yeilds before
 // sweeping). This test uses long slices to prove that this zeal mode yields
 // in sweeping, where normal IGC (above) does not.
 gczeal(10, 0);
 gcslice(1000000);
 assertEq(gcstate(), "Sweep");
 gcslice(1000000);
 while (gcstate() == "Finalize") { gcslice(1); }
 while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
+
+// Zeal mode 17: Incremental GC in two slices:
+//   1) mark everything and start sweeping
+//   2) finish sweeping
+gczeal(17, 0);
+gcslice(1);
+assertEq(gcstate(), "Sweep");
+gcslice(1);
+assertEq(gcstate(), "NotActive");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/builtin-import-sigs.js
@@ -0,0 +1,15 @@
+var code = wasmTextToBinary(`(module
+    (import $one "" "builtin")
+    (import $two "" "builtin" (param i32))
+    (import $three "" "builtin" (result i32))
+    (import $four "" "builtin" (result f32) (param f32 f32 f32 f32 f32 f32 f32 f32 f32 f32 f32 f32))
+    (func (export "run")
+        (call $one)
+        (call $two (i32.const 0))
+        (drop (call $three))
+        (drop (call $four (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0)))
+    )
+)`);
+var m = new WebAssembly.Module(code);
+var i = new WebAssembly.Instance(m, {'':{builtin:Math.sin}});
+i.exports.run();
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -609,23 +609,23 @@ JitRuntime::Trace(JSTracer* trc, AutoLoc
     Zone* zone = trc->runtime()->atomsCompartment(lock)->zone();
     for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
         JitCode* code = i;
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
-JitRuntime::TraceJitcodeGlobalTable(JSTracer* trc)
+JitRuntime::TraceJitcodeGlobalTableForMinorGC(JSTracer* trc)
 {
     if (trc->runtime()->geckoProfiler().enabled() &&
         trc->runtime()->hasJitRuntime() &&
         trc->runtime()->jitRuntime()->hasJitcodeGlobalTable())
     {
-        trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->trace(trc);
+        trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->traceForMinorGC(trc);
     }
 }
 
 /* static */ bool
 JitRuntime::MarkJitcodeGlobalTableIteratively(GCMarker* marker)
 {
     if (marker->runtime()->hasJitRuntime() &&
         marker->runtime()->jitRuntime()->hasJitcodeGlobalTable())
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12011,18 +12011,20 @@ IonBuilder::jsop_lambda(JSFunction* fun)
 AbortReasonOr<Ok>
 IonBuilder::jsop_lambda_arrow(JSFunction* fun)
 {
     MOZ_ASSERT(analysis().usesEnvironmentChain());
     MOZ_ASSERT(fun->isArrow());
     MOZ_ASSERT(!fun->isNative());
 
     MDefinition* newTargetDef = current->pop();
+    MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
+    current->add(cst);
     MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->environmentChain(),
-                                          newTargetDef, fun);
+                                          newTargetDef, cst);
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_setfunname(uint8_t prefixKind)
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -181,17 +181,17 @@ class JitRuntime
     }
 
   public:
     explicit JitRuntime(JSRuntime* rt);
     ~JitRuntime();
     MOZ_MUST_USE bool initialize(JSContext* cx, js::AutoLockForExclusiveAccess& lock);
 
     static void Trace(JSTracer* trc, js::AutoLockForExclusiveAccess& lock);
-    static void TraceJitcodeGlobalTable(JSTracer* trc);
+    static void TraceJitcodeGlobalTableForMinorGC(JSTracer* trc);
     static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(GCMarker* marker);
     static void SweepJitcodeGlobalTable(JSRuntime* rt);
 
     ExecutableAllocator& execAlloc() {
         return execAlloc_.ref();
     }
     ExecutableAllocator& backedgeExecAlloc() {
         return backedgeExecAlloc_.ref();
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -447,17 +447,17 @@ JitcodeGlobalTable::lookupForSamplerInfa
         JitcodeGlobalEntry& rejoinEntry = RejoinEntry(rt, entry->ionCacheEntry(), ptr);
         rejoinEntry.setGeneration(sampleBufferGen);
     }
 
     // JitcodeGlobalEntries are marked at the end of the mark phase. A read
     // barrier is not needed. Any JS frames sampled during the sweep phase of
     // the GC must be on stack, and on-stack frames must already be marked at
     // the beginning of the sweep phase. It's not possible to assert this here
-    // as we may not be off thread when called from the gecko profiler.
+    // as we may be off main thread when called from the gecko profiler.
 
     return *entry;
 }
 
 JitcodeGlobalEntry*
 JitcodeGlobalTable::lookupInternal(void* ptr)
 {
     JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr);
@@ -523,25 +523,34 @@ JitcodeGlobalTable::addEntry(const Jitco
             searchTowerEntry->tower_->setNext(level, newEntry);
         } else {
             newTower->setNext(level, startTower_[level]);
             startTower_[level] = newEntry;
         }
     }
     skiplistSize_++;
     // verifySkiplist(); - disabled for release.
+
+    // Any entries that may directly contain nursery pointers must be marked
+    // during a minor GC to update those pointers.
+    if (entry.canHoldNurseryPointers())
+        addToNurseryList(&newEntry->ionEntry());
+
     return true;
 }
 
 void
 JitcodeGlobalTable::removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower,
                                 JSRuntime* rt)
 {
     MOZ_ASSERT(!TlsContext.get()->isProfilerSamplingEnabled());
 
+    if (entry.canHoldNurseryPointers())
+        removeFromNurseryList(&entry.ionEntry());
+
     // Unlink query entry.
     for (int level = entry.tower_->height() - 1; level >= 0; level--) {
         JitcodeGlobalEntry* prevTowerEntry = prevTower[level];
         if (prevTowerEntry) {
             MOZ_ASSERT(prevTowerEntry->tower_->next(level) == &entry);
             prevTowerEntry->tower_->setNext(level, entry.tower_->next(level));
         } else {
             startTower_[level] = entry.tower_->next(level);
@@ -710,37 +719,46 @@ JitcodeGlobalTable::verifySkiplist()
     MOZ_ASSERT(count == skiplistSize_);
 }
 #endif // DEBUG
 
 void
 JitcodeGlobalTable::setAllEntriesAsExpired(JSRuntime* rt)
 {
     AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
-    for (Range r(*this); !r.empty(); r.popFront())
-        r.front()->setAsExpired();
+    for (Range r(*this); !r.empty(); r.popFront()) {
+        auto entry = r.front();
+        if (entry->canHoldNurseryPointers())
+            removeFromNurseryList(&entry->ionEntry());
+        entry->setAsExpired();
+    }
 }
 
 struct Unconditionally
 {
     template <typename T>
     static bool ShouldTrace(JSRuntime* rt, T* thingp) { return true; }
 };
 
 void
-JitcodeGlobalTable::trace(JSTracer* trc)
+JitcodeGlobalTable::traceForMinorGC(JSTracer* trc)
 {
-    // Trace all entries unconditionally. This is done during minor collection
-    // to tenure and update object pointers.
+    // Trace only entries that can directly contain nursery pointers.
 
     MOZ_ASSERT(trc->runtime()->geckoProfiler().enabled());
+    MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
 
     AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
-    for (Range r(*this); !r.empty(); r.popFront())
-        r.front()->trace<Unconditionally>(trc);
+    JitcodeGlobalEntry::IonEntry* entry = nurseryEntries_;
+    while (entry) {
+        entry->trace<Unconditionally>(trc);
+        JitcodeGlobalEntry::IonEntry* prev = entry;
+        entry = entry->nextNursery_;
+        removeFromNurseryList(prev);
+    }
 }
 
 struct IfUnmarked
 {
     template <typename T>
     static bool ShouldTrace(JSRuntime* rt, T* thingp) { return !IsMarkedUnbarriered(rt, thingp); }
 };
 
@@ -792,16 +810,18 @@ JitcodeGlobalTable::markIteratively(GCMa
         // If an entry is not sampled, reset its generation to the invalid
         // generation, and conditionally mark the rest of the entry if its
         // JitCode is not already marked. This conditional marking ensures
         // that so long as the JitCode *may* be sampled, we keep any
         // information that may be handed out to the sampler, like tracked
         // types used by optimizations and scripts used for pc to line number
         // mapping, alive as well.
         if (!entry->isSampled(gen, lapCount)) {
+            if (entry->canHoldNurseryPointers())
+                removeFromNurseryList(&entry->ionEntry());
             entry->setAsExpired();
             if (!entry->baseEntry().isJitcodeMarkedFromAnyThread(marker->runtime()))
                 continue;
         }
 
         // The table is runtime-wide. Not all zones may be participating in
         // the GC.
         if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -233,16 +233,22 @@ class JitcodeGlobalEntry
         const IonTrackedOptimizationsRegionTable* optsRegionTable_;
         const IonTrackedOptimizationsTypesTable* optsTypesTable_;
         const IonTrackedOptimizationsAttemptsTable* optsAttemptsTable_;
 
         // The types table above records type sets, which have been gathered
         // into one vector here.
         IonTrackedTypeVector* optsAllTypes_;
 
+        // Linked list pointers to allow traversing through all entries that
+        // could possibly contain nursery pointers. Note that the contained
+        // pointers can be mutated into nursery pointers at any time.
+        IonEntry* prevNursery_;
+        IonEntry* nextNursery_;
+
         struct ScriptNamePair {
             JSScript* script;
             char* str;
         };
 
         struct SizedScriptList {
             uint32_t size;
             ScriptNamePair pairs[1];
@@ -267,16 +273,17 @@ class JitcodeGlobalEntry
             MOZ_ASSERT(regionTable);
             BaseEntry::init(Ion, code, nativeStartAddr, nativeEndAddr);
             regionTable_ = regionTable;
             scriptList_ = scriptList;
             optsRegionTable_ = nullptr;
             optsTypesTable_ = nullptr;
             optsAllTypes_ = nullptr;
             optsAttemptsTable_ = nullptr;
+            prevNursery_ = nextNursery_ = nullptr;
         }
 
         void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable* regionTable,
                                       const IonTrackedOptimizationsTypesTable* typesTable,
                                       const IonTrackedOptimizationsAttemptsTable* attemptsTable,
                                       IonTrackedTypeVector* allTypes)
         {
             optsRegionTable_ = regionTable;
@@ -558,41 +565,41 @@ class JitcodeGlobalEntry
   public:
     JitcodeGlobalEntry()
       : tower_(nullptr)
     {
         base_.init();
     }
 
     explicit JitcodeGlobalEntry(const IonEntry& ion)
-      : tower_(nullptr)
+      : JitcodeGlobalEntry()
     {
         ion_ = ion;
     }
 
     explicit JitcodeGlobalEntry(const BaselineEntry& baseline)
-      : tower_(nullptr)
+      : JitcodeGlobalEntry()
     {
         baseline_ = baseline;
     }
 
     explicit JitcodeGlobalEntry(const IonCacheEntry& ionCache)
-      : tower_(nullptr)
+      : JitcodeGlobalEntry()
     {
         ionCache_ = ionCache;
     }
 
     explicit JitcodeGlobalEntry(const DummyEntry& dummy)
-      : tower_(nullptr)
+      : JitcodeGlobalEntry()
     {
         dummy_ = dummy;
     }
 
     explicit JitcodeGlobalEntry(const QueryEntry& query)
-      : tower_(nullptr)
+      : JitcodeGlobalEntry()
     {
         query_ = query;
     }
 
     static JitcodeGlobalEntry MakeQuery(void* ptr) {
         QueryEntry query;
         query.init(ptr);
         return JitcodeGlobalEntry(query);
@@ -835,16 +842,20 @@ class JitcodeGlobalEntry
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
         return false;
     }
 
+    bool canHoldNurseryPointers() const {
+        return isIon() && ionEntry().hasTrackedOptimizations();
+    }
+
     mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(
             JSRuntime *rt,
             void* addr,
             uint32_t* entryOffsetOut)
     {
         switch (kind()) {
           case Ion:
             return ionEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut);
@@ -1005,23 +1016,25 @@ class JitcodeGlobalTable
 {
   private:
     static const size_t LIFO_CHUNK_SIZE = 16 * 1024;
 
     LifoAlloc alloc_;
     JitcodeGlobalEntry* freeEntries_;
     uint32_t rand_;
     uint32_t skiplistSize_;
+    JitcodeGlobalEntry::IonEntry* nurseryEntries_;
 
     JitcodeGlobalEntry* startTower_[JitcodeSkiplistTower::MAX_HEIGHT];
     JitcodeSkiplistTower* freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT];
 
   public:
     JitcodeGlobalTable()
-      : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0)
+      : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0),
+        nurseryEntries_(nullptr)
     {
         for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
             startTower_[i] = nullptr;
         for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
             freeTowers_[i] = nullptr;
     }
     ~JitcodeGlobalTable() {}
 
@@ -1054,17 +1067,17 @@ class JitcodeGlobalTable
     MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::DummyEntry& entry, JSRuntime* rt) {
         return addEntry(JitcodeGlobalEntry(entry), rt);
     }
 
     void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt);
     void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt);
 
     void setAllEntriesAsExpired(JSRuntime* rt);
-    void trace(JSTracer* trc);
+    void traceForMinorGC(JSTracer* trc);
     MOZ_MUST_USE bool markIteratively(GCMarker* marker);
     void sweep(JSRuntime* rt);
 
   private:
     MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt);
 
     JitcodeGlobalEntry* lookupInternal(void* ptr);
 
@@ -1086,16 +1099,39 @@ class JitcodeGlobalTable
     JitcodeGlobalEntry* allocateEntry();
 
 #ifdef DEBUG
     void verifySkiplist();
 #else
     void verifySkiplist() {}
 #endif
 
+    void addToNurseryList(JitcodeGlobalEntry::IonEntry* entry) {
+        MOZ_ASSERT(entry->prevNursery_ == nullptr);
+        MOZ_ASSERT(entry->nextNursery_ == nullptr);
+
+        entry->nextNursery_ = nurseryEntries_;
+        if (nurseryEntries_)
+            nurseryEntries_->prevNursery_ = entry;
+        nurseryEntries_ = entry;
+    }
+
+    void removeFromNurseryList(JitcodeGlobalEntry::IonEntry* entry) {
+        // Splice out of list to be scanned on a minor GC.
+        if (entry->prevNursery_)
+            entry->prevNursery_->nextNursery_ = entry->nextNursery_;
+        if (entry->nextNursery_)
+            entry->nextNursery_->prevNursery_ = entry->prevNursery_;
+
+        if (nurseryEntries_ == entry)
+            nurseryEntries_ = entry->nextNursery_;
+
+        entry->prevNursery_ = entry->nextNursery_ = nullptr;
+    }
+
   public:
     class Range
     {
       protected:
         JitcodeGlobalTable& table_;
         JitcodeGlobalEntry* cur_;
 
       public:
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8518,39 +8518,47 @@ class MLambda
         return true;
     }
     bool appendRoots(MRootList& roots) const override {
         return info_.appendRoots(roots);
     }
 };
 
 class MLambdaArrow
-  : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data
+  : public MTernaryInstruction,
+    public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2>>::Data
 {
     const LambdaFunctionInfo info_;
 
     MLambdaArrow(CompilerConstraintList* constraints, MDefinition* envChain,
-                 MDefinition* newTarget_, JSFunction* fun)
-      : MBinaryInstruction(envChain, newTarget_), info_(fun)
+                 MDefinition* newTarget, MConstant* cst)
+      : MTernaryInstruction(envChain, newTarget, cst),
+        info_(&cst->toObject().as<JSFunction>())
     {
         setResultType(MIRType::Object);
-        MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun));
-        if (!fun->isSingleton())
-            setResultTypeSet(MakeSingletonTypeSet(constraints, fun));
+        MOZ_ASSERT(!ObjectGroup::useSingletonForClone(info().fun));
+        if (!info().fun->isSingleton())
+            setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun));
     }
 
   public:
     INSTRUCTION_HEADER(LambdaArrow)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, environmentChain), (1, newTargetDef))
 
+    MConstant* functionOperand() const {
+        return getOperand(2)->toConstant();
+    }
     const LambdaFunctionInfo& info() const {
         return info_;
     }
+    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+    bool canRecoverOnBailout() const override {
+        return true;
+    }
     bool appendRoots(MRootList& roots) const override {
         return info_.appendRoots(roots);
     }
 };
 
 class MSetFunName
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1473,16 +1473,45 @@ RLambda::recover(JSContext* cx, Snapshot
 
     RootedValue result(cx);
     result.setObject(*resultObject);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MLambdaArrow::writeRecoverData(CompactBufferWriter& writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_LambdaArrow));
+    return true;
+}
+
+RLambdaArrow::RLambdaArrow(CompactBufferReader& reader)
+{
+}
+
+bool
+RLambdaArrow::recover(JSContext* cx, SnapshotIterator& iter) const
+{
+    RootedObject scopeChain(cx, &iter.read().toObject());
+    RootedValue newTarget(cx, iter.read());
+    RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
+
+    JSObject* resultObject = js::LambdaArrow(cx, fun, scopeChain, newTarget);
+    if (!resultObject)
+        return false;
+
+    RootedValue result(cx);
+    result.setObject(*resultObject);
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
 MSimdBox::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_SimdBox));
     static_assert(unsigned(SimdType::Count) < 0x100, "assuming SimdType fits in 8 bits");
     writer.writeByte(uint8_t(simdType()));
     return true;
 }
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -101,16 +101,17 @@ namespace jit {
     _(TruncateToInt32)                          \
     _(NewObject)                                \
     _(NewTypedArray)                            \
     _(NewArray)                                 \
     _(NewArrayIterator)                         \
     _(NewDerivedTypedObject)                    \
     _(CreateThisWithTemplate)                   \
     _(Lambda)                                   \
+    _(LambdaArrow)                              \
     _(SimdBox)                                  \
     _(ObjectState)                              \
     _(ArrayState)                               \
     _(AtomicIsLockFree)                         \
     _(AssertRecoveredOnBailout)
 
 class RResumePoint;
 class SnapshotIterator;
@@ -621,16 +622,24 @@ class RCreateThisWithTemplate final : pu
 class RLambda final : public RInstruction
 {
   public:
     RINSTRUCTION_HEADER_NUM_OP_(Lambda, 2)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
 };
 
+class RLambdaArrow final : public RInstruction
+{
+  public:
+    RINSTRUCTION_HEADER_NUM_OP_(LambdaArrow, 3)
+
+    MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
+};
+
 class RSimdBox final : public RInstruction
 {
   private:
     uint8_t type_;
 
   public:
     RINSTRUCTION_HEADER_NUM_OP_(SimdBox, 1)
 
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -96,18 +96,19 @@ EmulateStateOf<MemoryView>::run(MemoryVi
 }
 
 static bool
 IsObjectEscaped(MInstruction* ins, JSObject* objDefault = nullptr);
 
 // Returns False if the lambda is not escaped and if it is optimizable by
 // ScalarReplacementOfObject.
 static bool
-IsLambdaEscaped(MLambda* lambda, JSObject* obj)
+IsLambdaEscaped(MInstruction* lambda, JSObject* obj)
 {
+    MOZ_ASSERT(lambda->isLambda() || lambda->isLambdaArrow());
     JitSpewDef(JitSpew_Escape, "Check lambda\n", lambda);
     JitSpewIndent spewIndent(JitSpew_Escape);
 
     // The scope chain is not escaped if none of the Lambdas which are
     // capturing it are escaped.
     for (MUseIterator i(lambda->usesBegin()); i != lambda->usesEnd(); i++) {
         MNode* consumer = (*i)->consumer();
         if (!consumer->isDefinition()) {
@@ -237,20 +238,20 @@ IsObjectEscaped(MInstruction* ins, JSObj
             }
             if (IsObjectEscaped(def->toInstruction(), obj)) {
                 JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def);
                 return true;
             }
             break;
           }
 
-          case MDefinition::Op_Lambda: {
-            MLambda* lambda = def->toLambda();
-            if (IsLambdaEscaped(lambda, obj)) {
-                JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", lambda);
+          case MDefinition::Op_Lambda:
+          case MDefinition::Op_LambdaArrow: {
+            if (IsLambdaEscaped(def->toInstruction(), obj)) {
+                JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def);
                 return true;
             }
             break;
           }
 
           // This instruction is a no-op used to verify that scalar replacement
           // is working as expected in jit-test.
           case MDefinition::Op_AssertRecoveredOnBailout:
@@ -307,16 +308,17 @@ class ObjectMemoryView : public MDefinit
     void visitStoreFixedSlot(MStoreFixedSlot* ins);
     void visitLoadFixedSlot(MLoadFixedSlot* ins);
     void visitPostWriteBarrier(MPostWriteBarrier* ins);
     void visitStoreSlot(MStoreSlot* ins);
     void visitLoadSlot(MLoadSlot* ins);
     void visitGuardShape(MGuardShape* ins);
     void visitFunctionEnvironment(MFunctionEnvironment* ins);
     void visitLambda(MLambda* ins);
+    void visitLambdaArrow(MLambdaArrow* ins);
     void visitStoreUnboxedScalar(MStoreUnboxedScalar* ins);
     void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins);
     void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins);
     void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins);
     void visitStoreUnboxedString(MStoreUnboxedString* ins);
     void visitLoadUnboxedString(MLoadUnboxedString* ins);
 
   private:
@@ -475,17 +477,17 @@ ObjectMemoryView::assertSuccess()
         // Resume points have been replaced by the object state.
         if (ins->isResumePoint() || (def = ins->toDefinition())->isRecoveredOnBailout()) {
             MOZ_ASSERT(obj_->isIncompleteObject());
             continue;
         }
 
         // The only remaining uses would be removed by DCE, which will also
         // recover the object on bailouts.
-        MOZ_ASSERT(def->isSlots() || def->isLambda());
+        MOZ_ASSERT(def->isSlots() || def->isLambda() || def->isLambdaArrow());
         MOZ_ASSERT(!def->hasDefUses());
     }
 }
 #endif
 
 void
 ObjectMemoryView::visitResumePoint(MResumePoint* rp)
 {
@@ -637,18 +639,25 @@ ObjectMemoryView::visitGuardShape(MGuard
     ins->block()->discard(ins);
 }
 
 void
 ObjectMemoryView::visitFunctionEnvironment(MFunctionEnvironment* ins)
 {
     // Skip function environment which are not aliases of the NewCallObject.
     MDefinition* input = ins->input();
-    if (!input->isLambda() || input->toLambda()->environmentChain() != obj_)
+    if (input->isLambda()) {
+        if (input->toLambda()->environmentChain() != obj_)
+            return;
+    } else if (input->isLambdaArrow()) {
+        if (input->toLambdaArrow()->environmentChain() != obj_)
+            return;
+    } else {
         return;
+    }
 
     // Replace the function environment by the scope chain of the lambda.
     ins->replaceAllUsesWith(obj_);
 
     // Remove original instruction.
     ins->block()->discard(ins);
 }
 
@@ -658,16 +667,25 @@ ObjectMemoryView::visitLambda(MLambda* i
     if (ins->environmentChain() != obj_)
         return;
 
     // In order to recover the lambda we need to recover the scope chain, as the
     // lambda is holding it.
     ins->setIncompleteObject();
 }
 
+void
+ObjectMemoryView::visitLambdaArrow(MLambdaArrow* ins)
+{
+    if (ins->environmentChain() != obj_)
+        return;
+
+    ins->setIncompleteObject();
+}
+
 static size_t
 GetOffsetOf(MDefinition* index, size_t width, int32_t baseOffset)
 {
     int32_t idx = index->toConstant()->toInt32();
     MOZ_ASSERT(idx >= 0);
     MOZ_ASSERT(baseOffset >= 0 && size_t(baseOffset) >= UnboxedPlainObject::offsetOfData());
     return idx * width + baseOffset - UnboxedPlainObject::offsetOfData();
 }
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -677,17 +677,19 @@ MacroAssembler::callWithABIPre(uint32_t*
 
     // ARM64 /really/ wants the stack to always be aligned.  Since we're already tracking it
     // getting it aligned for an abi call is pretty easy.
     MOZ_ASSERT(dynamicAlignment_);
     stackForCall += ComputeByteAlignment(stackForCall, StackAlignment);
     *stackAdjust = stackForCall;
     reserveStack(*stackAdjust);
     {
-        moveResolver_.resolve();
+        enoughMemory_ &= moveResolver_.resolve();
+        if (!enoughMemory_)
+            return;
         MoveEmitter emitter(*this);
         emitter.emit(moveResolver_);
         emitter.finish();
     }
 
     // Call boundaries communicate stack via sp.
     syncStackPtr();
 }
--- a/js/src/jit/arm64/vixl/Debugger-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Debugger-vixl.cpp
@@ -25,16 +25,17 @@
 // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "js-config.h"
 
 #ifdef JS_SIMULATOR_ARM64
 
 #include "jit/arm64/vixl/Debugger-vixl.h"
 
+#include "mozilla/Unused.h"
 #include "mozilla/Vector.h"
 
 #include "jsalloc.h"
 
 namespace vixl {
 
 // List of commands supported by the debugger.
 #define DEBUG_COMMAND_LIST(C)  \
@@ -1104,36 +1105,37 @@ bool DebugCommand::Match(const char* nam
     }
   }
 
   return false;
 }
 
 
 DebugCommand* DebugCommand::Parse(char* line) {
+  using mozilla::Unused;
   TokenVector args;
 
   for (char* chunk = strtok(line, " \t");
        chunk != NULL;
        chunk = strtok(NULL, " \t")) {
     char* dot = strchr(chunk, '.');
     if (dot != NULL) {
       // 'Token.format'.
       Token* format = FormatToken::Tokenize(dot + 1);
       if (format != NULL) {
         *dot = '\0';
-        args.append(Token::Tokenize(chunk));
-        args.append(format);
+        Unused << args.append(Token::Tokenize(chunk));
+        Unused << args.append(format);
       } else {
         // Error while parsing the format, push the UnknownToken so an error
         // can be accurately reported.
-        args.append(Token::Tokenize(chunk));
+        Unused << args.append(Token::Tokenize(chunk));
       }
     } else {
-      args.append(Token::Tokenize(chunk));
+      Unused << args.append(Token::Tokenize(chunk));
     }
   }
 
   if (args.empty()) {
     return NULL;
   }
 
   if (!args[0]->IsIdentifier()) {
--- a/js/src/jit/arm64/vixl/Decoder-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Decoder-vixl.cpp
@@ -107,49 +107,49 @@ void Decoder::DecodeInstruction(const In
       //      Advanced SIMD.
       case 0xE:
       case 0xF: DecodeFP(instr); break;
     }
   }
 }
 
 void Decoder::AppendVisitor(DecoderVisitor* new_visitor) {
-  visitors_.append(new_visitor);
+  MOZ_ALWAYS_TRUE(visitors_.append(new_visitor));
 }
 
 
 void Decoder::PrependVisitor(DecoderVisitor* new_visitor) {
-  visitors_.insert(visitors_.begin(), new_visitor);
+  MOZ_ALWAYS_TRUE(visitors_.insert(visitors_.begin(), new_visitor));
 }
 
 
 void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor,
                                   DecoderVisitor* registered_visitor) {
   for (auto it = visitors_.begin(); it != visitors_.end(); it++) {
     if (*it == registered_visitor) {
-      visitors_.insert(it, new_visitor);
+      MOZ_ALWAYS_TRUE(visitors_.insert(it, new_visitor));
       return;
     }
   }
   // We reached the end of the list without finding registered_visitor.
-  visitors_.append(new_visitor);
+  MOZ_ALWAYS_TRUE(visitors_.append(new_visitor));
 }
 
 
 void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor,
                                  DecoderVisitor* registered_visitor) {
   for (auto it = visitors_.begin(); it != visitors_.end(); it++) {
     if (*it == registered_visitor) {
       it++;
-      visitors_.insert(it, new_visitor);
+      MOZ_ALWAYS_TRUE(visitors_.insert(it, new_visitor));
       return;
     }
   }
   // We reached the end of the list without finding registered_visitor.
-  visitors_.append(new_visitor);
+  MOZ_ALWAYS_TRUE(visitors_.append(new_visitor));
 }
 
 
 void Decoder::RemoveVisitor(DecoderVisitor* visitor) {
   visitors_.erase(std::remove(visitors_.begin(), visitors_.end(), visitor),
                   visitors_.end());
 }
 
--- a/js/src/jit/arm64/vixl/Instrument-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Instrument-vixl.cpp
@@ -21,16 +21,18 @@
 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "jit/arm64/vixl/Instrument-vixl.h"
 
+#include "mozilla/Unused.h"
+
 namespace vixl {
 
 Counter::Counter(const char* name, CounterType type)
     : count_(0), enabled_(false), type_(type) {
   VIXL_ASSERT(name != NULL);
   strncpy(name_, name, kCounterNameMaxLength);
 }
 
@@ -134,17 +136,17 @@ Instrument::Instrument(const char* dataf
 
   // Dump an instrumentation description comment at the top of the file.
   fprintf(output_stream_, "# counters=%d\n", num_counters);
   fprintf(output_stream_, "# sample_period=%" PRIu64 "\n", sample_period_);
 
   // Construct Counter objects from counter description array.
   for (int i = 0; i < num_counters; i++) {
     if (Counter* counter = js_new<Counter>(kCounterList[i].name, kCounterList[i].type))
-      counters_.append(counter);
+      mozilla::Unused << counters_.append(counter);
   }
 
   DumpCounterNames();
 }
 
 
 Instrument::~Instrument() {
   // Dump any remaining instruction data to the output file.
--- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
+++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
@@ -24,16 +24,17 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "mozilla/DebugOnly.h"
 
 #include "jit/arm64/vixl/Debugger-vixl.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
 #include "jit/IonTypes.h"
+#include "js/Utility.h"
 #include "threading/LockGuard.h"
 #include "vm/Runtime.h"
 #include "wasm/WasmCode.h"
 
 js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
 
 namespace vixl {
 
@@ -422,19 +423,22 @@ void Simulator::VisitException(const Ins
       }
     case SVC:
       // The SVC instruction is hijacked by the JIT as a pseudo-instruction
       // causing the Simulator to execute host-native code for callWithABI.
       switch (instr->ImmException()) {
         case kCallRtRedirected:
           VisitCallRedirection(instr);
           return;
-        case kMarkStackPointer:
-          spStack_.append(xreg(31, Reg31IsStackPointer));
+        case kMarkStackPointer: {
+          js::AutoEnterOOMUnsafeRegion oomUnsafe;
+          if (!spStack_.append(xreg(31, Reg31IsStackPointer)))
+            oomUnsafe.crash("tracking stack for ARM64 simulator");
           return;
+        }
         case kCheckStackPointer: {
           int64_t current = xreg(31, Reg31IsStackPointer);
           int64_t expected = spStack_.popCopy();
           VIXL_ASSERT(current == expected);
           return;
         }
         default:
           VIXL_UNIMPLEMENTED();
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -407,18 +407,19 @@ CodeGeneratorShared::encodeAllocation(LS
         }
 
         // This MDefinition is recovered, thus it should be listed in the
         // LRecoverInfo.
         MOZ_ASSERT(it != end && mir == *it);
 
         // Lambda should have a default value readable for iterating over the
         // inner frames.
-        if (mir->isLambda()) {
-            MConstant* constant = mir->toLambda()->functionOperand();
+        if (mir->isLambda() || mir->isLambdaArrow()) {
+            MConstant* constant = mir->isLambda() ? mir->toLambda()->functionOperand()
+                                                  : mir->toLambdaArrow()->functionOperand();
             uint32_t cstIndex;
             masm.propagateOOM(graph.addConstantToPool(constant->toJSValue(), &cstIndex));
             alloc = RValueAllocation::RecoverInstruction(index, cstIndex);
             break;
         }
 
         alloc = RValueAllocation::RecoverInstruction(index);
         break;
--- a/js/src/jsapi-tests/testGCFinalizeCallback.cpp
+++ b/js/src/jsapi-tests/testGCFinalizeCallback.cpp
@@ -155,39 +155,40 @@ virtual void uninit() override
 {
     JS_RemoveFinalizeCallback(cx, FinalizeCallback);
     JSAPITest::uninit();
 }
 
 bool checkSingleGroup()
 {
     CHECK(FinalizeCalls < BufferSize);
-    CHECK(FinalizeCalls == 3);
+    CHECK(FinalizeCalls == 4);
     return true;
 }
 
 bool checkMultipleGroups()
 {
     CHECK(FinalizeCalls < BufferSize);
-    CHECK(FinalizeCalls % 2 == 1);
-    CHECK((FinalizeCalls - 1) / 2 > 1);
+    CHECK(FinalizeCalls % 3 == 1);
+    CHECK((FinalizeCalls - 1) / 3 > 1);
     return true;
 }
 
 bool checkFinalizeStatus()
 {
     /*
      * The finalize callback should be called twice for each sweep group
      * finalized, with status JSFINALIZE_GROUP_START and JSFINALIZE_GROUP_END,
      * and then once more with JSFINALIZE_COLLECTION_END.
      */
 
-    for (unsigned i = 0; i < FinalizeCalls - 1; i += 2) {
-        CHECK(StatusBuffer[i] == JSFINALIZE_GROUP_START);
-        CHECK(StatusBuffer[i + 1] == JSFINALIZE_GROUP_END);
+    for (unsigned i = 0; i < FinalizeCalls - 1; i += 3) {
+        CHECK(StatusBuffer[i] == JSFINALIZE_GROUP_PREPARE);
+        CHECK(StatusBuffer[i + 1] == JSFINALIZE_GROUP_START);
+        CHECK(StatusBuffer[i + 2] == JSFINALIZE_GROUP_END);
     }
 
     CHECK(StatusBuffer[FinalizeCalls - 1] == JSFINALIZE_COLLECTION_END);
 
     return true;
 }
 
 bool checkFinalizeIsZoneGC(bool isZoneGC)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -558,25 +558,30 @@ typedef void
 
 typedef void
 (* JSObjectsTenuredCallback)(JSContext* cx, void* data);
 
 typedef enum JSFinalizeStatus {
     /**
      * Called when preparing to sweep a group of zones, before anything has been
      * swept.  The collector will not yield to the mutator before calling the
-     * callback with JSFINALIZE_GROUP_END status.
+     * callback with JSFINALIZE_GROUP_START status.
+     */
+    JSFINALIZE_GROUP_PREPARE,
+
+    /**
+     * Called after preparing to sweep a group of zones. Weak references to
+     * unmarked things have been removed at this point, but no GC things have
+     * been swept. The collector may yield to the mutator after this point.
      */
     JSFINALIZE_GROUP_START,
 
     /**
-     * Called when preparing to sweep a group of zones. Weak references to
-     * unmarked things have been removed and things that are not swept
-     * incrementally have been finalized at this point.  The collector may yield
-     * to the mutator after this point.
+     * Called after sweeping a group of zones. All dead GC things have been
+     * swept at this point.
      */
     JSFINALIZE_GROUP_END,
 
     /**
      * Called at the end of collection when everything has been swept.
      */
     JSFINALIZE_COLLECTION_END
 } JSFinalizeStatus;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -618,16 +618,23 @@ js::ZoneGlobalsAreAllGray(JS::Zone* zone
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
         JSObject* obj = comp->unsafeUnbarrieredMaybeGlobal();
         if (!obj || !JS::ObjectIsMarkedGray(obj))
             return false;
     }
     return true;
 }
 
+JS_FRIEND_API(bool)
+js::IsObjectZoneSweepingOrCompacting(JSObject* obj)
+{
+    MOZ_ASSERT(obj);
+    return MaybeForwarded(obj)->zone()->isGCSweepingOrCompacting();
+}
+
 namespace {
 struct VisitGrayCallbackFunctor {
     GCThingCallback callback_;
     void* closure_;
     VisitGrayCallbackFunctor(GCThingCallback callback, void* closure)
       : callback_(callback), closure_(closure)
     {}
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -468,16 +468,19 @@ extern JS_FRIEND_API(void)
 TraceWeakMaps(WeakMapTracer* trc);
 
 extern JS_FRIEND_API(bool)
 AreGCGrayBitsValid(JSContext* cx);
 
 extern JS_FRIEND_API(bool)
 ZoneGlobalsAreAllGray(JS::Zone* zone);
 
+extern JS_FRIEND_API(bool)
+IsObjectZoneSweepingOrCompacting(JSObject* obj);
+
 typedef void
 (*GCThingCallback)(void* closure, JS::GCCellPtr thing);
 
 extern JS_FRIEND_API(void)
 VisitGrayWrapperTargets(JS::Zone* zone, GCThingCallback callback, void* closure);
 
 extern JS_FRIEND_API(JSObject*)
 GetWeakmapKeyDelegate(JSObject* key);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -320,16 +320,30 @@ FOR_EACH_ALLOCKIND(EXPAND_THINGS_PER_ARE
 
 struct js::gc::FinalizePhase
 {
     gcstats::Phase statsPhase;
     AllocKinds kinds;
 };
 
 /*
+ * Finalization order for objects swept incrementally on the active thread.
+ */
+static const FinalizePhase ForegroundObjectFinalizePhase = {
+    gcstats::PHASE_SWEEP_OBJECT, {
+        AllocKind::OBJECT0,
+        AllocKind::OBJECT2,
+        AllocKind::OBJECT4,
+        AllocKind::OBJECT8,
+        AllocKind::OBJECT12,
+        AllocKind::OBJECT16
+    }
+};
+
+/*
  * Finalization order for GC things swept incrementally on the active thread.
  */
 static const FinalizePhase IncrementalFinalizePhases[] = {
     {
         gcstats::PHASE_SWEEP_STRING, {
             AllocKind::EXTERNAL_STRING
         }
     },
@@ -948,17 +962,27 @@ const char* gc::ZealModeHelpText =
     "    8: (IncrementalRootsThenFinish) Incremental GC in two slices: 1) mark roots 2) finish collection\n"
     "    9: (IncrementalMarkAllThenFinish) Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
     "   10: (IncrementalMultipleSlices) Incremental GC in multiple slices\n"
     "   11: (IncrementalMarkingValidator) Verify incremental marking\n"
     "   12: (ElementsBarrier) Always use the individual element post-write barrier, regardless of elements size\n"
     "   13: (CheckHashTablesOnMinorGC) Check internal hashtables on minor GC\n"
     "   14: (Compact) Perform a shrinking collection every N allocations\n"
     "   15: (CheckHeapAfterGC) Walk the heap to check its integrity after every GC\n"
-    "   16: (CheckNursery) Check nursery integrity on minor GC\n";
+    "   16: (CheckNursery) Check nursery integrity on minor GC\n"
+    "   17: (IncrementalSweepThenFinish) Incremental GC in two slices: 1) start sweeping 2) finish collection\n";
+
+// The set of zeal modes that control incremental slices. These modes are
+// mutually exclusive.
+static const mozilla::EnumSet<ZealMode> IncrementalSliceZealModes = {
+    ZealMode::IncrementalRootsThenFinish,
+    ZealMode::IncrementalMarkAllThenFinish,
+    ZealMode::IncrementalMultipleSlices,
+    ZealMode::IncrementalSweepThenFinish
+};
 
 void
 GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
 {
     MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
 
     if (verifyPreData)
         VerifyBarriers(rt, PreBarrierVerifier);
@@ -969,24 +993,21 @@ GCRuntime::setZeal(uint8_t zeal, uint32_
     }
 
     ZealMode zealMode = ZealMode(zeal);
     if (zealMode == ZealMode::GenerationalGC) {
         for (ZoneGroupsIter group(rt); !group.done(); group.next())
             group->nursery().enterZealMode();
     }
 
-    // Zeal modes 8-10 are mutually exclusive. If we're setting one of those,
-    // we first reset all of them.
-    if (zealMode >= ZealMode::IncrementalRootsThenFinish &&
-        zealMode <= ZealMode::IncrementalMultipleSlices)
-    {
-        clearZealMode(ZealMode::IncrementalRootsThenFinish);
-        clearZealMode(ZealMode::IncrementalMarkAllThenFinish);
-        clearZealMode(ZealMode::IncrementalMultipleSlices);
+    // Some modes are mutually exclusive. If we're setting one of those, we
+    // first reset all of them.
+    if (IncrementalSliceZealModes.contains(zealMode)) {
+        for (auto mode : IncrementalSliceZealModes)
+            clearZealMode(mode);
     }
 
     bool schedule = zealMode >= ZealMode::Alloc;
     if (zeal != 0)
         zealModeBits |= 1 << unsigned(zeal);
     else
         zealModeBits = 0;
     zealFrequency = frequency;
@@ -2741,39 +2762,16 @@ ArenaLists::~ArenaLists()
     ReleaseArenaList(runtime_, incrementalSweptArenas.ref().head(), lock);
 
     for (auto i : ObjectAllocKinds())
         ReleaseArenaList(runtime_, savedObjectArenas(i).head(), lock);
     ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock);
 }
 
 void
-ArenaLists::finalizeNow(FreeOp* fop, AllocKind thingKind, Arena** empty)
-{
-    MOZ_ASSERT(!IsBackgroundFinalized(thingKind));
-    MOZ_ASSERT(backgroundFinalizeState(thingKind) == BFS_DONE);
-    MOZ_ASSERT(empty);
-
-    Arena* arenas = arenaLists(thingKind).head();
-    if (!arenas)
-        return;
-    arenaLists(thingKind).clear();
-
-    size_t thingsPerArena = Arena::thingsPerArena(thingKind);
-    SortedArenaList finalizedSorted(thingsPerArena);
-
-    auto unlimited = SliceBudget::unlimited();
-    FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, unlimited, KEEP_ARENAS);
-    MOZ_ASSERT(!arenas);
-
-    finalizedSorted.extractEmpty(empty);
-    arenaLists(thingKind) = finalizedSorted.toArenaList();
-}
-
-void
 ArenaLists::queueForForegroundSweep(FreeOp* fop, const FinalizePhase& phase)
 {
     gcstats::AutoPhase ap(fop->runtime()->gc.stats(), phase.statsPhase);
     for (auto kind : phase.kinds)
         queueForForegroundSweep(fop, kind);
 }
 
 void
@@ -2855,49 +2853,16 @@ ArenaLists::backgroundFinalize(FreeOp* f
 
         lists->arenaListsToSweep(thingKind) = nullptr;
     }
 
     lists->backgroundFinalizeState(thingKind) = BFS_DONE;
 }
 
 void
-ArenaLists::queueForegroundObjectsForSweep(FreeOp* fop)
-{
-    gcstats::AutoPhase ap(fop->runtime()->gc.stats(), gcstats::PHASE_SWEEP_OBJECT);
-
-#ifdef DEBUG
-    for (auto i : ObjectAllocKinds())
-        MOZ_ASSERT(savedObjectArenas(i).isEmpty());
-    MOZ_ASSERT(savedEmptyObjectArenas == nullptr);
-#endif
-
-    // Foreground finalized objects must be finalized at the beginning of the
-    // sweep phase, before control can return to the mutator. Otherwise,
-    // mutator behavior can resurrect certain objects whose references would
-    // otherwise have been erased by the finalizer.
-    finalizeNow(fop, AllocKind::OBJECT0, &savedEmptyObjectArenas.ref());
-    finalizeNow(fop, AllocKind::OBJECT2, &savedEmptyObjectArenas.ref());
-    finalizeNow(fop, AllocKind::OBJECT4, &savedEmptyObjectArenas.ref());
-    finalizeNow(fop, AllocKind::OBJECT8, &savedEmptyObjectArenas.ref());
-    finalizeNow(fop, AllocKind::OBJECT12, &savedEmptyObjectArenas.ref());
-    finalizeNow(fop, AllocKind::OBJECT16, &savedEmptyObjectArenas.ref());
-
-    // Prevent the arenas from having new objects allocated into them. We need
-    // to know which objects are marked while we incrementally sweep dead
-    // references from type information.
-    savedObjectArenas(AllocKind::OBJECT0) = arenaLists(AllocKind::OBJECT0).copyAndClear();
-    savedObjectArenas(AllocKind::OBJECT2) = arenaLists(AllocKind::OBJECT2).copyAndClear();
-    savedObjectArenas(AllocKind::OBJECT4) = arenaLists(AllocKind::OBJECT4).copyAndClear();
-    savedObjectArenas(AllocKind::OBJECT8) = arenaLists(AllocKind::OBJECT8).copyAndClear();
-    savedObjectArenas(AllocKind::OBJECT12) = arenaLists(AllocKind::OBJECT12).copyAndClear();
-    savedObjectArenas(AllocKind::OBJECT16) = arenaLists(AllocKind::OBJECT16).copyAndClear();
-}
-
-void
 ArenaLists::mergeForegroundSweptObjectArenas()
 {
     AutoLockGC lock(runtime_);
     ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock);
     savedEmptyObjectArenas = nullptr;
 
     mergeSweptArenas(AllocKind::OBJECT0);
     mergeSweptArenas(AllocKind::OBJECT2);
@@ -4590,28 +4555,37 @@ GCRuntime::findInterZoneEdges()
                 zone->setHasDeadProxies(false);
         }
     }
 
     return true;
 }
 
 void
-GCRuntime::groupZonesForSweeping(AutoLockForExclusiveAccess& lock)
+GCRuntime::groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
 {
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         MOZ_ASSERT(zone->gcSweepGroupEdges().empty());
 #endif
 
     JSContext* cx = TlsContext.get();
     ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode], lock);
     if (!isIncremental || !findInterZoneEdges())
         finder.useOneComponent();
 
+#ifdef JS_GC_ZEAL
+    // Use one component for IncrementalSweepThenFinish zeal mode.
+    if (isIncremental && reason == JS::gcreason::DEBUG_GC &&
+        hasZealMode(ZealMode::IncrementalSweepThenFinish))
+    {
+        finder.useOneComponent();
+    }
+#endif
+
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarking());
         finder.addNode(zone);
     }
     sweepGroups = finder.getResultsList();
     currentSweepGroup = sweepGroups;
     sweepGroupIndex = 0;
 
@@ -5167,28 +5141,29 @@ GCRuntime::beginSweepingSweepGroup(AutoL
         /* No need to look up any more weakmap keys from this sweep group. */
         AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!zone->gcWeakKeys().clear())
             oomUnsafe.crash("clearing weak keys in beginSweepingSweepGroup()");
     }
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_START);
-        callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
+        callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_PREPARE);
         {
             gcstats::AutoPhase ap2(stats(), gcstats::PHASE_WEAK_ZONES_CALLBACK);
             callWeakPointerZonesCallbacks();
         }
         {
             gcstats::AutoPhase ap2(stats(), gcstats::PHASE_WEAK_COMPARTMENT_CALLBACK);
             for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
                 for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
                     callWeakPointerCompartmentCallbacks(comp);
             }
         }
+        callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
     }
 
     if (sweepingAtoms) {
         AutoLockHelperThreadState helperLock;
         startTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS, helperLock);
     }
 
     {
@@ -5291,52 +5266,48 @@ GCRuntime::beginSweepingSweepGroup(AutoL
             joinTask(task, gcstats::PHASE_SWEEP_MISC, helperLock);
     }
 
     /*
      * Queue all GC things in all zones for sweeping, either in the
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
-     *
-     * Objects are finalized immediately but this may change in the future.
      */
 
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         gcstats::AutoSCC scc(stats(), sweepGroupIndex);
-        zone->arenas.queueForegroundObjectsForSweep(&fop);
-    }
-    for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
-        gcstats::AutoSCC scc(stats(), sweepGroupIndex);
+        zone->arenas.queueForForegroundSweep(&fop, ForegroundObjectFinalizePhase);
         for (unsigned i = 0; i < ArrayLength(IncrementalFinalizePhases); ++i)
             zone->arenas.queueForForegroundSweep(&fop, IncrementalFinalizePhases[i]);
     }
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         gcstats::AutoSCC scc(stats(), sweepGroupIndex);
         for (unsigned i = 0; i < ArrayLength(BackgroundFinalizePhases); ++i)
             zone->arenas.queueForBackgroundSweep(&fop, BackgroundFinalizePhases[i]);
     }
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         gcstats::AutoSCC scc(stats(), sweepGroupIndex);
         zone->arenas.queueForegroundThingsForSweep(&fop);
     }
 
     sweepPhaseIndex = 0;
     sweepZone = currentSweepGroup;
     sweepActionIndex = 0;
-
-    {
-        gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_END);
-        callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END);
-    }
 }
 
 void
 GCRuntime::endSweepingSweepGroup()
 {
+    {
+        gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_END);
+        FreeOp fop(rt);
+        callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END);
+    }
+
     /* Update the GC state for zones we have swept. */
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCSweeping());
         AutoLockGC lock(rt);
         zone->setGCState(Zone::Finished);
         zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind, tunables,
                                       schedulingState, lock);
     }
@@ -5353,17 +5324,17 @@ GCRuntime::endSweepingSweepGroup()
     /* Reset the list of arenas marked as being allocated during sweep phase. */
     while (Arena* arena = arenasAllocatedDuringSweep) {
         arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
     }
 }
 
 void
-GCRuntime::beginSweepPhase(bool destroyingRuntime, AutoLockForExclusiveAccess& lock)
+GCRuntime::beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
 {
     /*
      * Sweep phase.
      *
      * Finalize as we sweep, outside of lock but with CurrentThreadIsHeapBusy()
      * true so that any attempt to allocate a GC-thing from a finalizer will
      * fail, rather than nest badly and leave the unmarked newborn to be swept.
      */
@@ -5374,50 +5345,59 @@ GCRuntime::beginSweepPhase(bool destroyi
 
     releaseHeldRelocatedArenas();
 
     computeNonIncrementalMarkingForValidation(lock);
 
     gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP);
 
     sweepOnBackgroundThread =
-        !destroyingRuntime && !TraceEnabled() && CanUseExtraThreads();
+        reason != JS::gcreason::DESTROY_RUNTIME && !TraceEnabled() && CanUseExtraThreads();
 
     releaseObservedTypes = shouldReleaseObservedTypes();
 
     AssertNoWrappersInGrayList(rt);
     DropStringWrappers(rt);
 
-    groupZonesForSweeping(lock);
+    groupZonesForSweeping(reason, lock);
     endMarkingSweepGroup();
     beginSweepingSweepGroup(lock);
 }
 
 bool
 ArenaLists::foregroundFinalize(FreeOp* fop, AllocKind thingKind, SliceBudget& sliceBudget,
                                SortedArenaList& sweepList)
 {
+    MOZ_ASSERT_IF(IsObjectAllocKind(thingKind), savedObjectArenas(thingKind).isEmpty());
+
     if (!arenaListsToSweep(thingKind) && incrementalSweptArenas.ref().isEmpty())
         return true;
 
+    KeepArenasEnum keepArenas = IsObjectAllocKind(thingKind) ? KEEP_ARENAS : RELEASE_ARENAS;
     if (!FinalizeArenas(fop, &arenaListsToSweep(thingKind), sweepList,
-                        thingKind, sliceBudget, RELEASE_ARENAS))
+                        thingKind, sliceBudget, keepArenas))
     {
         incrementalSweptArenaKind = thingKind;
         incrementalSweptArenas = sweepList.toArenaList();
         return false;
     }
 
     // Clear any previous incremental sweep state we may have saved.
     incrementalSweptArenas.ref().clear();
 
-    // Join |arenaLists[thingKind]| and |sweepList| into a single list.
-    ArenaList finalized = sweepList.toArenaList();
-    arenaLists(thingKind) =
-        finalized.insertListWithCursorAtEnd(arenaLists(thingKind));
+    if (IsObjectAllocKind(thingKind)) {
+        // Delay releasing of object arenas until types have been swept.
+        sweepList.extractEmpty(&savedEmptyObjectArenas.ref());
+        savedObjectArenas(thingKind) = sweepList.toArenaList();
+    } else {
+        // Join |arenaLists[thingKind]| and |sweepList| into a single list.
+        ArenaList finalized = sweepList.toArenaList();
+        arenaLists(thingKind) =
+            finalized.insertListWithCursorAtEnd(arenaLists(thingKind));
+    }
 
     return true;
 }
 
 IncrementalProgress
 GCRuntime::drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase)
 {
     /* Run a marking slice and return whether the stack is now empty. */
@@ -5565,16 +5545,20 @@ AddSweepAction(bool* ok, SweepAction::Fu
 }
 
 /* static */ bool
 GCRuntime::initializeSweepActions()
 {
     bool ok = true;
 
     AddSweepPhase(&ok);
+    for (auto kind : ForegroundObjectFinalizePhase.kinds)
+        AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind);
+
+    AddSweepPhase(&ok);
     AddSweepAction(&ok, GCRuntime::sweepTypeInformation);
     AddSweepAction(&ok, GCRuntime::mergeSweptObjectArenas);
 
     for (const auto& finalizePhase : IncrementalFinalizePhases) {
         AddSweepPhase(&ok);
         for (auto kind : finalizePhase.kinds)
             AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind);
     }
@@ -6109,17 +6093,18 @@ GCRuntime::incrementalCollectSlice(Slice
         useZeal = true;
     }
 #endif
 
     MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
     isIncremental = !budget.isUnlimited();
 
     if (useZeal && (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
-                    hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
+                    hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
+                    hasZealMode(ZealMode::IncrementalSweepThenFinish)))
     {
         /*
          * Yields between slices occurs at predetermined points in these modes;
          * the budget is not used.
          */
         budget.makeUnlimited();
     }
 
@@ -6187,26 +6172,30 @@ GCRuntime::incrementalCollectSlice(Slice
         }
 
         incrementalState = State::Sweep;
 
         /*
          * This runs to completion, but we don't continue if the budget is
          * now exhasted.
          */
-        beginSweepPhase(destroyingRuntime, lock);
+        beginSweepPhase(reason, lock);
         if (budget.isOverBudget())
             break;
 
         /*
          * Always yield here when running in incremental multi-slice zeal
          * mode, so RunDebugGC can reset the slice buget.
          */
-        if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalMultipleSlices))
+        if (isIncremental && useZeal &&
+            (hasZealMode(ZealMode::IncrementalMultipleSlices) ||
+             hasZealMode(ZealMode::IncrementalSweepThenFinish)))
+        {
             break;
+        }
 
         MOZ_FALLTHROUGH;
 
       case State::Sweep:
         if (performSweepActions(budget, lock) == NotFinished)
             break;
 
         endSweepPhase(destroyingRuntime, lock);
@@ -6215,17 +6204,17 @@ GCRuntime::incrementalCollectSlice(Slice
 
         MOZ_FALLTHROUGH;
 
       case State::Finalize:
         {
             gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD);
 
             // Yield until background finalization is done.
-            if (isIncremental) {
+            if (!budget.isUnlimited()) {
                 // Poll for end of background sweeping
                 AutoLockGC lock(rt);
                 if (isBackgroundSweeping())
                     break;
             } else {
                 waitBackgroundSweepEnd();
             }
         }
@@ -6239,17 +6228,17 @@ GCRuntime::incrementalCollectSlice(Slice
             FreeOp fop(rt);
             sweepZoneGroups(&fop, destroyingRuntime);
         }
 
         MOZ_ASSERT(!startedCompacting);
         incrementalState = State::Compact;
 
         // Always yield before compacting since it is not incremental.
-        if (isCompacting && isIncremental)
+        if (isCompacting && !budget.isUnlimited())
             break;
 
         MOZ_FALLTHROUGH;
 
       case State::Compact:
         if (isCompacting) {
             if (!startedCompacting)
                 beginCompactPhase();
@@ -6265,17 +6254,17 @@ GCRuntime::incrementalCollectSlice(Slice
 
         MOZ_FALLTHROUGH;
 
       case State::Decommit:
         {
             gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD);
 
             // Yield until background decommit is done.
-            if (isIncremental && decommitTask.isRunning())
+            if (!budget.isUnlimited() && decommitTask.isRunning())
                 break;
 
             decommitTask.join();
         }
 
         finishCollection(reason);
         incrementalState = State::NotActive;
         break;
@@ -7149,17 +7138,18 @@ GCRuntime::runDebugGC()
     if (hasZealMode(ZealMode::GenerationalGC))
         return minorGC(JS::gcreason::DEBUG_GC);
 
     PrepareForDebugGC(rt);
 
     auto budget = SliceBudget::unlimited();
     if (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
         hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
-        hasZealMode(ZealMode::IncrementalMultipleSlices))
+        hasZealMode(ZealMode::IncrementalMultipleSlices) ||
+        hasZealMode(ZealMode::IncrementalSweepThenFinish))
     {
         js::gc::State initialState = incrementalState;
         if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
             /*
              * Start with a small slice limit and double it every slice. This
              * ensure that we get multiple slices, and collection runs to
              * completion.
              */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -802,18 +802,16 @@ class ArenaLists
     enum KeepArenasEnum {
         RELEASE_ARENAS,
         KEEP_ARENAS
     };
 
   private:
     inline void queueForForegroundSweep(FreeOp* fop, const FinalizePhase& phase);
     inline void queueForBackgroundSweep(FreeOp* fop, const FinalizePhase& phase);
-
-    inline void finalizeNow(FreeOp* fop, AllocKind thingKind, Arena** empty = nullptr);
     inline void queueForForegroundSweep(FreeOp* fop, AllocKind thingKind);
     inline void queueForBackgroundSweep(FreeOp* fop, AllocKind thingKind);
     inline void mergeSweptArenas(AllocKind thingKind);
 
     TenuredCell* allocateFromArena(JS::Zone* zone, AllocKind thingKind,
                                    ShouldCheckThresholds checkThresholds,
                                    AutoMaybeStartBackgroundAllocation& maybeStartBGAlloc);
     inline TenuredCell* allocateFromArenaInner(JS::Zone* zone, Arena* arena, AllocKind kind);
@@ -1178,23 +1176,24 @@ inline void CheckValueAfterMovingGC(cons
             D(IncrementalRootsThenFinish, 8)   \
             D(IncrementalMarkAllThenFinish, 9) \
             D(IncrementalMultipleSlices, 10)   \
             D(IncrementalMarkingValidator, 11) \
             D(ElementsBarrier, 12)             \
             D(CheckHashTablesOnMinorGC, 13)    \
             D(Compact, 14)                     \
             D(CheckHeapAfterGC, 15)            \
-            D(CheckNursery, 16)
+            D(CheckNursery, 16)                \
+            D(IncrementalSweepThenFinish, 17)
 
 enum class ZealMode {
 #define ZEAL_MODE(name, value) name = value,
     JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
 #undef ZEAL_MODE
-    Limit = 16
+    Limit = 17
 };
 
 enum VerifierType {
     PreBarrierVerifier
 };
 
 #ifdef JS_GC_ZEAL
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -607,16 +607,18 @@ if CONFIG['JS_HAS_CTYPES']:
         ]
 
 if CONFIG['MOZ_VTUNE']:
     SOURCES += [
         'vtune/ittnotify_static.c',
         'vtune/jitprofiling.c',
         'vtune/VTuneWrapper.cpp',
     ]
+    if CONFIG['CC_TYPE'] != 'msvc':
+        SOURCES['vtune/ittnotify_static.c'].flags += ['-Wno-varargs']
 
 if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
     SOURCES += [
         'perf/pm_linux.cpp'
     ]
     if CONFIG['LINUX_HEADERS_INCLUDES']:
         SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
 else:
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -3215,26 +3215,30 @@ Debugger::trace(JSTracer* trc)
 }
 
 /* static */ void
 Debugger::sweepAll(FreeOp* fop)
 {
     JSRuntime* rt = fop->runtime();
 
     for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
-        for (Debugger* dbg : group->debuggerList()) {
+        Debugger* dbg = group->debuggerList().getFirst();
+        while (dbg) {
+            Debugger* next = dbg->getNext();
             if (IsAboutToBeFinalized(&dbg->object)) {
                 /*
                  * dbg is being GC'd. Detach it from its debuggees. The debuggee
                  * might be GC'd too. Since detaching requires access to both
                  * objects, this must be done before finalize time.
                  */
                 for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
                     dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
+                fop->delete_(dbg);
             }
+            dbg = next;
         }
     }
 }
 
 /* static */ void
 Debugger::detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global)
 {
     const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
@@ -3266,47 +3270,35 @@ Debugger::findZoneEdges(Zone* zone, js::
                 dbg->wasmInstanceSources.hasKeyInZone(zone))
             {
                 finder.addEdgeTo(w);
             }
         }
     }
 }
 
-/* static */ void
-Debugger::finalize(FreeOp* fop, JSObject* obj)
-{
-    MOZ_ASSERT(fop->onActiveCooperatingThread());
-
-    Debugger* dbg = fromJSObject(obj);
-    if (!dbg)
-        return;
-    fop->delete_(dbg);
-}
-
 const ClassOps Debugger::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
-    Debugger::finalize,
+    nullptr,    /* finalize    */
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     Debugger::traceObject
 };
 
 const Class Debugger::class_ = {
     "Debugger",
     JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT) |
-    JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
     &Debugger::classOps_
 };
 
 static Debugger*
 Debugger_fromThisValue(JSContext* cx, const CallArgs& args, const char* fnname)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
@@ -3899,17 +3891,18 @@ Debugger::construct(JSContext* cx, unsig
         return false;
     RootedNativeObject proto(cx, &v.toObject().as<NativeObject>());
     MOZ_ASSERT(proto->getClass() == &Debugger::class_);
     /*
      * Make the new Debugger object. Each one has a reference to
      * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
      * rest of the reserved slots are for hooks; they default to undefined.
      */
-    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto));
+    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto,
+                                                             TenuredObject));
     if (!obj)
         return false;
     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
     obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
 
     // Debuggers currently require single threaded execution. A debugger may be
     // used to debug content in other zone groups, and may be used to observe
@@ -11791,18 +11784,17 @@ GarbageCollectionEvent::toJSObject(JSCon
     {
         return nullptr;
     }
 
     RootedArrayObject slicesArray(cx, NewDenseEmptyArray(cx));
     if (!slicesArray)
         return nullptr;
 
-    bool ignored; // Ignore inconsistencies in process creation timestamp.
-    TimeStamp originTime = TimeStamp::ProcessCreation(ignored);
+    TimeStamp originTime = TimeStamp::ProcessCreation();
 
     size_t idx = 0;
     for (auto range = collections.all(); !range.empty(); range.popFront()) {
         RootedPlainObject collectionObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!collectionObj)
             return nullptr;
 
         RootedValue start(cx), end(cx);
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -584,17 +584,16 @@ class Debugger : private mozilla::Linked
                                 const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
                                 JSTrapStatus& statusp, MutableHandleValue vp);
 
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
     void trace(JSTracer* trc);
     void traceForMovingGC(JSTracer* trc);
-    static void finalize(FreeOp* fop, JSObject* obj);
     void traceCrossCompartmentEdges(JSTracer* tracer);
 
     static const ClassOps classOps_;
 
   public:
     static const Class class_;
 
   private:
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -201,18 +201,18 @@ DebuggerMemory::drainAllocationsLog(JSCo
         // we must edit them with great care. Use the queue entry in place, and
         // then pop and delete together.
         Debugger::AllocationsLogEntry& entry = dbg->allocationsLog.front();
 
         RootedValue frame(cx, ObjectOrNullValue(entry.frame));
         if (!DefineProperty(cx, obj, cx->names().frame, frame))
             return false;
 
-        bool ignore;
-        double when = (entry.when - mozilla::TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+        double when = (entry.when -
+                       mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
         RootedValue timestampValue(cx, NumberValue(when));
         if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
             return false;
 
         RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className)));
         if (!className)
             return false;
         RootedValue classNameValue(cx, StringValue(className));
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -566,22 +566,16 @@ js::EnableContextProfilingStack(JSContex
 
 JS_FRIEND_API(void)
 js::RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*))
 {
     MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
     cx->runtime()->geckoProfiler().setEventMarker(fn);
 }
 
-JS_FRIEND_API(jsbytecode*)
-js::ProfilingGetPC(JSContext* cx, JSScript* script, void* ip)
-{
-    return cx->runtime()->geckoProfiler().ipToPC(script, size_t(ip));
-}
-
 AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx
                                                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : cx_(cx),
     previouslyEnabled_(cx->isProfilerSamplingEnabled()),
     prohibitContextChange_(cx->runtime())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (previouslyEnabled_)
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -183,18 +183,16 @@ class GeckoProfiler
             stack_[*size_ - 1].setPC(pc);
         }
     }
 
     /* Enter wasm code */
     void beginPseudoJS(const char* string, void* sp);
     void endPseudoJS() { pop(); }
 
-    jsbytecode* ipToPC(JSScript* script, size_t ip) { return nullptr; }
-
     void setProfilingStack(ProfileEntry* stack, mozilla::Atomic<uint32_t>* size, uint32_t max);
     void setEventMarker(void (*fn)(const char*));
     const char* profileString(JSScript* script, JSFunction* maybeFun);
     void onScriptFinalized(JSScript* script);
 
     void markEvent(const char* event);
 
     /* meant to be used for testing, not recommended to call in normal code */
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -83,18 +83,17 @@ JS::detail::InitWithFailureDiagnostic(bo
                "how do we have live runtimes before JS_Init?");
 
     PRMJ_NowInit();
 
     // The first invocation of `ProcessCreation` creates a temporary thread
     // and crashes if that fails, i.e. because we're out of memory. To prevent
     // that from happening at some later time, get it out of the way during
     // startup.
-    bool ignored;
-    mozilla::TimeStamp::ProcessCreation(ignored);
+    mozilla::TimeStamp::ProcessCreation();
 
 #ifdef DEBUG
     CheckMessageParameterCounts();
 #endif
 
     RETURN_IF_FAIL(js::TlsContext.init());
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1220,16 +1220,18 @@ class NativeObject : public ShapedObject
     }
     void setPrivate(void* data) {
         void** pprivate = &privateRef(numFixedSlots());
         privateWriteBarrierPre(pprivate);
         *pprivate = data;
     }
 
     void setPrivateGCThing(gc::Cell* cell) {
+        MOZ_ASSERT_IF(IsMarkedBlack(this),
+                      !JS::GCThingIsMarkedGray(JS::GCCellPtr(cell, cell->getTraceKind())));
         void** pprivate = &privateRef(numFixedSlots());
         privateWriteBarrierPre(pprivate);
         *pprivate = reinterpret_cast<void*>(cell);
         privateWriteBarrierPost(pprivate);
     }
 
     void setPrivateUnbarriered(void* data) {
         void** pprivate = &privateRef(numFixedSlots());
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1222,16 +1222,21 @@ bool
 JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
 {
     if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_NOT_CLONABLE,
                                   "SharedArrayBuffer");
         return false;
     }
 
+    // We must not transfer buffer pointers cross-process.  The cloneDataPolicy
+    // should guard against this; check that it does.
+
+    MOZ_RELEASE_ASSERT(scope <= JS::StructuredCloneScope::SameProcessDifferentThread);
+
     Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
     SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
 
     if (!refsHeld.acquire(context(), rawbuf))
         return false;
 
     intptr_t p = reinterpret_cast<intptr_t>(rawbuf);
     return out.writePair(SCTAG_SHARED_ARRAY_BUFFER_OBJECT, static_cast<uint32_t>(sizeof(p))) &&
@@ -1958,16 +1963,21 @@ JSStructuredCloneReader::readSharedArray
     // transmission point, but that's tricky, and it will be a very rare problem
     // in any case.  Just fail at the receiving end if we can't handle it.
 
     if (!context()->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_DISABLED);
         return false;
     }
 
+    // We must not transfer buffer pointers cross-process.  The cloneDataPolicy
+    // in the sender should guard against this; check that it does.
+
+    MOZ_RELEASE_ASSERT(storedScope <= JS::StructuredCloneScope::SameProcessDifferentThread);
+
     // The new object will have a new reference to the rawbuf.
 
     if (!rawbuf->addReference()) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
         return false;
     }
 
     JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf);
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -899,51 +899,56 @@ wasm::SymbolicAddressTarget(SymbolicAddr
     if (!NeedsBuiltinThunk(sym))
         return funcPtr;
 
     const BuiltinThunks& thunks = *builtinThunks;
     uint32_t codeRangeIndex = thunks.symbolicAddressToCodeRange[sym];
     return thunks.codeBase + thunks.codeRanges[codeRangeIndex].begin();
 }
 
-static ABIFunctionType
-ToABIFunctionType(const Sig& sig)
+static Maybe<ABIFunctionType>
+ToBuiltinABIFunctionType(const Sig& sig)
 {
     const ValTypeVector& args = sig.args();
     ExprType ret = sig.ret();
 
     uint32_t abiType;
     switch (ret) {
       case ExprType::F32: abiType = ArgType_Float32 << RetType_Shift; break;
       case ExprType::F64: abiType = ArgType_Double << RetType_Shift; break;
-      default:            MOZ_CRASH("unhandled ret type");
+      default: return Nothing();
     }
 
+    if ((args.length() + 1) > (sizeof(uint32_t) * 8 / ArgType_Shift))
+        return Nothing();
+
     for (size_t i = 0; i < args.length(); i++) {
         switch (args[i]) {
           case ValType::F32: abiType |= (ArgType_Float32 << (ArgType_Shift * (i + 1))); break;
           case ValType::F64: abiType |= (ArgType_Double << (ArgType_Shift * (i + 1))); break;
-          default:           MOZ_CRASH("unhandled arg type");
+          default: return Nothing();
         }
     }
 
-    return ABIFunctionType(abiType);
+    return Some(ABIFunctionType(abiType));
 }
 
 void*
 wasm::MaybeGetBuiltinThunk(HandleFunction f, const Sig& sig, JSContext* cx)
 {
     MOZ_ASSERT(builtinThunks);
 
     if (!f->isNative() || !f->jitInfo() || f->jitInfo()->type() != JSJitInfo::InlinableNative)
         return nullptr;
 
-    InlinableNative native = f->jitInfo()->inlinableNative;
-    ABIFunctionType abiType = ToABIFunctionType(sig);
-    TypedNative typedNative(native, abiType);
+    Maybe<ABIFunctionType> abiType = ToBuiltinABIFunctionType(sig);
+    if (!abiType)
+        return nullptr;
+
+    TypedNative typedNative(f->jitInfo()->inlinableNative, *abiType);
 
     const BuiltinThunks& thunks = *builtinThunks;
     auto p = thunks.typedNativeToCodeRange.readonlyThreadsafeLookup(typedNative);
     if (!p)
         return nullptr;
 
     return thunks.codeBase + thunks.codeRanges[p->value()].begin();
 }
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -135,18 +135,19 @@ ModuleGenerator::initWasm(const CompileA
     MOZ_ASSERT(!env_->isAsmJS());
 
     metadata_ = js_new<Metadata>();
     if (!metadata_)
         return false;
 
     MOZ_ASSERT(!isAsmJS());
 
-    metadata_->debugEnabled = args.debugEnabled && BaselineCanCompile();
-    compileMode_ = args.alwaysBaseline || metadata_->debugEnabled
+    bool canBaseline = BaselineCanCompile();
+    metadata_->debugEnabled = args.debugEnabled && canBaseline;
+    compileMode_ = ((args.alwaysBaseline || metadata_->debugEnabled) && canBaseline)
                    ? CompileMode::Baseline
                    : CompileMode::Ion;
 
     // For wasm, the Vectors are correctly-sized and already initialized.
 
     numSigs_ = env_->sigs.length();
     numTables_ = env_->tables.length();
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -410,17 +410,17 @@ sandbox_finalize(js::FreeOp* fop, JSObje
 {
     nsIScriptObjectPrincipal* sop =
         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
     if (!sop) {
         // sop can be null if CreateSandboxObject fails in the middle.
         return;
     }
 
-    static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject();
+    static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject(obj);
     DestroyProtoAndIfaceCache(obj);
     DeferredFinalize(sop);
 }
 
 static void
 sandbox_moved(JSObject* obj, const JSObject* old)
 {
     // Note that this hook can be called before the private pointer is set. In
--- a/js/xpconnect/src/SandboxPrivate.h
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -38,19 +38,19 @@ public:
         return mPrincipal;
     }
 
     JSObject* GetGlobalJSObject() override
     {
         return GetWrapper();
     }
 
-    void ForgetGlobalObject()
+    void ForgetGlobalObject(JSObject* obj)
     {
-        ClearWrapper();
+        ClearWrapper(obj);
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
     {
         MOZ_CRASH("SandboxPrivate doesn't use DOM bindings!");
     }
 
     void ObjectMoved(JSObject* obj, const JSObject* old)
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3351,18 +3351,17 @@ nsXPCComponents_Utils::AllowCPOWsInAddon
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_Utils::Now(double* aRetval)
 {
-    bool isInconsistent = false;
-    TimeStamp start = TimeStamp::ProcessCreation(isInconsistent);
+    TimeStamp start = TimeStamp::ProcessCreation();
     *aRetval = (TimeStamp::Now() - start).ToMilliseconds();
     return NS_OK;
 }
 
 /***************************************************************************/
 /***************************************************************************/
 /***************************************************************************/
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -808,37 +808,44 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
                                bool isZoneGC,
                                void* data)
 {
     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     if (!self)
         return;
 
     switch (status) {
-        case JSFINALIZE_GROUP_START:
+        case JSFINALIZE_GROUP_PREPARE:
         {
             MOZ_ASSERT(!self->mDoingFinalization, "bad state");
 
             MOZ_ASSERT(!self->mGCIsRunning, "bad state");
             self->mGCIsRunning = true;
 
             self->mDoingFinalization = true;
+
+            break;
+        }
+        case JSFINALIZE_GROUP_START:
+        {
+            MOZ_ASSERT(self->mDoingFinalization, "bad state");
+
+            MOZ_ASSERT(self->mGCIsRunning, "bad state");
+            self->mGCIsRunning = false;
+
             break;
         }
         case JSFINALIZE_GROUP_END:
         {
+            // Sweep scopes needing cleanup
+            XPCWrappedNativeScope::KillDyingScopes();
+
             MOZ_ASSERT(self->mDoingFinalization, "bad state");
             self->mDoingFinalization = false;
 
-            // Sweep scopes needing cleanup
-            XPCWrappedNativeScope::KillDyingScopes();
-
-            MOZ_ASSERT(self->mGCIsRunning, "bad state");
-            self->mGCIsRunning = false;
-
             break;
         }
         case JSFINALIZE_COLLECTION_END:
         {
             MOZ_ASSERT(!self->mGCIsRunning, "bad state");
             self->mGCIsRunning = true;
 
             // For now we only have one context. Eventually we'll need to
@@ -908,17 +915,17 @@ XPCJSRuntime::WeakPointerZonesCallback(J
 {
     // Called before each sweeping slice -- after processing any final marking
     // triggered by barriers -- to clear out any references to things that are
     // about to be finalized and update any pointers to moved GC things.
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
 
     self->mWrappedJSMap->UpdateWeakPointersAfterGC();
 
-    XPCWrappedNativeScope::UpdateWeakPointersAfterGC();
+    XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC();
 }
 
 /* static */ void
 XPCJSRuntime::WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data)
 {
     // Called immediately after the ZoneGroup weak pointer callback, but only
     // once for each compartment that is being swept.
     CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -142,16 +142,18 @@ public:
         MOZ_ASSERT(!wrapperInMap || wrapperInMap == wrapper,
                    "About to remove a different wrapper with the same "
                    "nsISupports identity! This will most likely cause serious "
                    "problems!");
 #endif
         mTable.Remove(wrapper->GetIdentityObject());
     }
 
+    inline void Clear() { mTable.Clear(); }
+
     inline uint32_t Count() { return mTable.EntryCount(); }
 
     PLDHashTable::Iterator Iter() { return mTable.Iter(); }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 private:
     Native2WrappedNativeMap();    // no implementation
@@ -361,16 +363,18 @@ public:
     }
 
     inline void Remove(nsIClassInfo* info)
     {
         NS_PRECONDITION(info,"bad param");
         mTable.Remove(info);
     }
 
+    inline void Clear() { mTable.Clear(); }
+
     inline uint32_t Count() { return mTable.EntryCount(); }
 
     PLDHashTable::Iterator Iter() { return mTable.Iter(); }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 private:
     ClassInfo2WrappedNativeProtoMap();    // no implementation
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -564,24 +564,24 @@ XPCWrappedNative::~XPCWrappedNative()
     Destroy();
 }
 
 void
 XPCWrappedNative::Destroy()
 {
     mScriptable = nullptr;
 
+#ifdef DEBUG
+    // Check that this object has already been swept from the map.
     XPCWrappedNativeScope* scope = GetScope();
     if (scope) {
         Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
-
-        // Post-1.9 we should not remove this wrapper from the map if it is
-        // uninitialized.
-        map->Remove(this);
+        MOZ_ASSERT(map->Find(GetIdentityObject()) != this);
     }
+#endif
 
     if (mIdentity) {
         XPCJSRuntime* rt = GetRuntime();
         if (rt && rt->GetDoingFinalization()) {
             DeferredFinalize(mIdentity.forget().take());
         } else {
             mIdentity = nullptr;
         }
@@ -857,17 +857,17 @@ XPCWrappedNative::FlatJSObjectFinalized(
         }
 
         to->SetInterface(nullptr);
     }
 
     nsWrapperCache* cache = nullptr;
     CallQueryInterface(mIdentity, &cache);
     if (cache)
-        cache->ClearWrapper();
+        cache->ClearWrapper(mFlatJSObject.unbarrieredGetPtr());
 
     mFlatJSObject = nullptr;
     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
 
     MOZ_ASSERT(mIdentity, "bad pointer!");
 #ifdef XP_WIN
     // Try to detect free'd pointer
     MOZ_ASSERT(*(int*)mIdentity.get() != 0xdddddddd, "bad pointer!");
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -98,20 +98,21 @@ XPCWrappedNativeProto::CallPostCreatePro
     return true;
 }
 
 void
 XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(obj == mJSProtoObject, "huh?");
 
-    // Only remove this proto from the map if it is the one in the map.
+#ifdef DEBUG
+    // Check that this object has already been swept from the map.
     ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap();
-    if (map->Find(mClassInfo) == this)
-        map->Remove(mClassInfo);
+    MOZ_ASSERT(map->Find(mClassInfo) != this);
+#endif
 
     GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this);
 
     mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime());
 }
 
 void
 XPCWrappedNativeProto::JSProtoObjectMoved(JSObject* obj, const JSObject* old)
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -474,19 +474,16 @@ XPCWrappedNativeScope::~XPCWrappedNative
     // XXX we should assert that we are dead or that xpconnect has shutdown
     // XXX might not want to do this at xpconnect shutdown time???
     mComponents = nullptr;
 
     if (mXrayExpandos.initialized())
         mXrayExpandos.destroy();
 
     JSContext* cx = dom::danger::GetJSContext();
-    mContentXBLScope.finalize(cx);
-    for (size_t i = 0; i < mAddonScopes.Length(); i++)
-        mAddonScopes[i].finalize(cx);
     mGlobalJSObject.finalize(cx);
 }
 
 // static
 void
 XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc)
 {
     // Do JS::TraceEdge for all wrapped natives with external references, as
@@ -527,58 +524,118 @@ XPCWrappedNativeScope::SuspectAllWrapper
             for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront())
                 SuspectDOMExpandos(r.front().unbarrieredGet(), cb);
         }
     }
 }
 
 // static
 void
-XPCWrappedNativeScope::UpdateWeakPointersAfterGC()
+XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC()
 {
     // If this is called from the finalization callback in JSGC_MARK_END then
     // JSGC_FINALIZE_END must always follow it calling
     // FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
     // KillDyingScopes.
     MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END");
 
-    XPCWrappedNativeScope* prev = nullptr;
-    XPCWrappedNativeScope* cur = gScopes;
+    XPCWrappedNativeScope** scopep = &gScopes;
+    while (*scopep) {
+        XPCWrappedNativeScope* cur = *scopep;
+        cur->UpdateWeakPointersAfterGC();
+        if (cur->mGlobalJSObject) {
+            scopep = &cur->mNext;
+        } else {
+            // The scope's global is dead so move it to the dying scopes list.
+            *scopep = cur->mNext;
+            cur->mNext = gDyingScopes;
+            gDyingScopes = cur;
+        }
+    }
+}
 
-    while (cur) {
-        // Sweep waivers.
-        if (cur->mWaiverWrapperMap)
-            cur->mWaiverWrapperMap->Sweep();
+static inline void
+AssertSameCompartment(DebugOnly<JSCompartment*>& comp, JSObject* obj)
+{
+    MOZ_ASSERT_IF(obj, js::GetObjectCompartment(obj) == comp);
+}
 
-        XPCWrappedNativeScope* next = cur->mNext;
+static inline void
+AssertSameCompartment(DebugOnly<JSCompartment*>& comp, const JS::ObjectPtr& obj)
+{
+#ifdef DEBUG
+    AssertSameCompartment(comp, obj.unbarrieredGet());
+#endif
+}
 
-        if (cur->mContentXBLScope)
-            cur->mContentXBLScope.updateWeakPointerAfterGC();
-        for (size_t i = 0; i < cur->mAddonScopes.Length(); i++)
-            cur->mAddonScopes[i].updateWeakPointerAfterGC();
+void
+XPCWrappedNativeScope::UpdateWeakPointersAfterGC()
+{
+    // Sweep waivers.
+    if (mWaiverWrapperMap)
+        mWaiverWrapperMap->Sweep();
+
+    if (!js::IsObjectZoneSweepingOrCompacting(mGlobalJSObject.unbarrieredGet()))
+        return;
 
-        // Check for finalization of the global object or update our pointer if
-        // it was moved.
-        if (cur->mGlobalJSObject) {
-            cur->mGlobalJSObject.updateWeakPointerAfterGC();
-            if (!cur->mGlobalJSObject) {
-                // Move this scope from the live list to the dying list.
-                if (prev)
-                    prev->mNext = next;
-                else
-                    gScopes = next;
-                cur->mNext = gDyingScopes;
-                gDyingScopes = cur;
-                cur = nullptr;
-            }
-        }
+    // Update our pointer to the global object in case it was moved or
+    // finalized.
+    mGlobalJSObject.updateWeakPointerAfterGC();
+    if (!mGlobalJSObject) {
+        JSContext* cx = dom::danger::GetJSContext();
+        mContentXBLScope.finalize(cx);
+        for (size_t i = 0; i < mAddonScopes.Length(); i++)
+            mAddonScopes[i].finalize(cx);
+        GetWrappedNativeMap()->Clear();
+        mWrappedNativeProtoMap->Clear();
+        return;
+    }
+
+    DebugOnly<JSCompartment*> comp =
+        js::GetObjectCompartment(mGlobalJSObject.unbarrieredGet());
 
-        if (cur)
-            prev = cur;
-        cur = next;
+#ifdef DEBUG
+    // These are traced, so no updates are necessary.
+    if (mContentXBLScope) {
+        JSObject* prev = mContentXBLScope.unbarrieredGet();
+        mContentXBLScope.updateWeakPointerAfterGC();
+        MOZ_ASSERT(prev == mContentXBLScope.unbarrieredGet());
+        AssertSameCompartment(comp, mContentXBLScope);
+    }
+    for (size_t i = 0; i < mAddonScopes.Length(); i++) {
+        JSObject* prev = mAddonScopes[i].unbarrieredGet();
+        mAddonScopes[i].updateWeakPointerAfterGC();
+        MOZ_ASSERT(prev == mAddonScopes[i].unbarrieredGet());
+        AssertSameCompartment(comp, mAddonScopes[i]);
+    }
+#endif
+
+    // Sweep mWrappedNativeMap for dying flat JS objects. Moving has already
+    // been handled by XPCWrappedNative::FlatJSObjectMoved.
+    for (auto iter = GetWrappedNativeMap()->Iter(); !iter.Done(); iter.Next()) {
+        auto entry = static_cast<Native2WrappedNativeMap::Entry*>(iter.Get());
+        XPCWrappedNative* wrapper = entry->value;
+        JSObject* obj = wrapper->GetFlatJSObjectPreserveColor();
+        JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
+        MOZ_ASSERT(!obj || obj == wrapper->GetFlatJSObjectPreserveColor());
+        AssertSameCompartment(comp, obj);
+        if (!obj)
+            iter.Remove();
+    }
+
+    // Sweep mWrappedNativeProtoMap for dying prototype JSObjects. Moving has
+    // already been handled by XPCWrappedNativeProto::JSProtoObjectMoved.
+    for (auto i = mWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
+        auto entry = static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(i.Get());
+        JSObject* obj = entry->value->GetJSProtoObjectPreserveColor();
+        JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
+        AssertSameCompartment(comp, obj);
+        MOZ_ASSERT(!obj || obj == entry->value->GetJSProtoObjectPreserveColor());
+        if (!obj)
+            i.Remove();
     }
 }
 
 // static
 void
 XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs()
 {
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -957,16 +957,19 @@ public:
 
     static void
     SuspectAllWrappers(nsCycleCollectionNoteRootCallback& cb);
 
     static void
     SweepAllWrappedNativeTearOffs();
 
     static void
+    UpdateWeakPointersInAllScopesAfterGC();
+
+    void
     UpdateWeakPointersAfterGC();
 
     static void
     KillDyingScopes();
 
     static void
     DebugDumpAllScopes(int16_t depth);
 
@@ -1439,16 +1442,19 @@ public:
     GetScope()   const {return mScope;}
 
     XPCJSRuntime*
     GetRuntime() const {return mScope->GetRuntime();}
 
     JSObject*
     GetJSProtoObject() const { return mJSProtoObject; }
 
+    JSObject*
+    GetJSProtoObjectPreserveColor() const { return mJSProtoObject.unbarrieredGet(); }
+
     nsIClassInfo*
     GetClassInfo()     const {return mClassInfo;}
 
     XPCNativeSet*
     GetSet()           const {return mSet;}
 
     nsIXPCScriptable*
     GetScriptable() const { return mScriptable; }
--- a/layout/forms/test/test_bug536567_perwindowpb.html
+++ b/layout/forms/test/test_bug536567_perwindowpb.html
@@ -165,17 +165,20 @@ function testOnWindow(aIsPrivate, aCallb
     win.setTimeout(function() { win.gBrowser.loadURI(contentPage); }, 0);
   });
 }
 
 MockFilePicker.showCallback = function(filepicker) {
   var test = tests[testIndex];
   var returned = -1;
   for (var i = 0; i < dirs.length; i++) {
-    if (dirs[i].path == MockFilePicker.displayDirectory.path) {
+     var dir = MockFilePicker.displayDirectory
+                 ? MockFilePicker.displayDirectory
+                 : Services.dirsvc.get(MockFilePicker.displaySpecialDirectory, Ci.nsILocalFile);
+    if (dirs[i].path == dir.path) {
       returned = i;
       break;
     }
   }
   if (test[1] == -1) {
     ok(false, "We should never get an unknown directory back");
   } else {
     is(returned, test[1], 'test ' + testIndex);
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1787,16 +1787,20 @@ nsImageFrame::BuildDisplayList(nsDisplay
       // decoded yet. And we are not going to ask the image to draw, so this
       // may be the only chance to tell it that it should decode.
       if (currentRequest) {
         uint32_t status = 0;
         currentRequest->GetImageStatus(&status);
         if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
           MaybeDecodeForPredictedSize();
         }
+        // Increase loading priority if the image is ready to be displayed.
+        if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)){
+          currentRequest->BoostPriority(imgIRequest::CATEGORY_DISPLAY);
+        }
       }
     } else {
       aLists.Content()->AppendNewToTop(new (aBuilder)
         nsDisplayImage(aBuilder, this, mImage, mPrevImage));
 
       // If we were previously displaying an icon, we're not anymore
       if (mDisplayingIcon) {
         gIconLoad->RemoveIconObserver(this);
--- a/layout/style/nsCSSAnonBoxes.cpp
+++ b/layout/style/nsCSSAnonBoxes.cpp
@@ -51,16 +51,17 @@ bool nsCSSAnonBoxes::IsAnonBox(nsIAtom *
   return nsAtomListUtils::IsMember(aAtom, CSSAnonBoxes_info,
                                    ArrayLength(CSSAnonBoxes_info));
 }
 
 #ifdef MOZ_XUL
 /* static */ bool
 nsCSSAnonBoxes::IsTreePseudoElement(nsIAtom* aPseudo)
 {
+  MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudo));
   return StringBeginsWith(nsDependentAtomString(aPseudo),
                           NS_LITERAL_STRING(":-moz-tree-"));
 }
 #endif
 
 /* static*/ nsCSSAnonBoxes::NonInheriting
 nsCSSAnonBoxes::NonInheritingTypeForPseudoTag(nsIAtom* aPseudo)
 {
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -2167,17 +2167,17 @@ nsStyleSet::ResolveNonInheritingAnonymou
                nullptr, eNoFlags);
   cache = retval;
   return retval.forget();
 }
 
 #ifdef MOZ_XUL
 already_AddRefed<nsStyleContext>
 nsStyleSet::ResolveXULTreePseudoStyle(Element* aParentElement,
-                                      nsIAtom* aPseudoTag,
+                                      nsICSSAnonBoxPseudo* aPseudoTag,
                                       nsStyleContext* aParentContext,
                                       nsICSSPseudoComparator* aComparator)
 {
   NS_ENSURE_FALSE(mInShutdown, nullptr);
 
   NS_ASSERTION(aPseudoTag, "must have pseudo tag");
   NS_ASSERTION(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag),
                "Unexpected pseudo");
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -293,17 +293,17 @@ class nsStyleSet final
   ResolveNonInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag);
 
 #ifdef MOZ_XUL
   // Get a style context for a XUL tree pseudo.  aPseudoTag is the
   // pseudo-tag to use and must be non-null.  aParentContent must be
   // non-null.  aComparator must be non-null.
   already_AddRefed<nsStyleContext>
   ResolveXULTreePseudoStyle(mozilla::dom::Element* aParentElement,
-                            nsIAtom* aPseudoTag,
+                            nsICSSAnonBoxPseudo* aPseudoTag,
                             nsStyleContext* aParentContext,
                             nsICSSPseudoComparator* aComparator);
 #endif
 
   // Append all the currently-active font face rules to aArray.  Return
   // true for success and false for failure.
   bool AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray);
 
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -1052,17 +1052,17 @@ nsTreeBodyFrame::GetCellAt(int32_t aX, i
 
   // Check if the coordinates are above our visible space.
   if (point.y < 0) {
     *aRow = -1;
     return NS_OK;
   }
 
   nsTreeColumn* col;
-  nsIAtom* child;
+  nsICSSAnonBoxPseudo* child;
   GetCellAt(point.x, point.y, aRow, &col, &child);
 
   if (col) {
     NS_ADDREF(*aCol = col);
     if (child == nsCSSAnonBoxes::moztreecell)
       aChildElt.AssignLiteral("cell");
     else if (child == nsCSSAnonBoxes::moztreetwisty)
       aChildElt.AssignLiteral("twisty");
@@ -1479,17 +1479,17 @@ nsTreeBodyFrame::AdjustForCellText(nsAut
       aTextRect.x += (aTextRect.width - width) / 2;
     }
     break;
   }
 
   aTextRect.width = width;
 }
 
-nsIAtom*
+nsICSSAnonBoxPseudo*
 nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, 
                                      int32_t aRowIndex,
                                      nsTreeColumn* aColumn)
 {
   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
 
   // Obtain the properties for our cell.
   PrefillPropertyArray(aRowIndex, aColumn);
@@ -1629,17 +1629,18 @@ nsTreeBodyFrame::GetItemWithinCellAt(nsc
   if (aX >= textRect.x && aX < textRect.x + textRect.width)
     return nsCSSAnonBoxes::moztreecelltext;
   else
     return nsCSSAnonBoxes::moztreecell;
 }
 
 void
 nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow,
-                           nsTreeColumn** aCol, nsIAtom** aChildElt)
+                           nsTreeColumn** aCol,
+                           nsICSSAnonBoxPseudo** aChildElt)
 {
   *aCol = nullptr;
   *aChildElt = nullptr;
 
   *aRow = GetRowAt(aX, aY);
   if (*aRow < 0)
     return;
 
@@ -2523,17 +2524,17 @@ nsTreeBodyFrame::GetCursor(const nsPoint
                            nsIFrame::Cursor& aCursor)
 {
   // Check the GetScriptHandlingObject so we don't end up running code when
   // the document is a zombie.
   bool dummy;
   if (mView && GetContent()->GetComposedDoc()->GetScriptHandlingObject(dummy)) {
     int32_t row;
     nsTreeColumn* col;
-    nsIAtom* child;
+    nsICSSAnonBoxPseudo* child;
     GetCellAt(aPoint.x, aPoint.y, &row, &col, &child);
 
     if (child) {
       // Our scratch array is already prefilled.
       nsStyleContext* childContext = GetPseudoStyleContext(child);
 
       FillCursorInformationFromStyle(childContext->StyleUserInterface(),
                                      aCursor);
@@ -4470,17 +4471,17 @@ nsTreeBodyFrame::ThumbMoved(nsScrollbarF
   }
   if (weakFrame.IsAlive()) {
     UpdateScrollbars(parts);
   }
 }
 
 // The style cache.
 nsStyleContext*
-nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement)
+nsTreeBodyFrame::GetPseudoStyleContext(nsICSSAnonBoxPseudo* aPseudoElement)
 {
   return mStyleCache.GetStyleContext(this, PresContext(), mContent,
                                      mStyleContext, aPseudoElement,
                                      mScratchArray);
 }
 
 // Our comparator for resolving our complex pseudos
 bool
--- a/layout/xul/tree/nsTreeBodyFrame.h
+++ b/layout/xul/tree/nsTreeBodyFrame.h
@@ -313,23 +313,25 @@ protected:
   void AdjustForCellText(nsAutoString& aText,
                          int32_t aRowIndex,
                          nsTreeColumn* aColumn,
                          nsRenderingContext& aRenderingContext,
                          nsFontMetrics& aFontMetrics,
                          nsRect& aTextRect);
 
   // A helper used when hit testing.
-  nsIAtom* GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect,
-                               int32_t aRowIndex, nsTreeColumn* aColumn);
+  nsICSSAnonBoxPseudo* GetItemWithinCellAt(nscoord aX,
+                                           const nsRect& aCellRect,
+                                           int32_t aRowIndex,
+                                           nsTreeColumn* aColumn);
 
   // An internal hit test.  aX and aY are expected to be in twips in the
   // coordinate system of this frame.
   void GetCellAt(nscoord aX, nscoord aY, int32_t* aRow, nsTreeColumn** aCol,
-                 nsIAtom** aChildElt);
+                 nsICSSAnonBoxPseudo** aChildElt);
 
   // Retrieve the area for the twisty for a cell.
   nsITheme* GetTwistyRect(int32_t aRowIndex,
                           nsTreeColumn* aColumn,
                           nsRect& aImageRect,
                           nsRect& aTwistyRect,
                           nsPresContext* aPresContext,
                           nsStyleContext* aTwistyContext);
@@ -357,17 +359,17 @@ protected:
   // Calculates our width/height once border and padding have been removed.
   void CalcInnerBox();
 
   // Calculate the total width of our scrollable portion
   nscoord CalcHorzWidth(const ScrollParts& aParts);
 
   // Looks up a style context in the style cache.  On a cache miss we resolve
   // the pseudo-styles passed in and place them into the cache.
-  nsStyleContext* GetPseudoStyleContext(nsIAtom* aPseudoElement);
+  nsStyleContext* GetPseudoStyleContext(nsICSSAnonBoxPseudo* aPseudoElement);
 
   // Retrieves the scrollbars and scrollview relevant to this treebody. We
   // traverse the frame tree under our base element, in frame order, looking
   // for the first relevant vertical scrollbar, horizontal scrollbar, and
   // scrollable frame (with associated content and scrollable view). These
   // are all volatile and should not be retained.
   ScrollParts GetScrollParts();
 
--- a/layout/xul/tree/nsTreeStyleCache.cpp
+++ b/layout/xul/tree/nsTreeStyleCache.cpp
@@ -31,19 +31,21 @@ nsTreeStyleCache::Transition::Hash() con
 
 
 // The style context cache impl
 nsStyleContext*
 nsTreeStyleCache::GetStyleContext(nsICSSPseudoComparator* aComparator,
                                   nsPresContext* aPresContext,
                                   nsIContent* aContent,
                                   nsStyleContext* aContext,
-                                  nsIAtom* aPseudoElement,
+                                  nsICSSAnonBoxPseudo* aPseudoElement,
                                   const AtomArray & aInputWord)
 {
+  MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoElement));
+
   uint32_t count = aInputWord.Length();
 
   // Go ahead and init the transition table.
   if (!mTransitionTable) {
     // Automatic miss. Build the table
     mTransitionTable = new TransitionTable();
   }
 
--- a/layout/xul/tree/nsTreeStyleCache.h
+++ b/layout/xul/tree/nsTreeStyleCache.h
@@ -35,17 +35,17 @@ public:
     mCache = nullptr;
     mNextState = 0;
   }
 
   nsStyleContext* GetStyleContext(nsICSSPseudoComparator* aComparator,
                                   nsPresContext* aPresContext,
                                   nsIContent* aContent,
                                   nsStyleContext* aContext,
-                                  nsIAtom* aPseudoElement,
+                                  nsICSSAnonBoxPseudo* aPseudoElement,
                                   const AtomArray & aInputWord);
 
 protected:
   typedef uint32_t DFAState;
 
   class Transition final
   {
   public:
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -259,17 +259,17 @@
             </intent-filter>
         </receiver>
 
 #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_activities.xml.in
 #endif
 
-#if MOZ_CRASHREPORTER
+#ifdef MOZ_CRASHREPORTER
   <activity android:name="org.mozilla.gecko.CrashReporter"
             android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
             android:label="@string/crash_reporter_title"
             android:icon="@drawable/crash_reporter"
             android:theme="@style/Gecko"
             android:exported="false"
             android:excludeFromRecents="true">
           <intent-filter>
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -186,17 +186,17 @@ public class AppConstants {
     public static final boolean MOZ_TELEMETRY_REPORTING =
 //#ifdef MOZ_TELEMETRY_REPORTING
     true;
 //#else
     false;
 //#endif
 
     public static final boolean MOZ_CRASHREPORTER =
-//#if MOZ_CRASHREPORTER
+//#ifdef MOZ_CRASHREPORTER
     true;
 //#else
     false;
 //#endif
 
     public static final boolean MOZ_DATA_REPORTING =
 //#ifdef MOZ_DATA_REPORTING
       true;
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -3,38 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # We call mach -> Make -> gradle -> mach, which races to find and
 # create .mozconfig files and to generate targets.
 ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
 .NOTPARALLEL:
 endif
 
-MOZ_BUILDID := $(shell awk '{print $$3}' $(DEPTH)/buildid.h)
-
-# Set the appropriate version code, based on the existance of the
-# MOZ_APP_ANDROID_VERSION_CODE variable.
-ifdef MOZ_APP_ANDROID_VERSION_CODE
-    ANDROID_VERSION_CODE:=$(MOZ_APP_ANDROID_VERSION_CODE)
-else
-    ANDROID_VERSION_CODE:=$(shell $(PYTHON) \
-      $(topsrcdir)/python/mozbuild/mozbuild/android_version_code.py \
-        --verbose \
-        --with-android-cpu-arch=$(ANDROID_CPU_ARCH) \
-        $(if $(MOZ_ANDROID_MIN_SDK_VERSION),--with-android-min-sdk=$(MOZ_ANDROID_MIN_SDK_VERSION)) \
-        $(if $(MOZ_ANDROID_MAX_SDK_VERSION),--with-android-max-sdk=$(MOZ_ANDROID_MAX_SDK_VERSION)) \
-        $(MOZ_BUILDID))
-endif
-
-DEFINES += \
-  -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
-  -DMOZ_ANDROID_SHARED_ID="$(MOZ_ANDROID_SHARED_ID)" \
-  -DMOZ_BUILDID=$(MOZ_BUILDID) \
-  $(NULL)
-
 GARBAGE += \
   classes.dex  \
   gecko.ap_  \
   res/values/strings.xml \
   res/raw/browsersearch.json \
   res/raw/suggestedsites.json \
   .aapt.deps \
   GeneratedJNINatives.h \
@@ -304,17 +282,17 @@ classycle_jar := $(topsrcdir)/mobile/and
 		-Xmx512m -Xms128m \
 		-jar $(ANDROID_SDK_ROOT)/tools/proguard/lib/proguard.jar \
 		@$(proguard_config_dir)/proguard.cfg \
 		-optimizationpasses $(PROGUARD_PASSES) \
 		-injars $(subst ::,:,$(all_jars_classpath)):bundled-jars-nodebug \
 		-outjars jars-proguarded \
 		-libraryjars $(library_jars)
 
-ANNOTATION_PROCESSOR_JAR_FILES := $(DEPTH)/build/annotationProcessors/annotationProcessors.jar
+ANNOTATION_PROCESSOR_JAR_FILES := $(abspath $(DEPTH)/build/annotationProcessors/annotationProcessors.jar)
 
 # This annotation processing step also generates
 # GeneratedJNIWrappers.h and GeneratedJNINatives.h
 GeneratedJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES) $(GECKOVIEW_JARS)
 	$(JAVA) -classpath $(geckoview_jars_classpath):$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) \
 		org.mozilla.gecko.annotationProcessors.AnnotationProcessor \
 		Generated $(GECKOVIEW_JARS)
 
@@ -370,17 +348,17 @@ android_res_files := $(filter-out $(not_
 # suggestedsites.json. The trailing semi-colon defines an empty
 # recipe: defining no recipe at all causes Make to treat the target
 # differently, in a way that defeats our dependencies.
 res/values/strings.xml: .locales.deps ;
 res/raw/browsersearch.json: .locales.deps ;
 res/raw/suggestedsites.json: .locales.deps ;
 
 all_resources = \
-  $(DEPTH)/mobile/android/base/AndroidManifest.xml \
+  AndroidManifest.xml \
   $(android_res_files) \
   $(ANDROID_GENERATED_RESFILES) \
   $(NULL)
 
 # All of generated/org/mozilla/gecko/R.java, gecko.ap_, and R.txt are
 # produced by aapt; this saves aapt invocations.  The trailing
 # semi-colon defines an empty recipe; defining no recipe at all causes
 # Make to treat the target differently, in a way that defeats our
@@ -474,18 +452,18 @@ endef
 
 # .aapt.deps: $(all_resources)
 $(eval $(call aapt_command,.aapt.deps,$(all_resources),gecko.ap_,generated/,./))
 
 ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
 .aapt.nodeps: FORCE
 	cp $(gradle_dir)/app/intermediates/res/resources-automation-debug.ap_ gecko-nodeps.ap_
 else
-# .aapt.nodeps: $(DEPTH)/mobile/android/base/AndroidManifest.xml FORCE
-$(eval $(call aapt_command,.aapt.nodeps,$(DEPTH)/mobile/android/base/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
+# .aapt.nodeps: AndroidManifest.xml FORCE
+$(eval $(call aapt_command,.aapt.nodeps,AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
 endif
 
 # Override the Java settings with some specific android settings
 include $(topsrcdir)/config/android-common.mk
 
 update-generated-wrappers:
 	@cp $(CURDIR)/GeneratedJNIWrappers.cpp \
 	    $(CURDIR)/GeneratedJNIWrappers.h \
@@ -510,17 +488,17 @@ update-fennec-wrappers:
 	$(MAKE) -C ../../../faster
 	$(MAKE) -C ../installer stage-package
 	$(MKDIR) -p $(@D)
 	rsync --update $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) $@
 	$(RM) $(DIST)/fennec/$(notdir $(OMNIJAR_NAME))
 
 # Targets built very early during a Gradle build.
 gradle-targets: $(foreach f,$(constants_PP_JAVAFILES),$(f))
-gradle-targets: $(DEPTH)/mobile/android/base/AndroidManifest.xml
+gradle-targets: AndroidManifest.xml
 gradle-targets: $(ANDROID_GENERATED_RESFILES)
 
 ifndef MOZILLA_OFFICIAL
 # Local developers update omni.ja during their builds.  There's a
 # chicken-and-egg problem here.
 gradle-omnijar: $(abspath $(DIST)/fennec/$(OMNIJAR_NAME))
 else
 # In automation, omni.ja is built only during packaging.
--- a/mobile/android/base/generate_build_config.py
+++ b/mobile/android/base/generate_build_config.py
@@ -23,16 +23,17 @@ from __future__ import (
 
 from collections import defaultdict
 import os
 import sys
 
 import buildconfig
 
 from mozbuild import preprocessor
+from mozbuild.android_version_code import android_version_code
 
 
 def _defines():
     CONFIG = defaultdict(lambda: None)
     CONFIG.update(buildconfig.substs)
     DEFINES = dict(buildconfig.defines)
 
     for var in ('MOZ_ANDROID_ACTIVITY_STREAM'
@@ -40,16 +41,17 @@ def _defines():
                 'MOZ_ANDROID_BEAM',
                 'MOZ_ANDROID_CUSTOM_TABS',
                 'MOZ_ANDROID_DOWNLOADS_INTEGRATION',
                 'MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE',
                 'MOZ_ANDROID_EXCLUDE_FONTS',
                 'MOZ_ANDROID_GCM',
                 'MOZ_ANDROID_MLS_STUMBLER',
                 'MOZ_ANDROID_SEARCH_ACTIVITY',
+                'MOZ_CRASHREPORTER',
                 'MOZ_DEBUG',
                 'MOZ_INSTALL_TRACKING',
                 'MOZ_LOCALE_SWITCHER',
                 'MOZ_NATIVE_DEVICES',
                 'MOZ_SWITCHBOARD'):
         if CONFIG[var]:
             DEFINES[var] = 1
 
@@ -57,28 +59,28 @@ def _defines():
                 'MOZ_PKG_SPECIAL',
                 'MOZ_UPDATER'):
         if CONFIG[var]:
             DEFINES[var] = CONFIG[var]
 
     for var in ('ANDROID_CPU_ARCH',
                 'ANDROID_PACKAGE_NAME',
                 'GRE_MILESTONE',
+                'MOZ_ANDROID_SHARED_ID',
                 'MOZ_ANDROID_APPLICATION_CLASS',
                 'MOZ_ANDROID_BROWSER_INTENT_CLASS',
                 'MOZ_ANDROID_SEARCH_INTENT_CLASS',
                 'MOZ_APP_BASENAME',
                 'MOZ_APP_DISPLAYNAME',
                 'MOZ_APP_ID',
                 'MOZ_APP_NAME',
                 'MOZ_APP_UA_NAME',
                 'MOZ_APP_VENDOR',
                 'MOZ_APP_VERSION',
                 'MOZ_CHILD_PROCESS_NAME',
-                'MOZ_CRASHREPORTER',
                 'MOZ_MOZILLA_API_KEY',
                 'MOZ_UPDATE_CHANNEL',
                 'OMNIJAR_NAME',
                 'OS_TARGET',
                 'TARGET_XPCOM_ABI'):
         DEFINES[var] = CONFIG[var]
 
     # Mangle our package name to avoid Bug 750548.
@@ -96,18 +98,40 @@ def _defines():
 
     # It's okay to use MOZ_ADJUST_SDK_KEY here because this doesn't
     # leak the value to build logs.
     if CONFIG['MOZ_INSTALL_TRACKING']:
         DEFINES['MOZ_INSTALL_TRACKING_ADJUST_SDK_APP_TOKEN'] = CONFIG['MOZ_ADJUST_SDK_KEY']
 
     DEFINES['MOZ_BUILDID'] = open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]
 
+    # Set the appropriate version code if not set by MOZ_APP_ANDROID_VERSION_CODE.
+    if CONFIG.get('MOZ_APP_ANDROID_VERSION_CODE'):
+        DEFINES['ANDROID_VERSION_CODE'] = \
+            CONFIG.get('MOZ_APP_ANDROID_VERSION_CODE')
+    else:
+        min_sdk = int(CONFIG.get('MOZ_ANDROID_MIN_SDK_VERSION') or '0') or None
+        max_sdk = int(CONFIG.get('MOZ_ANDROID_MAX_SDK_VERSION') or '0') or None
+        DEFINES['ANDROID_VERSION_CODE'] = android_version_code(
+            buildid=DEFINES['MOZ_BUILDID'],
+            cpu_arch=CONFIG['ANDROID_CPU_ARCH'],
+            min_sdk=min_sdk,
+            max_sdk=max_sdk)
+
     return DEFINES
 
 
 def generate_java(output_file, input_filename):
     includes = preprocessor.preprocess(includes=[input_filename],
                                    defines=_defines(),
                                    output=output_file,
-                                   marker="//#")
+                                   marker='//#')
     includes.add(os.path.join(buildconfig.topobjdir, 'buildid.h'))
     return includes
+
+
+def generate_android_manifest(output_file, input_filename):
+    includes = preprocessor.preprocess(includes=[input_filename],
+                                       defines=_defines(),
+                                       output=output_file,
+                                       marker='#')
+    includes.add(os.path.join(buildconfig.topobjdir, 'buildid.h'))
+    return includes
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -114,28 +114,32 @@ with Files('resources/menu/*activitystre
 
 with Files('resources/menu/browsersearch_contextmenu.xml'):
     BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
 
 DIRS += ['locales']
 
 GENERATED_FILES += [
     '../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java',
+    'AndroidManifest.xml',
     'generated/preprocessed/org/mozilla/gecko/AdjustConstants.java',
     'generated/preprocessed/org/mozilla/gecko/AppConstants.java',
 ]
 w = GENERATED_FILES['../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java']
 w.script = 'generate_build_config.py:generate_java'
 w.inputs += ['../geckoview/BuildConfig.java.in']
 x = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AdjustConstants.java']
 x.script = 'generate_build_config.py:generate_java'
 x.inputs += ['AdjustConstants.java.in']
 y = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AppConstants.java']
 y.script = 'generate_build_config.py:generate_java'
 y.inputs += ['AppConstants.java.in']
+z = GENERATED_FILES['AndroidManifest.xml']
+z.script = 'generate_build_config.py:generate_android_manifest'
+z.inputs += ['AndroidManifest.xml.in']
 
 include('android-services.mozbuild')
 
 geckoview_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/main/'
 geckoview_thirdparty_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/thirdparty/'
 thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/'
 
 constants_jar = add_java_jar('constants')
@@ -1224,55 +1228,16 @@ if CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRE
     # If you change this, also change its equivalent in mobile/android/bouncer.
     if not CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']:
         # If we are packaging the bouncer, it will have the distribution, so don't put
         # it in the main APK as well.
         ANDROID_ASSETS_DIRS += [
             '%' + CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY'] + '/assets',
         ]
 
-# We do not expose MOZ_ADJUST_SDK_KEY here because that # would leak the value
-# to build logs.  Instead we expose the token quietly where appropriate in
-# Makefile.in.
-for var in ('MOZ_ANDROID_ANR_REPORTER', 'MOZ_DEBUG',
-            'MOZ_ANDROID_SEARCH_ACTIVITY', 'MOZ_NATIVE_DEVICES', 'MOZ_ANDROID_MLS_STUMBLER',
-            'MOZ_ANDROID_DOWNLOADS_INTEGRATION', 'MOZ_INSTALL_TRACKING',
-            'MOZ_ANDROID_GCM', 'MOZ_ANDROID_EXCLUDE_FONTS', 'MOZ_LOCALE_SWITCHER',
-            'MOZ_ANDROID_BEAM', 'MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE',
-            'MOZ_SWITCHBOARD', 'MOZ_ANDROID_CUSTOM_TABS',
-            'MOZ_ANDROID_ACTIVITY_STREAM'):
-    if CONFIG[var]:
-        DEFINES[var] = 1
-
-for var in ('MOZ_UPDATER', 'MOZ_PKG_SPECIAL', 'MOZ_ANDROID_GCM_SENDERID'):
-    if CONFIG[var]:
-        DEFINES[var] = CONFIG[var]
-
-for var in ('ANDROID_PACKAGE_NAME', 'ANDROID_CPU_ARCH',
-            'GRE_MILESTONE', 'MOZ_APP_BASENAME', 'MOZ_MOZILLA_API_KEY',
-            'MOZ_APP_DISPLAYNAME', 'MOZ_APP_UA_NAME', 'MOZ_APP_ID', 'MOZ_APP_NAME',
-            'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'MOZ_CHILD_PROCESS_NAME',
-            'MOZ_ANDROID_APPLICATION_CLASS', 'MOZ_ANDROID_BROWSER_INTENT_CLASS', 'MOZ_ANDROID_SEARCH_INTENT_CLASS',
-            'MOZ_CRASHREPORTER', 'MOZ_UPDATE_CHANNEL', 'OMNIJAR_NAME',
-            'OS_TARGET', 'TARGET_XPCOM_ABI'):
-    DEFINES[var] = CONFIG[var]
-
-# Mangle our package name to avoid Bug 750548.
-DEFINES['MANGLED_ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME'].replace('fennec', 'f3nn3c')
-DEFINES['MOZ_APP_ABI'] = CONFIG['TARGET_XPCOM_ABI']
-if not CONFIG['COMPILE_ENVIRONMENT']:
-    # These should really come from the included binaries, but that's not easy.
-    DEFINES['MOZ_APP_ABI'] = 'arm-eabi-gcc3' # Observe quote differences here ...
-    DEFINES['TARGET_XPCOM_ABI'] = '"arm-eabi-gcc3"' # ... and here.
-
-if '-march=armv7' in CONFIG['OS_CFLAGS']:
-    DEFINES['MOZ_MIN_CPU_VERSION'] = 7
-else:
-    DEFINES['MOZ_MIN_CPU_VERSION'] = 5
-
 if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']:
     # The Search Activity is mostly independent of Fennec proper, but
     # it does depend on Geckoview.  Therefore, we build it as a jar
     # that depends on the Geckoview jars.
     search_source_dir = SRCDIR + '/../search'
     include('../search/search_activity_sources.mozbuild')
 
     search_activity = add_java_jar('search-activity')
@@ -1286,25 +1251,19 @@ if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']
         'gecko-R.jar',
         'gecko-browser.jar',
         'gecko-mozglue.jar',
         'gecko-thirdparty.jar',
         'gecko-util.jar',
         'gecko-view.jar',
     ]
 
+DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
 FINAL_TARGET_PP_FILES += ['package-name.txt.in']
 
-DEFINES['OBJDIR'] = OBJDIR
-DEFINES['TOPOBJDIR'] = TOPOBJDIR
-
-OBJDIR_PP_FILES.mobile.android.base += [
-    'AndroidManifest.xml.in',
-]
-
 gvjar.sources += ['generated/org/mozilla/gecko/' + x for x in [
     'IGeckoEditableChild.java',
     'IGeckoEditableParent.java',
     'media/ICodec.java',
     'media/ICodecCallbacks.java',
     'media/IMediaDrmBridge.java',
     'media/IMediaDrmBridgeCallbacks.java',
     'media/IMediaManager.java',
--- a/mobile/android/components/FilePicker.js
+++ b/mobile/android/components/FilePicker.js
@@ -19,16 +19,17 @@ function FilePicker() {
 FilePicker.prototype = {
   _mimeTypeFilter: 0,
   _extensionsFilter: "",
   _defaultString: "",
   _domWin: null,
   _domFile: null,
   _defaultExtension: null,
   _displayDirectory: null,
+  _displaySpecialDirectory: null,
   _filePath: null,
   _promptActive: false,
   _filterIndex: 0,
   _addToRecentDocs: false,
   _title: "",
 
   init: function(aParent, aTitle, aMode) {
     this._domWin = aParent;
@@ -117,16 +118,24 @@ FilePicker.prototype = {
   get displayDirectory() {
     return this._displayDirectory;
   },
 
   set displayDirectory(dir) {
     this._displayDirectory = dir;
   },
 
+  get displaySpecialDirectory() {
+    return this._displaySpecialDirectory;
+  },
+
+  set displaySpecialDirectory(dir) {
+    this._displaySpecialDirectory = dir;
+  },
+
   get file() {
     if (!this._filePath) {
         return null;
     }
 
     return new FileUtils.File(this._filePath);
   },
 
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -1012,16 +1012,23 @@ FilePickerDelegate.prototype = {
 
   get displayDirectory() {
     return null;
   },
 
   set displayDirectory(aValue) {
   },
 
+  get displaySpecialDirectory() {
+    return "";
+  },
+
+  set displaySpecialDirectory(aValue) {
+  },
+
   get addToRecentDocs() {
     return false;
   },
 
   set addToRecentDocs(aValue) {
   },
 
   get okButtonLabel() {
--- a/mobile/android/geckoview/proguard-rules.txt
+++ b/mobile/android/geckoview/proguard-rules.txt
@@ -3,17 +3,17 @@
 # Preserve all annotations.
 
 -keepattributes *Annotation*
 
 # Preserve all public classes, and their public and protected fields and
 # methods.
 
 -keep public class * {
-    public protected *;
+    static public protected *;
 }
 
 # Preserve all .class method names.
 
 -keepclassmembernames class * {
     java.lang.Class class$(java.lang.String);
     java.lang.Class class$(java.lang.String, boolean);
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -226,17 +226,18 @@ public class GeckoView extends LayerView
             } else if ("GeckoView:PageStop".equals(event)) {
                 if (mProgressListener != null) {
                     mProgressListener.onPageStop(GeckoView.this, message.getBoolean("success"));
                 }
             } else if ("GeckoView:Prompt".equals(event)) {
                 handlePromptEvent(GeckoView.this, message, callback);
             } else if ("GeckoView:SecurityChanged".equals(event)) {
                 if (mProgressListener != null) {
-                    mProgressListener.onSecurityChange(GeckoView.this, message.getInt("status"));
+                    int state = message.getInt("status") & ProgressListener.STATE_ALL;
+                    mProgressListener.onSecurityChange(GeckoView.this, state);
                 }
             }
         }
     }
 
     protected Window mWindow;
     private boolean mStateSaved;
     private final Listener mListener = new Listener();
@@ -252,38 +253,50 @@ public class GeckoView extends LayerView
         // TODO: Convert custom attributes to GeckoViewSettings
         init(context, null);
     }
 
     public GeckoView(Context context, final GeckoViewSettings settings) {
         super(context);
 
         final GeckoViewSettings newSettings = new GeckoViewSettings(settings, getEventDispatcher());
+        init(context, settings);
+    }
+
+    public GeckoView(Context context, AttributeSet attrs, final GeckoViewSettings settings) {
+        super(context, attrs);
+
+        final GeckoViewSettings newSettings = new GeckoViewSettings(settings, getEventDispatcher());
         init(context, newSettings);
     }
 
-    private void init(final Context context, final GeckoViewSettings settings) {
+    public static final void preload(Context context) {
+        final GeckoProfile profile = GeckoProfile.get(
+            context.getApplicationContext());
+
         if (GeckoAppShell.getApplicationContext() == null) {
             GeckoAppShell.setApplicationContext(context.getApplicationContext());
         }
 
+        if (GeckoThread.initMainProcess(profile,
+                                        /* args */ null,
+                                        /* debugging */ false)) {
+            GeckoThread.launch();
+        }
+    }
+
+    private void init(final Context context, final GeckoViewSettings settings) {
         // Set the GeckoInterface if the context is an activity and the
         // GeckoInterface has not already been set
         if (context instanceof Activity && getGeckoInterface() == null) {
             setGeckoInterface(new BaseGeckoInterface(context));
             GeckoAppShell.setContextGetter(this);
         }
 
-        final GeckoProfile profile = GeckoProfile.get(
-            context.getApplicationContext());
-        if (GeckoThread.initMainProcess(profile,
-                                        /* args */ null,
-                                        /* debugging */ false)) {
-            GeckoThread.launch();
-        }
+        preload(context);
 
         // Perform common initialization for Fennec/GeckoView.
         GeckoAppShell.setLayerView(this);
 
         initializeView();
         mListener.registerListeners();
 
         if (settings == null) {
@@ -570,17 +583,17 @@ public class GeckoView extends LayerView
         return mProgressListener;
     }
 
     /**
     * Set the navigation callback handler.
     * This will replace the current handler.
     * @param navigation An implementation of NavigationListener.
     */
-    public void setNavigationDelegate(NavigationListener listener) {
+    public void setNavigationListener(NavigationListener listener) {
         if (mNavigationListener == listener) {
             return;
         }
         if (listener == null) {
             mEventDispatcher.dispatch("GeckoViewNavigation:Inactive", null);
         } else if (mNavigationListener == null) {
             mEventDispatcher.dispatch("GeckoViewNavigation:Active", null);
         }
@@ -1008,16 +1021,17 @@ public class GeckoView extends LayerView
     public EventDispatcher getEventDispatcher() {
         return mEventDispatcher;
     }
 
     public interface ProgressListener {
         static final int STATE_IS_BROKEN = 1;
         static final int STATE_IS_SECURE = 2;
         static final int STATE_IS_INSECURE = 4;
+        /* package */ final int STATE_ALL = STATE_IS_BROKEN | STATE_IS_SECURE | STATE_IS_INSECURE;
 
         /**
         * A View has started loading content from the network.
         * @param view The GeckoView that initiated the callback.
         * @param url The resource being loaded.
         */
         void onPageStart(GeckoView view, String url);
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -25,33 +25,33 @@ public class DynamicToolbarAnimator {
         DISABLED(0),
         RELAYOUT(1),
         ACTION_MODE(2),
         FULL_SCREEN(3),
         CARET_DRAG(4),
         PAGE_LOADING(5),
         CUSTOM_TAB(6);
 
-        public final int mValue;
-        PinReason(final int value) {
-            mValue = value;
+        public final int value;
+        PinReason(final int aValue) {
+            value = aValue;
         }
     }
 
     public interface MetricsListener {
         public void onMetricsChanged(ImmutableViewportMetrics viewport);
     }
 
     public interface ToolbarChromeProxy {
         public Bitmap getBitmapOfToolbarChrome();
         public boolean isToolbarChromeVisible();
         public void toggleToolbarChrome(boolean aShow);
     }
 
-    private final Set<PinReason> pinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
+    private final Set<PinReason> mPinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
 
     private final GeckoLayerClient mTarget;
     private LayerView.Compositor mCompositor;
     private final List<MetricsListener> mListeners;
     private ToolbarChromeProxy mToolbarChromeProxy;
     private int mMaxToolbarHeight;
     private boolean mCompositorControllerOpen;
 
@@ -98,32 +98,32 @@ public class DynamicToolbarAnimator {
         }
         return 0;
     }
 
     /**
      * If true, scroll changes will not affect translation.
      */
     public boolean isPinned() {
-        return !pinFlags.isEmpty();
+        return !mPinFlags.isEmpty();
     }
 
     public boolean isPinnedBy(PinReason reason) {
-        return pinFlags.contains(reason);
+        return mPinFlags.contains(reason);
     }
 
     public void setPinned(boolean pinned, PinReason reason) {
-        if ((mCompositor != null) && (pinned != pinFlags.contains(reason))) {
-             mCompositor.setPinned(pinned, reason.mValue);
+        if ((mCompositor != null) && (pinned != mPinFlags.contains(reason))) {
+             mCompositor.setPinned(pinned, reason.value);
         }
 
         if (pinned) {
-            pinFlags.add(reason);
+            mPinFlags.add(reason);
         } else {
-            pinFlags.remove(reason);
+            mPinFlags.remove(reason);
         }
     }
 
     public void showToolbar(boolean immediately) {
         if (mCompositor != null) {
             mCompositor.sendToolbarAnimatorMessage(immediately ?
                 LayerView.REQUEST_SHOW_TOOLBAR_IMMEDIATELY : LayerView.REQUEST_SHOW_TOOLBAR_ANIMATED);
         }
@@ -155,18 +155,18 @@ public class DynamicToolbarAnimator {
     private void dumpStateToCompositor() {
         if ((mCompositor != null) && mCompositorControllerOpen) {
             mCompositor.setMaxToolbarHeight(mMaxToolbarHeight);
             if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) {
                 mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_SHOW_TOOLBAR_IMMEDIATELY);
             } else {
                 mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_HIDE_TOOLBAR_IMMEDIATELY);
             }
-            for (PinReason reason : pinFlags) {
-              mCompositor.setPinned(true, reason.mValue);
+            for (PinReason reason : PinReason.values()) {
+              mCompositor.setPinned(mPinFlags.contains(reason), reason.value);
             }
         } else if ((mCompositor != null) && !mCompositorControllerOpen) {
             // Ask the UiCompositorControllerChild if it is open since the open message can
             // sometimes be sent to a different instance of the LayerView such as when
             // Fennec is being used in custom tabs.
             mCompositor.sendToolbarAnimatorMessage(LayerView.IS_COMPOSITOR_CONTROLLER_OPEN);
         }
     }
--- a/mozglue/misc/TimeStamp.cpp
+++ b/mozglue/misc/TimeStamp.cpp
@@ -42,19 +42,21 @@ struct TimeStampInitialization
   {
     TimeStamp::Shutdown();
   };
 };
 
 static TimeStampInitialization sInitOnce;
 
 MFBT_API TimeStamp
-TimeStamp::ProcessCreation(bool& aIsInconsistent)
+TimeStamp::ProcessCreation(bool* aIsInconsistent)
 {
-  aIsInconsistent = false;
+  if (aIsInconsistent) {
+    *aIsInconsistent = false;
+  }
 
   if (sInitOnce.mProcessCreation.IsNull()) {
     char* mozAppRestart = getenv("MOZ_APP_RESTART");
     TimeStamp ts;
 
     /* When calling PR_SetEnv() with an empty value the existing variable may
      * be unset or set to the empty string depending on the underlying platform
      * thus we have to check if the variable is present and not empty. */
@@ -67,17 +69,19 @@ TimeStamp::ProcessCreation(bool& aIsInco
       uint64_t uptime = ComputeProcessUptime();
 
       ts = now - TimeDuration::FromMicroseconds(uptime);
 
       if ((ts > sInitOnce.mFirstTimeStamp) || (uptime == 0)) {
         /* If the process creation timestamp was inconsistent replace it with
          * the first one instead and notify that a telemetry error was
          * detected. */
-        aIsInconsistent = true;
+        if (aIsInconsistent) {
+          *aIsInconsistent = true;
+        }
         ts = sInitOnce.mFirstTimeStamp;
       }
     }
 
     sInitOnce.mProcessCreation = ts;
   }
 
   return sInitOnce.mProcessCreation;
--- a/mozglue/misc/TimeStamp.h
+++ b/mozglue/misc/TimeStamp.h
@@ -469,22 +469,22 @@ public:
 
   /**
    * Return a timestamp representing the time when the current process was
    * created which will be comparable with other timestamps taken with this
    * class. If the actual process creation time is detected to be inconsistent
    * the @a aIsInconsistent parameter will be set to true, the returned
    * timestamp however will still be valid though inaccurate.
    *
-   * @param aIsInconsistent Set to true if an inconsistency was detected in the
-   * process creation time
+   * @param aIsInconsistent If non-null, set to true if an inconsistency was
+   * detected in the process creation time
    * @returns A timestamp representing the time when the process was created,
    * this timestamp is always valid even when errors are reported
    */
-  static MFBT_API TimeStamp ProcessCreation(bool& aIsInconsistent);
+  static MFBT_API TimeStamp ProcessCreation(bool* aIsInconsistent = nullptr);
 
   /**
    * Records a process restart. After this call ProcessCreation() will return
    * the time when the browser was restarted instead of the actual time when
    * the process was created.
    */
   static MFBT_API void RecordProcessRestart();
 
--- a/netwerk/base/nsUDPSocket.cpp
+++ b/netwerk/base/nsUDPSocket.cpp
@@ -26,16 +26,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsIPipe.h"
 #include "prerror.h"
 #include "nsThreadUtils.h"
 #include "nsIDNSRecord.h"
 #include "nsIDNSService.h"
 #include "nsICancelable.h"
+#include "nsWrapperCacheInlines.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "NetStatistics.h"
 #endif
 
 namespace mozilla {
 namespace net {
 
--- a/taskcluster/ci/android-stuff/kind.yml
+++ b/taskcluster/ci/android-stuff/kind.yml
@@ -166,17 +166,19 @@ jobs:
         optimizations:
           - - files-changed
             - - "mobile/android/**/*.java"
               - "mobile/android/**/*.jpeg"
               - "mobile/android/**/*.jpg"
               - "mobile/android/**/*.png"
               - "mobile/android/**/*.svg"
               - "mobile/android/**/*.xml" # Manifest & android resources
-              - "mobile/android/**/build.gradle"
+              - "mobile/android/**/*.gradle"
+              - "mobile/android/**/Makefile.in"
+              - "mobile/android/**/moz.build"
 
     android-checkstyle:
         description: "Android checkstyle"
         attributes:
             build_platform: android-checkstyle
             build_type: opt
         treeherder:
             platform: android-4-0-armv7-api15/opt
@@ -215,18 +217,20 @@ jobs:
               - "bin/build.sh"
             max-run-time: 36000
         scopes:
           - docker-worker:relengapi-proxy:tooltool.download.internal
           - docker-worker:relengapi-proxy:tooltool.download.public
         optimizations:
           - - files-changed
             - - "mobile/android/**/checkstyle.xml"
+              - "mobile/android/**/*.java"
               - "mobile/android/**/*.gradle"
-              - "mobile/android/**/*.java"
+              - "mobile/android/**/Makefile.in"
+              - "mobile/android/**/moz.build"
 
     android-findbugs:
         description: "Android findbugs"
         attributes:
             build_platform: android-findbugs
             build_type: opt
         treeherder:
             platform: android-4-0-armv7-api15/opt
@@ -264,10 +268,12 @@ jobs:
               - "/bin/bash"
               - "bin/build.sh"
             max-run-time: 36000
         scopes:
           - docker-worker:relengapi-proxy:tooltool.download.internal
           - docker-worker:relengapi-proxy:tooltool.download.public
         optimizations:
           - - files-changed
-            - - "mobile/android/**/*.gradle"
-              - "mobile/android/**/*.java"
+            - - "mobile/android/**/*.java"
+              - "mobile/android/**/*.gradle"
+              - "mobile/android/**/Makefile.in"
+              - "mobile/android/**/moz.build"
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1914,23 +1914,31 @@ or run without that action (ie: --no-{ac
 
         yield {
             'name': 'sccache hit rate',
             'value': hits,
             'extraOptions': self.perfherder_resource_options(),
             'subtests': [],
         }
 
-        for stat in ['cache_write_errors', 'requests_not_cacheable']:
-            yield {
-                'name': 'sccache %s' % stat,
-                'value': stats['stats'][stat],
-                'extraOptions': self.perfherder_resource_options(),
-                'subtests': [],
-            }
+        yield {
+            'name': 'sccache cache_write_errors',
+            'value': stats['stats']['cache_write_errors'],
+            'extraOptions': self.perfherder_resource_options(),
+            'alertThreshold': 50.0,
+            'subtests': [],
+        }
+
+        yield {
+            'name': 'sccache requests_not_cacheable',
+            'value': stats['stats']['requests_not_cacheable'],
+            'extraOptions': self.perfherder_resource_options(),
+            'alertThreshold': 50.0,
+            'subtests': [],
+        }
 
     def get_firefox_version(self):
         versionFilePath = os.path.join(
             self.query_abs_dirs()['abs_src_dir'], 'browser/config/version.txt')
         with open(versionFilePath, 'r') as versionFile:
             return versionFile.readline().strip()
 
     def generate_build_stats(self):
--- a/testing/specialpowers/content/MockFilePicker.jsm
+++ b/testing/specialpowers/content/MockFilePicker.jsm
@@ -67,16 +67,17 @@ this.MockFilePicker = {
       registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
     }
   },
 
   reset: function() {
     this.appendFilterCallback = null;
     this.appendFiltersCallback = null;
     this.displayDirectory = null;
+    this.displaySpecialDirectory = "";
     this.filterIndex = 0;
     this.mode = null;
     this.returnData = [];
     this.returnValue = null;
     this.showCallback = null;
     this.afterOpenCallback = null;
     this.shown = false;
     this.showing = false;
@@ -174,16 +175,17 @@ MockFilePickerInstance.prototype = {
     if (typeof MockFilePicker.appendFiltersCallback == "function")
       MockFilePicker.appendFiltersCallback(this, aFilterMask);
   },
   defaultString: "",
   defaultExtension: "",
   parent: null,
   filterIndex: 0,
   displayDirectory: null,
+  displaySpecialDirectory: "",
   get file() {
     if (MockFilePicker.returnData.length >= 1) {
       return MockFilePicker.returnData[0].nsIFile;
     }
 
     return null;
   },
 
@@ -264,16 +266,17 @@ MockFilePickerInstance.prototype = {
         // Nothing else has to be done.
         MockFilePicker.pendingPromises = [];
 
         if (result == Ci.nsIFilePicker.returnCancel) {
           return result;
         }
 
         MockFilePicker.displayDirectory = this.displayDirectory;
+        MockFilePicker.displaySpecialDirectory = this.displaySpecialDirectory;
         MockFilePicker.shown = true;
         if (typeof MockFilePicker.showCallback == "function") {
           try {
             var returnValue = MockFilePicker.showCallback(this);
             if (typeof returnValue != "undefined") {
               return returnValue;
             }
           } catch(ex) {
--- a/toolkit/components/filepicker/content/filepicker.js
+++ b/toolkit/components/filepicker/content/filepicker.js
@@ -42,16 +42,17 @@ function filepickerLoad() {
   if (window.arguments) {
     var o = window.arguments[0];
     retvals = o.retvals; /* set this to a global var so we can set return values */
     const title = o.title;
     filePickerMode = o.mode;
     if (o.displayDirectory) {
       var directory = o.displayDirectory.path;
     }
+    var specialDirectory = o.displaySpecialDirectory;
 
     const initialText = o.defaultString;
     var filterTitles = o.filters.titles;
     var filterTypes = o.filters.types;
     var numFilters = filterTitles.length;
 
     document.title = title;
     allowURLs = o.allowURLs;
@@ -128,31 +129,33 @@ function filepickerLoad() {
 
   // Start out with the ok button disabled since nothing will be
   // selected and nothing will be in the text field.
   okButton.disabled = filePickerMode != nsIFilePicker.modeGetFolder;
 
   // This allows the window to show onscreen before we begin
   // loading the file list
 
-  setTimeout(setInitialDirectory, 0, directory);
+  setTimeout(setInitialDirectory, 0, { directory, specialDirectory });
 }
 
-function setInitialDirectory(directory) {
+function setInitialDirectory(directories) {
   // Start in the user's home directory
   var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID]
                              .getService(nsIProperties);
-  homeDir = dirService.get("Home", Components.interfaces.nsIFile);
+  homeDir = dirService.get(directories.specialDirectory
+                             ? directories.specialDirectory : "Home",
+                           Components.interfaces.nsIFile);
 
-  if (directory) {
-    sfile.initWithPath(directory);
+  if (directories.directory) {
+    sfile.initWithPath(directories.directory);
     if (!sfile.exists() || !sfile.isDirectory())
-      directory = false;
+      directories.directory = false;
   }
-  if (!directory) {
+  if (!directories.directory) {
     sfile.initWithPath(homeDir.path);
   }
 
   gotoDirectory(sfile);
 }
 
 function onFilterChanged(target) {
   // Do this on a timeout callback so the filter list can roll up
--- a/toolkit/components/filepicker/nsFilePicker.js
+++ b/toolkit/components/filepicker/nsFilePicker.js
@@ -50,16 +50,17 @@ function nsFilePicker() {
     filterBundle = srGetStrBundle("chrome://global/content/filepicker.properties");
 
   /* attributes */
   this.mDefaultString = "";
   this.mFilterIndex = 0;
   this.mFilterTitles = new Array();
   this.mFilters = new Array();
   this.mDisplayDirectory = null;
+  this.mDisplaySpecialDirectory = null;
   if (lastDirectory) {
     try {
       var dir = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile);
       dir.initWithPath(lastDirectory);
       this.mDisplayDirectory = dir;
     } catch (e) {}
   }
 }
@@ -82,16 +83,24 @@ nsFilePicker.prototype = {
       a.clone().QueryInterface(nsILocalFile);
   },
   get displayDirectory() {
     return this.mDisplayDirectory &&
            this.mDisplayDirectory.clone()
                .QueryInterface(nsILocalFile);
   },
 
+  /* attribute AString displaySpecialDirectory; */
+  set displaySpecialDirectory(a) {
+    this.mDisplaySpecialDirectory = a;
+  },
+  get displaySpecialDirectory() {
+    return this.mDisplaySpecialDirectory;
+  },
+
   /* readonly attribute nsILocalFile file; */
   get file() { return this.mFilesEnumerator.mFiles[0]; },
 
   /* readonly attribute nsISimpleEnumerator files; */
   get files() { return this.mFilesEnumerator; },
 
   /* we don't support directories, yet */
   get domFileOrDirectory() {
@@ -251,16 +260,17 @@ nsFilePicker.prototype = {
     });
   },
 
   show() {
     var o = {};
     o.title = this.mTitle;
     o.mode = this.mMode;
     o.displayDirectory = this.mDisplayDirectory;
+    o.displaySpecialDirectory = this.mDisplaySpecialDirectory;
     o.defaultString = this.mDefaultString;
     o.filterIndex = this.mFilterIndex;
     o.filters = {};
     o.filters.titles = this.mFilterTitles;
     o.filters.types = this.mFilters;
     o.allowURLs = this.mAllowURLs;
     o.retvals = {};
 
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -753,17 +753,17 @@ nsAppStartup::GetStartupInfo(JSContext* 
 
   TimeStamp procTime = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION);
   TimeStamp now = TimeStamp::Now();
   PRTime absNow = PR_Now();
 
   if (procTime.IsNull()) {
     bool error = false;
 
-    procTime = TimeStamp::ProcessCreation(error);
+    procTime = TimeStamp::ProcessCreation(&error);
 
     if (error) {
       Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS,
         StartupTimeline::PROCESS_CREATION);
     }
 
     StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, procTime);
   }
--- a/toolkit/components/telemetry/TelemetryCommon.cpp
+++ b/toolkit/components/telemetry/TelemetryCommon.cpp
@@ -78,17 +78,17 @@ CanRecordInProcess(RecordedProcessType p
          ((processType != GeckoProcessType_Default) && recordAllChild);
 }
 
 nsresult
 MsSinceProcessStart(double* aResult)
 {
   bool error;
   *aResult = (TimeStamp::NowLoRes() -
-              TimeStamp::ProcessCreation(error)).ToMilliseconds();
+              TimeStamp::ProcessCreation(&error)).ToMilliseconds();
   if (error) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   return NS_OK;
 }
 
 void
 LogToBrowserConsole(uint32_t aLogLevel, const nsAString& aMsg)
--- a/toolkit/components/telemetry/TelemetryEvent.cpp
+++ b/toolkit/components/telemetry/TelemetryEvent.cpp
@@ -583,18 +583,17 @@ TelemetryEvent::RecordChildEvents(GeckoP
   MOZ_ASSERT(XRE_IsParentProcess());
   StaticMutexAutoLock locker(gTelemetryEventsMutex);
   for (uint32_t i = 0; i < aEvents.Length(); ++i) {
     const mozilla::Telemetry::ChildEventData e = aEvents[i];
 
     // Timestamps from child processes are absolute. We fix them up here to be
     // relative to the main process start time.
     // This allows us to put events from all processes on the same timeline.
-    bool inconsistent = false;
-    double relativeTimestamp = (e.timestamp - TimeStamp::ProcessCreation(inconsistent)).ToMilliseconds();
+    double relativeTimestamp = (e.timestamp - TimeStamp::ProcessCreation()).ToMilliseconds();
 
     ::RecordEvent(locker, aProcessType, relativeTimestamp, e.category, e.method, e.object, e.value, e.extra);
   }
   return NS_OK;
 }
 
 nsresult
 TelemetryEvent::RecordEvent(const nsACString& aCategory, const nsACString& aMethod,
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1036,19 +1036,18 @@ bool MinidumpCallback(
     XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10);
   }
   // write crash time to file
   if (lastCrashTimeFilename[0] != 0) {
     PlatformWriter lastCrashFile(lastCrashTimeFilename);
     WriteString(lastCrashFile, crashTimeString);
   }
 
-  bool ignored = false;
-  double uptimeTS = (TimeStamp::NowLoRes()-
-                     TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits();
+  double uptimeTS = (TimeStamp::NowLoRes() -
+                     TimeStamp::ProcessCreation()).ToSecondsSigDigits();
   char uptimeTSString[64];
   SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString));
 
   // Write crash event file.
 
   // Minidump IDs are UUIDs (36) + NULL.
   static char id_ascii[37];
 #ifdef XP_LINUX
@@ -3210,19 +3209,18 @@ WriteExtraData(nsIFile* extraFile,
     time_t crashTime = time(nullptr);
     char crashTimeString[32];
     XP_TTOA(crashTime, crashTimeString, 10);
 
     WriteAnnotation(fd,
                     nsDependentCString("CrashTime"),
                     nsDependentCString(crashTimeString));
 
-    bool ignored = false;
-    double uptimeTS = (TimeStamp::NowLoRes()-
-                       TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits();
+    double uptimeTS = (TimeStamp::NowLoRes() -
+                       TimeStamp::ProcessCreation()).ToSecondsSigDigits();
     char uptimeTSString[64];
     SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString));
 
     WriteAnnotation(fd,
                     nsDependentCString("UptimeTS"),
                     nsDependentCString(uptimeTSString));
   }
 
--- a/toolkit/library/gtest/rust/moz.build
+++ b/toolkit/library/gtest/rust/moz.build
@@ -1,23 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-features = []
-if CONFIG['MOZ_STYLO']:
-    features += ['servo']
-
-    if CONFIG['MOZ_STYLO_BINDGEN']:
-        features += ['bindgen']
+include('../../rust/gkrust-features.mozbuild')
 
-    if CONFIG['MOZ_DEBUG']:
-        features += ['gecko_debug']
-
-if CONFIG['MOZ_BUILD_WEBRENDER']:
-    features += ['quantum_render']
-
-if CONFIG['MOZ_PULSEAUDIO']:
-    features += ['cubeb_pulse_rust']
-
-RustLibrary('gkrust-gtest', features, '../..')
+RustLibrary('gkrust-gtest', gkrust_features, '../..')
new file mode 100644
--- /dev/null
+++ b/toolkit/library/rust/gkrust-features.mozbuild
@@ -0,0 +1,22 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gkrust_features = []
+if CONFIG['MOZ_STYLO']:
+    gkrust_features += ['servo']
+
+    if CONFIG['MOZ_STYLO_BINDGEN']:
+        gkrust_features += ['bindgen']
+
+    if CONFIG['MOZ_DEBUG']:
+        gkrust_features += ['gecko_debug']
+
+if CONFIG['MOZ_BUILD_WEBRENDER']:
+    gkrust_features += ['quantum_render']
+
+if CONFIG['MOZ_PULSEAUDIO']:
+    gkrust_features += ['cubeb_pulse_rust']
+
--- a/toolkit/library/rust/moz.build
+++ b/toolkit/library/rust/moz.build
@@ -1,23 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-features = []
-if CONFIG['MOZ_STYLO']:
-    features += ['servo']
-
-    if CONFIG['MOZ_STYLO_BINDGEN']:
-        features += ['bindgen']
+include('gkrust-features.mozbuild')
 
-    if CONFIG['MOZ_DEBUG']:
-        features += ['gecko_debug']
-
-if CONFIG['MOZ_BUILD_WEBRENDER']:
-    features += ['quantum_render']
-
-if CONFIG['MOZ_PULSEAUDIO']:
-    features += ['cubeb_pulse_rust']
-
-RustLibrary('gkrust', features, '..')
+RustLibrary('gkrust', gkrust_features, '..')
--- a/tools/memory-profiler/MemoryProfiler.cpp
+++ b/tools/memory-profiler/MemoryProfiler.cpp
@@ -90,18 +90,17 @@ MemoryProfiler::InitOnce()
   if (!initialized) {
     MallocHook::Initialize();
     sLock = PR_NewLock();
     sProfileContextCount = 0;
     sJSContextProfilerMap = new JSContextProfilerMap();
     ClearOnShutdown(&sJSContextProfilerMap);
     ClearOnShutdown(&sNativeProfiler);
     std::srand(PR_Now());
-    bool ignored;
-    sStartTime = TimeStamp::ProcessCreation(ignored);
+    sStartTime = TimeStamp::ProcessCreation();
     initialized = true;
   }
 }
 
 NS_IMETHODIMP
 MemoryProfiler::StartProfiler()
 {
   InitOnce();
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -468,18 +468,17 @@ AddDynamicCodeLocationTag(ProfileBuffer*
     aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
   }
 }
 
 static const int SAMPLER_MAX_STRING_LENGTH = 128;
 
 static void
 AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
-               volatile js::ProfileEntry& entry, PseudoStack* stack,
-               void* lastpc)
+               volatile js::ProfileEntry& entry, PseudoStack* stack)
 {
   // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations and
   // should not be recorded in the profile.
   if (entry.hasFlag(js::ProfileEntry::BEGIN_PSEUDO_JS)) {
     return;
   }
 
   int lineno = -1;
@@ -504,25 +503,16 @@ AddPseudoEntry(PSLockRef aLock, ProfileB
     // That will happen to the preceding tag.
     AddDynamicCodeLocationTag(aBuffer, sampleLabel);
     if (entry.isJs()) {
       JSScript* script = entry.script();
       if (script) {
         if (!entry.pc()) {
           // The JIT only allows the top-most entry to have a nullptr pc.
           MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
-
-          // If stack-walking was disabled, then that's just unfortunate.
-          if (lastpc) {
-            jsbytecode* jspc = js::ProfilingGetPC(stack->mContext, script,
-                                                  lastpc);
-            if (jspc) {
-              lineno = JS_PCToLineNumber(script, jspc);
-            }
-          }
         } else {
           lineno = JS_PCToLineNumber(script, entry.pc());
         }
       }
     } else {
       lineno = entry.line();
     }
   } else {
@@ -701,17 +691,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
                                jsStackAddr != nativeStackAddr);
     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                    nativeStackAddr != jsStackAddr);
 
     // Check to see if pseudoStack frame is top-most.
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
       volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex];
-      AddPseudoEntry(aLock, aBuffer, pseudoFrame, pseudoStack, nullptr);
+      AddPseudoEntry(aLock, aBuffer, pseudoFrame, pseudoStack);
       pseudoIndex++;
       continue;
     }
 
     // Check to see if JS jit stack frame is top-most
     if (jsStackAddr > nativeStackAddr) {
       MOZ_ASSERT(jsIndex >= 0);
       const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
@@ -1983,18 +1973,17 @@ profiler_init(void* aStackTop)
 
   {
     PSAutoLock lock(gPSMutex);
 
     // We've passed the possible failure point. Instantiate gPS, which
     // indicates that the profiler has initialized successfully.
     gPS = new PS();
 
-    bool ignore;
-    gPS->SetProcessStartTime(lock, mozilla::TimeStamp::ProcessCreation(ignore));
+    gPS->SetProcessStartTime(lock, mozilla::TimeStamp::ProcessCreation());
 
     locked_register_thread(lock, kMainThreadName, aStackTop);
 
     // Platform-specific initialization.
     PlatformInit(lock);
 
 #ifdef MOZ_TASK_TRACER
     mozilla::tasktracer::InitTaskTracer();
--- a/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -290,43 +290,81 @@ NS_IMETHODIMP nsBaseFilePicker::GetFiles
   files.AppendObject(file);
 
   return NS_NewArrayEnumerator(aFiles, files);
 }
 
 // Set the display directory
 NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory)
 {
+  // if displaySpecialDirectory has been previously called, let's abort this
+  // operation.
+  if (!mDisplaySpecialDirectory.IsEmpty()) {
+    return NS_OK;
+  }
+
   if (!aDirectory) {
     mDisplayDirectory = nullptr;
     return NS_OK;
   }
   nsCOMPtr<nsIFile> directory;
   nsresult rv = aDirectory->Clone(getter_AddRefs(directory));
   if (NS_FAILED(rv))
     return rv;
   mDisplayDirectory = do_QueryInterface(directory, &rv);
   return rv;
 }
 
 // Get the display directory
 NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory)
 {
   *aDirectory = nullptr;
+
+  // if displaySpecialDirectory has been previously called, let's abort this
+  // operation.
+  if (!mDisplaySpecialDirectory.IsEmpty()) {
+    return NS_OK;
+  }
+
   if (!mDisplayDirectory)
     return NS_OK;
   nsCOMPtr<nsIFile> directory;
   nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory));
   if (NS_FAILED(rv)) {
     return rv;
   }
   directory.forget(aDirectory);
   return NS_OK;
 }
 
+// Set the display special directory
+NS_IMETHODIMP nsBaseFilePicker::SetDisplaySpecialDirectory(const nsAString& aDirectory)
+{
+  // if displayDirectory has been previously called, let's abort this operation.
+  if (mDisplayDirectory && mDisplaySpecialDirectory.IsEmpty()) {
+    return NS_OK;
+  }
+
+  mDisplaySpecialDirectory = aDirectory;
+  if (mDisplaySpecialDirectory.IsEmpty()) {
+    mDisplayDirectory = nullptr;
+    return NS_OK;
+  }
+
+  return NS_GetSpecialDirectory(NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory).get(),
+                                getter_AddRefs(mDisplayDirectory));
+}
+
+// Get the display special directory
+NS_IMETHODIMP nsBaseFilePicker::GetDisplaySpecialDirectory(nsAString& aDirectory)
+{
+  aDirectory = mDisplaySpecialDirectory;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag)
 {
   *aFlag = mAddToRecentDocs;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/widget/nsBaseFilePicker.h
+++ b/widget/nsBaseFilePicker.h
@@ -29,30 +29,33 @@ public:
 
   NS_IMETHOD Open(nsIFilePickerShownCallback *aCallback);
   NS_IMETHOD AppendFilters(int32_t filterMask);
   NS_IMETHOD GetFilterIndex(int32_t *aFilterIndex);
   NS_IMETHOD SetFilterIndex(int32_t aFilterIndex);
   NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles);
   NS_IMETHOD GetDisplayDirectory(nsIFile * *aDisplayDirectory);
   NS_IMETHOD SetDisplayDirectory(nsIFile * aDisplayDirectory);
+  NS_IMETHOD GetDisplaySpecialDirectory(nsAString& aDisplayDirectory);
+  NS_IMETHOD SetDisplaySpecialDirectory(const nsAString& aDisplayDirectory);
   NS_IMETHOD GetAddToRecentDocs(bool *aFlag);
   NS_IMETHOD SetAddToRecentDocs(bool aFlag);
   NS_IMETHOD GetMode(int16_t *aMode);
   NS_IMETHOD SetOkButtonLabel(const nsAString& aLabel);
   NS_IMETHOD GetOkButtonLabel(nsAString& aLabel);
 
   NS_IMETHOD GetDomFileOrDirectory(nsISupports** aValue);
   NS_IMETHOD GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue);
 
 protected:
 
   virtual void InitNative(nsIWidget *aParent, const nsAString& aTitle) = 0;
 
   bool mAddToRecentDocs;
   nsCOMPtr<nsIFile> mDisplayDirectory;
+  nsString mDisplaySpecialDirectory;
 
   nsCOMPtr<nsPIDOMWindowOuter> mParent;
   int16_t mMode;
   nsString mOkButtonLabel;
 };
 
 #endif // nsBaseFilePicker_h__
--- a/widget/nsFilePickerProxy.cpp
+++ b/widget/nsFilePickerProxy.cpp
@@ -139,17 +139,18 @@ nsFilePickerProxy::Open(nsIFilePickerSho
     mDisplayDirectory->GetPath(displayDirectory);
   }
 
   if (!mIPCActive) {
     return NS_ERROR_FAILURE;
   }
 
   SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension,
-           mFilters, mFilterNames, displayDirectory, mOkButtonLabel);
+           mFilters, mFilterNames, displayDirectory, mDisplaySpecialDirectory,
+           mOkButtonLabel);
 
   return NS_OK;
 }
 
 mozilla::ipc::IPCResult
 nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
                                   const int16_t& aResult)
 {
--- a/widget/nsIFilePicker.idl
+++ b/widget/nsIFilePicker.idl
@@ -107,22 +107,35 @@ interface nsIFilePicker : nsISupports
   * The filter which is currently selected in the File Picker dialog
   *
   * @return Returns the index (0 based) of the selected filter in the filter list. 
   */
   attribute long filterIndex;
 
  /**
   * Set the directory that the file open/save dialog initially displays
+  * Note that, if displaySpecialDirectory has been already set, this value will
+  * be ignored.
   *
   * @param      displayDirectory  the name of the directory
   *
   */
   attribute nsIFile displayDirectory;
 
+ /**
+  * Set the directory that the file open/save dialog initially displays using
+  * one of the special name as such as 'Desk', 'TmpD', and so on.
+  * Note that, if displayDirectory has been already set, this value will be
+  * ignored.
+  *
+  * @param      displaySpecialDirectory  the name of the special directory
+  *
+  */
+  attribute AString displaySpecialDirectory;
+
 
  /**
   * Get the nsIFile for the file or directory.
   *
   * @return Returns the file currently selected
   */
   readonly attribute nsIFile file;
 
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1176,17 +1176,17 @@ CycleCollectedJSRuntime::GarbageCollect(
 
 void
 CycleCollectedJSRuntime::JSObjectsTenured()
 {
   MOZ_ASSERT(mJSContext);
 
   for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
     nsWrapperCache* cache = iter.Get();
-    JSObject* wrapper = cache->GetWrapperPreserveColor();
+    JSObject* wrapper = cache->GetWrapperMaybeDead();
     MOZ_DIAGNOSTIC_ASSERT(wrapper);
     if (!JS::ObjectIsTenured(wrapper)) {
       MOZ_ASSERT(!cache->PreservingWrapper());
       const JSClass* jsClass = js::GetObjectJSClass(wrapper);
       jsClass->doFinalize(nullptr, wrapper);
     }
   }
 
@@ -1200,18 +1200,18 @@ for (auto iter = mPreservedNurseryObject
   mPreservedNurseryObjects.Clear();
 }
 
 void
 CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
 {
   MOZ_ASSERT(mJSContext);
   MOZ_ASSERT(aCache);
-  MOZ_ASSERT(aCache->GetWrapperPreserveColor());
-  MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
+  MOZ_ASSERT(aCache->GetWrapperMaybeDead());
+  MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperMaybeDead()));
   mNurseryObjects.InfallibleAppend(aCache);
 }
 
 void
 CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper)
 {
   MOZ_ASSERT(mJSContext);
 
--- a/xpcom/base/nsDebug.h
+++ b/xpcom/base/nsDebug.h
@@ -356,25 +356,16 @@ inline void MOZ_PretendNoReturn()
   NS_ENSURE_FALSE(outer, NS_ERROR_NO_AGGREGATION)
 
 /*****************************************************************************/
 
 #if (defined(DEBUG) || (defined(NIGHTLY_BUILD) && !defined(MOZ_PROFILING))) && !defined(XPCOM_GLUE_AVOID_NSPR)
   #define MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED  1
 #endif
 
-#ifdef XPCOM_GLUE
-  #define NS_CheckThreadSafe(owningThread, msg)
-#else
-  #define NS_CheckThreadSafe(owningThread, msg)                 \
-    if (MOZ_UNLIKELY(owningThread != PR_GetCurrentThread())) {  \
-      MOZ_CRASH(msg);                                           \
-    }
-#endif
-
 #ifdef MOZILLA_INTERNAL_API
 void NS_ABORT_OOM(size_t aSize);
 #else
 inline void NS_ABORT_OOM(size_t)
 {
   MOZ_CRASH();
 }
 #endif
--- a/xpcom/base/nsISupportsImpl.cpp
+++ b/xpcom/base/nsISupportsImpl.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupportsImpl.h"
+#include "mozilla/Assertions.h"
 
 nsresult NS_FASTCALL
 NS_TableDrivenQI(void* aThis, REFNSIID aIID, void** aInstancePtr,
                  const QITableEntry* aEntries)
 {
   do {
     if (aIID.Equals(*aEntries->iid)) {
       nsISupports* r = reinterpret_cast<nsISupports*>(
@@ -20,8 +21,19 @@ NS_TableDrivenQI(void* aThis, REFNSIID a
     }
 
     ++aEntries;
   } while (aEntries->iid);
 
   *aInstancePtr = nullptr;
   return NS_ERROR_NO_INTERFACE;
 }
+
+#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
+void
+nsAutoOwningThread::AssertCurrentThreadOwnsMe(const char* msg) const
+{
+  if (MOZ_UNLIKELY(mThread != PR_GetCurrentThread())) {
+    // `msg` is a string literal by construction.
+    MOZ_CRASH_UNSAFE_OOL(msg);
+  }
+}
+#endif
--- a/xpcom/base/nsISupportsImpl.h
+++ b/xpcom/base/nsISupportsImpl.h
@@ -8,20 +8,19 @@
 
 #ifndef nsISupportsImpl_h__
 #define nsISupportsImpl_h__
 
 #include "nscore.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsUtils.h"
 
-
 #if !defined(XPCOM_GLUE_AVOID_NSPR)
-#include "prthread.h" /* needed for thread-safety checks */
-#endif // !XPCOM_GLUE_AVOID_NSPR
+#include "prthread.h" /* needed for cargo-culting headers */
+#endif
 
 #include "nsDebug.h"
 #include "nsXPCOM.h"
 #include <atomic>
 #include "mozilla/Attributes.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Compiler.h"
 #include "mozilla/Likely.h"
@@ -46,37 +45,52 @@ ToCanonicalSupports(nsISupports* aSuppor
   return nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Macros to help detect thread-safety:
 
 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
 
+#include "prthread.h" /* needed for thread-safety checks */
+
 class nsAutoOwningThread
 {
 public:
   nsAutoOwningThread() { mThread = PR_GetCurrentThread(); }
-  void* GetThread() const { return mThread; }
+
+  // We move the actual assertion checks out-of-line to minimize code bloat,
+  // but that means we have to pass a non-literal string to
+  // MOZ_CRASH_UNSAFE_OOL.  To make that more safe, the public interface
+  // requires a literal string and passes that to the private interface; we
+  // can then be assured that we effectively are passing a literal string
+  // to MOZ_CRASH_UNSAFE_OOL.
+  template<int N>
+  void AssertOwnership(const char (&aMsg)[N]) const
+  {
+    AssertCurrentThreadOwnsMe(aMsg);
+  }
 
 private:
+  void AssertCurrentThreadOwnsMe(const char* aMsg) const;
+
   void* mThread;
 };
 
 #define NS_DECL_OWNINGTHREAD            nsAutoOwningThread _mOwningThread;
 #define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) \
-  NS_CheckThreadSafe(agg->_mOwningThread.GetThread(), #_class " not thread-safe")
+  agg->_mOwningThread.AssertOwnership(#_class " not thread-safe")
 #define NS_ASSERT_OWNINGTHREAD(_class) NS_ASSERT_OWNINGTHREAD_AGGREGATE(this, _class)
-#else // !DEBUG && !(NIGHTLY_BUILD && !MOZ_PROFILING)
+#else // !MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
 
 #define NS_DECL_OWNINGTHREAD            /* nothing */
 #define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) ((void)0)
 #define NS_ASSERT_OWNINGTHREAD(_class)  ((void)0)
 
-#endif // DEBUG || (NIGHTLY_BUILD && !MOZ_PROFILING)
+#endif // MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
 
 
 // Macros for reference-count and constructor logging
 
 #if defined(NS_BUILD_REFCNT_LOGGING)
 
 #define NS_LOG_ADDREF(_p, _rc, _type, _size) \
   NS_LogAddRef((_p), (_rc), (_type), (uint32_t) (_size))
--- a/xpcom/base/nsWeakReference.cpp
+++ b/xpcom/base/nsWeakReference.cpp
@@ -11,19 +11,19 @@
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 
 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
 
 #define MOZ_WEAKREF_DECL_OWNINGTHREAD nsAutoOwningThread _mWeakRefOwningThread;
 #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD \
-  NS_CheckThreadSafe(_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe")
+  _mWeakRefOwningThread.AssertOwnership("nsWeakReference not thread-safe")
 #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) \
-  NS_CheckThreadSafe((that)->_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe")
+  (that)->_mWeakRefOwningThread.AssertOwnership("nsWeakReference not thread-safe")
 
 #else
 
 #define MOZ_WEAKREF_DECL_OWNINGTHREAD
 #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD do { } while (false)
 #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) do { } while (false)
 
 #endif
--- a/xpcom/threads/HangMonitor.cpp
+++ b/xpcom/threads/HangMonitor.cpp
@@ -178,17 +178,17 @@ GetChromeHangReport(Telemetry::Processed
   }
   aStack = Telemetry::GetStackAndModules(rawStack);
 
   // Record system uptime (in minutes) at the time of the hang
   aSystemUptime = ((GetTickCount() / 1000) - (gTimeout * 2)) / 60;
 
   // Record Firefox uptime (in minutes) at the time of the hang
   bool error;
-  TimeStamp processCreation = TimeStamp::ProcessCreation(error);
+  TimeStamp processCreation = TimeStamp::ProcessCreation(&error);
   if (!error) {
     TimeDuration td = TimeStamp::Now() - processCreation;
     aFirefoxUptime = (static_cast<int32_t>(td.ToSeconds()) - (gTimeout * 2)) / 60;
   } else {
     aFirefoxUptime = -1;
   }
 }