Merge mozilla-central to mozilla-inbound
authorDorel Luca <dluca@mozilla.com>
Tue, 21 Aug 2018 12:59:51 +0300
changeset 487673 88803cf0dec1cffe6990cc48705d18591aaa9e2b
parent 487672 1d748613485fee744aa70641100664334118fa23 (current diff)
parent 487644 a955df76e2b636889a1c37a7863739d6306520eb (diff)
child 487674 973b58266e4437e2cb14cd47d4cf8f052f4a1fa4
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
editor/libeditor/HTMLEditor.cpp
editor/nsIHTMLEditor.idl
toolkit/components/extensions/parent/ext-theme.js
toolkit/components/extensions/test/browser/browser.ini
toolkit/components/normandy/lib/Addons.jsm
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -6,13 +6,13 @@
         "NathanRidley.autotrim",
         // JS Babel ES6/ES7 syntax hilight.
         "dzannotti.vscode-babel-coloring",
         // ESLint support.
         "dbaeumer.vscode-eslint",
         // C/C++ language support.
         "ms-vscode.cpptools",
         // Rust language support.
-        "kalitaalexey.vscode-rust",
+        "rust-lang.rust",
         // CSS support for HTML documents.
         "ecmel.vscode-html-css"
     ]
 }
--- a/browser/components/extensions/extension.css
+++ b/browser/components/extensions/extension.css
@@ -317,18 +317,18 @@ textarea.browser-style.disabled {
 
 .browser-style > input[type="text"].focused,
 textarea.browser-style.focused {
   border-color: #0996f8;
   box-shadow: 0 0 0 2px rgba(97, 181, 255, 0.75);
 }
 
 /* Interactive States */
-.browser-style > input[type="text"]:not(disabled):hover,
-textarea.browser-style:not(disabled):hover {
+.browser-style > input[type="text"]:not(.disabled):hover,
+textarea.browser-style:not(.disabled):hover {
   border: 1px solid #858585;
 }
 
 .browser-style > input[type="text"]:focus,
 .browser-style > input[type="text"]:focus:hover,
 textarea.browser-style:focus,
 textarea.browser-style:focus:hover {
   border-color: #0996f8;
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1076,17 +1076,17 @@ nsHTMLDocument::CreateDummyChannelForCoo
     if (isTracking) {
       // If our document channel is from a tracking resource, we must
       // override our channel's tracking status.
       nsCOMPtr<nsIHttpChannel> httpChannel =
         do_QueryInterface(channel);
       MOZ_ASSERT(httpChannel, "How come we're coming from an HTTP doc but "
                               "we don't have an HTTP channel here?");
       if (httpChannel) {
-        httpChannel->OverrideTrackingResource(isTracking);
+        httpChannel->OverrideTrackingFlagsForDocumentCookieAccessor(docHTTPChannel);
       }
     }
   }
 
   return channel.forget();
 }
 
 void
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5335,28 +5335,16 @@ mozilla::ipc::IPCResult
 ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
                                                            const IPC::Principal& aPrincipal)
 {
   PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult
-ContentParent::RecvNotifyLowMemory()
-{
-  MarkAsTroubled();
-
-  Telemetry::ScalarAdd(Telemetry::ScalarID::DOM_CONTENTPROCESS_TROUBLED_DUE_TO_MEMORY, 1);
-
-  nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport);
-
-  return IPC_OK();
-}
-
 /* static */ void
 ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
                                             BlobImpl* aBlobImpl,
                                             nsIPrincipal* aPrincipal,
                                             ContentParent* aIgnoreThisCP)
 {
   nsCString uri(aURI);
   IPC::Principal principal(aPrincipal);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1217,18 +1217,16 @@ public:
                                                                   InfallibleTArray<uint8_t>&& aData) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope,
                                                                             const IPC::Principal& aPrincipal) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
                                                                               const IPC::Principal& aPrincipal) override;
 
-  virtual mozilla::ipc::IPCResult RecvNotifyLowMemory() override;
-
   virtual mozilla::ipc::IPCResult RecvGetFilesRequest(const nsID& aID,
                                                       const nsString& aDirectoryPath,
                                                       const bool& aRecursiveFlag) override;
 
   virtual mozilla::ipc::IPCResult RecvDeleteGetFilesRequest(const nsID& aID) override;
 
   virtual mozilla::ipc::IPCResult
   RecvFileCreationRequest(const nsID& aID, const nsString& aFullPath,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1118,23 +1118,16 @@ parent:
                                       nsString messageId, uint8_t[] data);
 
     /**
      * Notify `push-subscription-change` observers in the parent.
      */
     async NotifyPushSubscriptionChangeObservers(nsCString scope,
                                                 Principal principal);
 
-    /**
-     * Tell the parent process that the child process is low on memory. This
-     * allows the parent process to save a memory report that can potentially be
-     * sent with a crash report from the content process.
-     */
-     async NotifyLowMemory();
-
      async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
      async DeleteGetFilesRequest(nsID aID);
 
      async FileCreationRequest(nsID aID, nsString aFullPath, nsString aType,
                                nsString aName, bool lastModifiedPassed,
                                int64_t lastModified, bool aExistenceCheck,
                                bool aIsFromNsIFile);
 
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -3157,17 +3157,17 @@ ScriptLoader::PrepareLoadedRequest(Scrip
     }
 
     nsAutoCString sourceMapURL;
     if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
       aRequest->mHasSourceMapURL = true;
       aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL);
     }
 
-    if (httpChannel->GetIsTrackingResource()) {
+    if (httpChannel->GetIsThirdPartyTrackingResource()) {
       aRequest->SetIsTracking();
     }
   }
 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
   // If this load was subject to a CORS check, don't flag it with a separate
   // origin principal, so that it will treat our document's principal as the
   // origin principal.  Module loads always use CORS.
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -2202,17 +2202,17 @@ HTMLEditor::MakeOrChangeList(const nsASt
       return error.StealNSResult();
     }
   }
 
   return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 NS_IMETHODIMP
-HTMLEditor::RemoveList(const nsAString& aListType)
+HTMLEditor::RemoveList(const nsAString&)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
@@ -2223,21 +2223,16 @@ HTMLEditor::RemoveList(const nsAString& 
                                       *this, EditSubAction::eRemoveList,
                                       nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   EditSubActionInfo subActionInfo(EditSubAction::eRemoveList);
-  if (aListType.LowerCaseEqualsLiteral("ol")) {
-    subActionInfo.bOrdered = true;
-  } else {
-    subActionInfo.bOrdered = false;
-  }
   nsresult rv =
     rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   // no default behavior for this yet.  what would it mean?
 
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -519,17 +519,16 @@ public:
     : mEditSubAction(aEditSubAction)
     , inString(nullptr)
     , outString(nullptr)
     , outputFormat(nullptr)
     , maxLength(-1)
     , flags(0)
     , collapsedAction(nsIEditor::eNext)
     , stripWrappers(nsIEditor::eStrip)
-    , bOrdered(false)
     , entireList(false)
     , bulletType(nullptr)
     , alignType(nullptr)
     , blockType(nullptr)
   {}
 
   EditSubAction mEditSubAction;
 
@@ -541,19 +540,16 @@ public:
 
   // EditSubAction::eComputeTextToOutput
   uint32_t flags;
 
   // EditSubAction::eDeleteSelectedContent
   nsIEditor::EDirection collapsedAction;
   nsIEditor::EStripWrappers stripWrappers;
 
-  // EditSubAction::eRemoveList
-  bool bOrdered;
-
   // EditSubAction::eCreateOrChangeList
   bool entireList;
   const nsAString* bulletType;
 
   // EditSubAction::eSetOrClearAlignment
   const nsAString* alignType;
 
   // EditSubAction::eCreateOrRemoveBlock
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -279,18 +279,20 @@ interface nsIHTMLEditor : nsISupports
   /**
    * Document me!
    *
    */
   void makeOrChangeList(in AString aListType, in boolean entireList,
                         in AString aBulletType);
 
   /**
-   * Document me!
+   * removeList removes list items (<li>, <dd>, and <dt>) and list structures
+   * (<ul>, <ol>, and <dl>).
    *
+   * @param aListType  Unused.
    */
   void removeList(in AString aListType);
 
   /**
    * Document me!
    *
    */
   void indent(in AString aIndent);
--- a/intl/encoding_glue/src/lib.rs
+++ b/intl/encoding_glue/src/lib.rs
@@ -15,46 +15,42 @@ extern crate encoding_rs;
 extern crate nserror;
 extern crate nsstring;
 
 use encoding_rs::*;
 use nserror::*;
 use nsstring::*;
 use std::slice;
 
-// nsStringBuffer's internal bookkeeping takes 8 bytes from
-// the allocation. Plus one for termination.
-const NS_CSTRING_OVERHEAD: usize = 9;
-
 /// Takes `Option<usize>`, the destination string and a value
-/// to return on failure and tries to set the length of the
-/// destination string to the `usize` wrapped in the first
-/// argument.
-macro_rules! try_dst_set_len {
+/// to return on failure and tries to start a bulk write of the
+/// destination string with the capacity given by the `usize`
+/// wrapped in the first argument. Returns the bulk write
+/// handle.
+macro_rules! try_start_bulk_write {
     ($needed:expr,
      $dst:ident,
-     $ret:expr) => (
-    let needed = match $needed {
-        Some(max) => {
-            // XPCOM strings use uint32_t for length.
-            if max > ::std::u32::MAX as usize {
+     $ret:expr) => ({
+        let needed = match $needed {
+            Some(needed) => {
+                needed
+            }
+            None => {
                 return $ret;
             }
-            max as u32
-        }
-        None => {
-            return $ret;
+        };
+        match unsafe { $dst.bulk_write(needed, 0, false) } {
+            Err(_) => {
+                return $ret;
+            },
+            Ok(handle) => {
+                handle
+            }
         }
-    };
-    unsafe {
-        if $dst.fallible_set_length(needed).is_err() {
-            return $ret;
-        }
-    }
-     )
+    })
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mozilla_encoding_decode_to_nsstring(encoding: *mut *const Encoding,
                                                              src: *const u8,
                                                              src_len: usize,
                                                              dst: *mut nsAString)
                                                              -> nsresult {
@@ -100,29 +96,24 @@ pub unsafe extern "C" fn mozilla_encodin
                                             &mut *dst)
 }
 
 pub fn decode_to_nsstring_without_bom_handling(encoding: &'static Encoding,
                                                src: &[u8],
                                                dst: &mut nsAString)
                                                -> nsresult {
     let mut decoder = encoding.new_decoder_without_bom_handling();
-    try_dst_set_len!(decoder.max_utf16_buffer_length(src.len()),
-                     dst,
-                     NS_ERROR_OUT_OF_MEMORY);
-    // to_mut() shouldn't fail right after setting length.
-    let (result, read, written, had_errors) = decoder.decode_to_utf16(src, dst.to_mut(), true);
+    let mut handle = try_start_bulk_write!(decoder.max_utf16_buffer_length(src.len()),
+                                           dst,
+                                           NS_ERROR_OUT_OF_MEMORY);
+    let (result, read, written, had_errors) = decoder.decode_to_utf16(src, handle.as_mut_slice(), true);
     debug_assert_eq!(result, CoderResult::InputEmpty);
     debug_assert_eq!(read, src.len());
-    debug_assert!(written <= dst.len());
-    unsafe {
-        if dst.fallible_set_length(written as u32).is_err() {
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
-    }
+    debug_assert!(written <= handle.as_mut_slice().len());
+    let _ = handle.finish(written, true);
     if had_errors {
         return NS_OK_HAD_REPLACEMENTS;
     }
     NS_OK
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mozilla_encoding_decode_to_nsstring_without_bom_handling_and_without_replacement
@@ -134,35 +125,30 @@ pub unsafe extern "C" fn mozilla_encodin
     decode_to_nsstring_without_bom_handling_and_without_replacement(&*encoding,
                                                                     slice::from_raw_parts(src,
                                                                                           src_len),
                                                                     &mut *dst)
 }
 
 pub fn decode_to_nsstring_without_bom_handling_and_without_replacement(encoding: &'static Encoding, src: &[u8], dst: &mut nsAString) -> nsresult{
     let mut decoder = encoding.new_decoder_without_bom_handling();
-    try_dst_set_len!(decoder.max_utf16_buffer_length(src.len()),
-                     dst,
-                     NS_ERROR_OUT_OF_MEMORY);
-    // to_mut() shouldn't fail right after setting length.
+    let mut handle = try_start_bulk_write!(decoder.max_utf16_buffer_length(src.len()),
+                                           dst,
+                                           NS_ERROR_OUT_OF_MEMORY);
     let (result, read, written) = decoder
-        .decode_to_utf16_without_replacement(src, dst.to_mut(), true);
+        .decode_to_utf16_without_replacement(src, handle.as_mut_slice(), true);
     match result {
         DecoderResult::InputEmpty => {
             debug_assert_eq!(read, src.len());
-            debug_assert!(written <= dst.len());
-            unsafe {
-                if dst.fallible_set_length(written as u32).is_err() {
-                    return NS_ERROR_OUT_OF_MEMORY;
-                }
-            }
+            debug_assert!(written <= handle.as_mut_slice().len());
+            let _ = handle.finish(written, true);
             NS_OK
         }
         DecoderResult::Malformed(_, _) => {
-            dst.truncate();
+            // Let handle's drop() run
             NS_ERROR_UDEC_ILLEGALINPUT
         }
         DecoderResult::OutputFull => unreachable!(),
     }
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mozilla_encoding_encode_from_utf16(encoding: *mut *const Encoding,
@@ -176,65 +162,48 @@ pub unsafe extern "C" fn mozilla_encodin
 }
 
 pub fn encode_from_utf16(encoding: &'static Encoding,
                          src: &[u16],
                          dst: &mut nsACString)
                          -> (nsresult, &'static Encoding) {
     let output_encoding = encoding.output_encoding();
     let mut encoder = output_encoding.new_encoder();
-    try_dst_set_len!(encoder.max_buffer_length_from_utf16_if_no_unmappables(src.len()),
-                     dst,
-                     (NS_ERROR_OUT_OF_MEMORY, output_encoding));
+    let mut handle = try_start_bulk_write!(encoder.max_buffer_length_from_utf16_if_no_unmappables(src.len()),
+                                           dst,
+                                           (NS_ERROR_OUT_OF_MEMORY, output_encoding));
 
     let mut total_read = 0;
     let mut total_written = 0;
     let mut total_had_errors = false;
     loop {
         let (result, read, written, had_errors) = encoder
             .encode_from_utf16(&src[total_read..],
-                               &mut (dst.to_mut())[total_written..],
+                               &mut (handle.as_mut_slice())[total_written..],
                                true);
         total_read += read;
         total_written += written;
         total_had_errors |= had_errors;
         match result {
             CoderResult::InputEmpty => {
                 debug_assert_eq!(total_read, src.len());
-                debug_assert!(total_written <= dst.len());
-                unsafe {
-                    if dst.fallible_set_length(total_written as u32).is_err() {
-                        return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
-                    }
-                }
+                debug_assert!(total_written <= handle.as_mut_slice().len());
+                let _ = handle.finish(total_written, true);
                 if total_had_errors {
                     return (NS_OK_HAD_REPLACEMENTS, output_encoding);
                 }
                 return (NS_OK, output_encoding);
             }
             CoderResult::OutputFull => {
                 if let Some(needed) =
                     checked_add(total_written,
                                 encoder.max_buffer_length_from_utf16_if_no_unmappables(src.len() -
                                                                                        total_read)) {
-                    // Let's round the allocation up in order to avoid repeated
-                    // allocations. Using power-of-two as the approximation of
-                    // available jemalloc buckets, since linking with
-                    // malloc_good_size is messy.
-                    if let Some(with_bookkeeping) = NS_CSTRING_OVERHEAD.checked_add(needed) {
-                        let rounded = with_bookkeeping.next_power_of_two();
-                        let unclowned = rounded - NS_CSTRING_OVERHEAD;
-                        // XPCOM strings use uint32_t for length.
-                        if unclowned <= ::std::u32::MAX as usize {
-                            unsafe {
-                                if dst.fallible_set_length(unclowned as u32).is_ok() {
-                                    continue;
-                                }
-                            }
-                        }
+                    if unsafe { handle.restart_bulk_write(needed, total_written, false).is_ok() } {
+                        continue;
                     }
                 }
                 return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
             }
         }
     }
 }
 
@@ -320,63 +289,55 @@ pub unsafe extern "C" fn mozilla_encodin
 
 fn decode_from_slice_to_nscstring_without_bom_handling(encoding: &'static Encoding,
                                                        src: &[u8],
                                                        dst: &mut nsACString,
                                                        already_validated: usize)
                                                        -> nsresult {
     let bytes = src;
     let mut decoder = encoding.new_decoder_without_bom_handling();
-    let rounded_without_replacement =
-        checked_next_power_of_two(checked_add(already_validated, decoder.max_utf8_buffer_length_without_replacement(bytes.len() - already_validated)));
-    let with_replacement = checked_add(already_validated,
-                                       decoder.max_utf8_buffer_length(bytes.len() -
-                                                                      already_validated));
-    try_dst_set_len!(checked_min(rounded_without_replacement, with_replacement),
-                     dst,
-                     NS_ERROR_OUT_OF_MEMORY);
+    let mut handle = try_start_bulk_write!(Some(src.len()),
+                                           dst,
+                                           NS_ERROR_OUT_OF_MEMORY);
 
     if already_validated != 0 {
-        // to_mut() shouldn't fail right after setting length.
-        &mut (dst.to_mut())[..already_validated].copy_from_slice(&bytes[..already_validated]);
+        &mut (handle.as_mut_slice())[..already_validated].copy_from_slice(&bytes[..already_validated]);
     }
     let mut total_read = already_validated;
     let mut total_written = already_validated;
     let mut total_had_errors = false;
     loop {
-        // to_mut() shouldn't fail right after setting length.
         let (result, read, written, had_errors) =
             decoder.decode_to_utf8(&bytes[total_read..],
-                                   &mut (dst.to_mut())[total_written..],
+                                   &mut (handle.as_mut_slice())[total_written..],
                                    true);
         total_read += read;
         total_written += written;
         total_had_errors |= had_errors;
         match result {
             CoderResult::InputEmpty => {
                 debug_assert_eq!(total_read, bytes.len());
-                unsafe {
-                    if dst.fallible_set_length(total_written as u32).is_err() {
-                        return NS_ERROR_OUT_OF_MEMORY;
-                    }
-                }
+                let _ = handle.finish(total_written, true);
                 if total_had_errors {
                     return NS_OK_HAD_REPLACEMENTS;
                 }
                 return NS_OK;
             }
             CoderResult::OutputFull => {
                 // Allocate for the worst case. That is, we should come
                 // here at most once per invocation of this method.
-                try_dst_set_len!(checked_add(total_written,
-                                             decoder.max_utf8_buffer_length(bytes.len() -
-                                                                            total_read)),
-                                 dst,
-                                 NS_ERROR_OUT_OF_MEMORY);
-                continue;
+                if let Some(needed) =
+                    checked_add(total_written,
+                                decoder.max_utf8_buffer_length(bytes.len() -
+                                                               total_read)) {
+                    if unsafe { handle.restart_bulk_write(needed, total_written, false).is_ok() } {
+                        continue;
+                    }
+                }
+                return NS_ERROR_OUT_OF_MEMORY;
             }
         }
     }
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mozilla_encoding_decode_to_nscstring_without_bom_handling_and_without_replacement
     (encoding: *const Encoding,
@@ -407,42 +368,36 @@ pub fn decode_to_nscstring_without_bom_h
     };
     if valid_up_to == bytes.len() {
         if dst.fallible_assign(src).is_err() {
             return NS_ERROR_OUT_OF_MEMORY;
         }
         return NS_OK;
     }
     let mut decoder = encoding.new_decoder_without_bom_handling();
-    try_dst_set_len!(checked_add(valid_up_to,
-                                 decoder.max_utf8_buffer_length_without_replacement(bytes.len() -
-                                                                                    valid_up_to)),
-                     dst,
-                     NS_ERROR_OUT_OF_MEMORY);
-    // to_mut() shouldn't fail right after setting length.
+    let mut handle = try_start_bulk_write!(checked_add(valid_up_to,
+                                                       decoder.max_utf8_buffer_length_without_replacement(bytes.len() -
+                                                                                                          valid_up_to)),
+                                           dst,
+                                           NS_ERROR_OUT_OF_MEMORY);
     let (result, read, written) = {
-        let dest = dst.to_mut();
+        let dest = handle.as_mut_slice();
         dest[..valid_up_to].copy_from_slice(&bytes[..valid_up_to]);
         decoder
             .decode_to_utf8_without_replacement(&src[valid_up_to..], &mut dest[valid_up_to..], true)
     };
     match result {
         DecoderResult::InputEmpty => {
             debug_assert_eq!(valid_up_to + read, src.len());
-            debug_assert!(valid_up_to + written <= dst.len());
-            unsafe {
-                if dst.fallible_set_length((valid_up_to + written) as u32)
-                       .is_err() {
-                    return NS_ERROR_OUT_OF_MEMORY;
-                }
-            }
+            debug_assert!(valid_up_to + written <= handle.as_mut_slice().len());
+            let _ = handle.finish(valid_up_to + written, true);
             NS_OK
         }
         DecoderResult::Malformed(_, _) => {
-            dst.truncate();
+            // let handle's drop() run
             NS_ERROR_UDEC_ILLEGALINPUT
         }
         DecoderResult::OutputFull => unreachable!(),
     }
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn mozilla_encoding_encode_from_nscstring(encoding: *mut *const Encoding,
@@ -487,73 +442,58 @@ pub fn encode_from_nscstring(encoding: &
     // to avoid unsafe blocks.
     let trail = if let Ok(trail) = ::std::str::from_utf8(&bytes[valid_up_to..]) {
         trail
     } else {
         return (NS_ERROR_UDEC_ILLEGALINPUT, output_encoding);
     };
 
     let mut encoder = output_encoding.new_encoder();
-    try_dst_set_len!(checked_add(valid_up_to,
-                    encoder.max_buffer_length_from_utf8_if_no_unmappables(trail.len())), dst, (NS_ERROR_OUT_OF_MEMORY, output_encoding));
+    let mut handle = try_start_bulk_write!(checked_add(valid_up_to,
+                                                       encoder.max_buffer_length_from_utf8_if_no_unmappables(trail.len())),
+                                           dst,
+                                           (NS_ERROR_OUT_OF_MEMORY, output_encoding));
 
     if valid_up_to != 0 {
         // to_mut() shouldn't fail right after setting length.
-        &mut (dst.to_mut())[..valid_up_to].copy_from_slice(&bytes[..valid_up_to]);
+        &mut (handle.as_mut_slice())[..valid_up_to].copy_from_slice(&bytes[..valid_up_to]);
     }
 
     // `total_read` tracks `trail` only but `total_written` tracks the overall situation!
     // This asymmetry is here, because trail is materialized as `str` without resorting
     // to unsafe code here.
     let mut total_read = 0;
     let mut total_written = valid_up_to;
     let mut total_had_errors = false;
     loop {
         let (result, read, written, had_errors) = encoder
             .encode_from_utf8(&trail[total_read..],
-                              &mut (dst.to_mut())[total_written..],
+                              &mut (handle.as_mut_slice())[total_written..],
                               true);
         total_read += read;
         total_written += written;
         total_had_errors |= had_errors;
         match result {
             CoderResult::InputEmpty => {
                 debug_assert_eq!(valid_up_to + total_read, src.len());
-                debug_assert!(total_written <= dst.len());
-                unsafe {
-                    if dst.fallible_set_length(total_written as u32).is_err() {
-                        return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
-                    }
-                }
+                debug_assert!(total_written <= handle.as_mut_slice().len());
+                let _ = handle.finish(total_written, true);
                 if total_had_errors {
                     return (NS_OK_HAD_REPLACEMENTS, output_encoding);
                 }
                 return (NS_OK, output_encoding);
             }
             CoderResult::OutputFull => {
                 if let Some(needed) =
                     checked_add(total_written,
                                 encoder
                                     .max_buffer_length_from_utf8_if_no_unmappables(trail.len() -
                                                                                    total_read)) {
-                    // Let's round the allocation up in order to avoid repeated
-                    // allocations. Using power-of-two as the approximation of
-                    // available jemalloc buckets, since linking with
-                    // malloc_good_size is messy.
-                    if let Some(with_bookkeeping) = NS_CSTRING_OVERHEAD.checked_add(needed) {
-                        let rounded = with_bookkeeping.next_power_of_two();
-                        let unclowned = rounded - NS_CSTRING_OVERHEAD;
-                        // XPCOM strings use uint32_t for length.
-                        if unclowned <= ::std::u32::MAX as usize {
-                            unsafe {
-                                if dst.fallible_set_length(unclowned as u32).is_ok() {
-                                    continue;
-                                }
-                            }
-                        }
+                    if unsafe { handle.restart_bulk_write(needed, total_written, false).is_ok() } {
+                        continue;
                     }
                 }
                 return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
             }
         }
     }
 }
 
@@ -561,34 +501,16 @@ pub fn encode_from_nscstring(encoding: &
 fn checked_add(num: usize, opt: Option<usize>) -> Option<usize> {
     if let Some(n) = opt {
         n.checked_add(num)
     } else {
         None
     }
 }
 
-#[inline(always)]
-fn checked_next_power_of_two(opt: Option<usize>) -> Option<usize> {
-    opt.map(|n| n.next_power_of_two())
-}
-
-#[inline(always)]
-fn checked_min(one: Option<usize>, other: Option<usize>) -> Option<usize> {
-    if let Some(a) = one {
-        if let Some(b) = other {
-            Some(::std::cmp::min(a, b))
-        } else {
-            Some(a)
-        }
-    } else {
-        other
-    }
-}
-
 // Bindings for encoding_rs::mem. These may move to a separate crate in the future.
 
 #[no_mangle]
 pub unsafe extern "C" fn encoding_mem_is_utf16_bidi(buffer: *const u16, len: usize) -> bool {
     encoding_rs::mem::is_utf16_bidi(::std::slice::from_raw_parts(buffer, len))
 }
 
 #[no_mangle]
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -76,16 +76,20 @@ class ContentParent;
 } // namespace dom
 
 namespace net {
 class NeckoParent;
 } // namespace net
 
 namespace ipc {
 
+#ifdef FUZZING
+class ProtocolFuzzerHelper;
+#endif
+
 class MessageChannel;
 
 #ifdef XP_WIN
 const base::ProcessHandle kInvalidProcessHandle = INVALID_HANDLE_VALUE;
 
 // In theory, on Windows, this is a valid process ID, but in practice they are
 // currently divisible by four. Process IDs share the kernel handle allocation
 // code and they are guaranteed to be divisible by four.
@@ -135,16 +139,20 @@ enum RacyInterruptPolicy {
     RIPParentWins
 };
 
 
 class IToplevelProtocol;
 
 class IProtocol : public HasResultCodes
 {
+#ifdef FUZZING
+  friend class mozilla::ipc::ProtocolFuzzerHelper;
+#endif
+
 public:
     enum ActorDestroyReason {
         FailedConstructor,
         Deletion,
         AncestorDeletion,
         NormalShutdown,
         AbnormalShutdown
     };
@@ -415,16 +423,20 @@ public:
         eUnstarted,
         ePending,
         eReady,
         eError
     };
 
     class ToplevelState final : public ProtocolState
     {
+#ifdef FUZZING
+      friend class mozilla::ipc::ProtocolFuzzerHelper;
+#endif
+
     public:
         ToplevelState(const char* aName, IToplevelProtocol* aProtocol, Side aSide);
 
         Shmem::SharedMemory* CreateSharedMemory(
             size_t, SharedMemory::SharedMemoryType, bool, int32_t*) override;
         Shmem::SharedMemory* LookupSharedMemory(int32_t) override;
         bool IsTrackingSharedMemory(Shmem::SharedMemory*) override;
         bool DestroySharedMemory(Shmem&) override;
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -2919,18 +2919,25 @@ ContainerState::FindOpaqueBackgroundColo
   *aOutIntersectsLayer = true;
 
   // Scan the candidate's display items.
   nsIntRect deviceRect = aRect;
   nsRect appUnitRect = ToAppUnits(deviceRect, mAppUnitsPerDevPixel);
   appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale);
 
   for (auto& assignedItem : Reversed(aData->mAssignedDisplayItems)) {
-    if (assignedItem.mType != DisplayItemEntryType::ITEM) {
-      // |assignedItem| is an effect marker.
+    if (assignedItem.mHasOpacity || assignedItem.mHasTransform) {
+      // We cannot easily calculate the opaque background color for items inside
+      // a flattened effect.
+      continue;
+    }
+
+    if (IsEffectEndMarker(assignedItem.mType)) {
+      // An optimization: the underlying display item for effect markers is the
+      // same for both start and end markers. Skip the effect end markers.
       continue;
     }
 
     nsDisplayItem* item = assignedItem.mItem;
     bool snap;
     nsRect bounds = item->GetBounds(mBuilder, &snap);
     if (snap && mSnappingEnabled) {
       nsIntRect snappedBounds = ScaleToNearestPixels(bounds);
@@ -2956,22 +2963,21 @@ ContainerState::FindOpaqueBackgroundColo
 
     if (item->GetClip().IsRectAffectedByClip(deviceRect,
                                              mParameters.mXScale,
                                              mParameters.mYScale,
                                              mAppUnitsPerDevPixel)) {
       return NS_RGBA(0,0,0,0);
     }
 
-    if (!assignedItem.mHasOpacity && !assignedItem.mHasTransform) {
-      Maybe<nscolor> color = item->IsUniform(mBuilder);
-
-      if (color && NS_GET_A(*color) == 255) {
-        return *color;
-      }
+    MOZ_ASSERT(!assignedItem.mHasOpacity && !assignedItem.mHasTransform);
+    Maybe<nscolor> color = item->IsUniform(mBuilder);
+
+    if (color && NS_GET_A(*color) == 255) {
+      return *color;
     }
 
     return NS_RGBA(0,0,0,0);
   }
 
   *aOutIntersectsLayer = false;
   return NS_RGBA(0,0,0,0);
 }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1483649-1-ref.xul
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!DOCTYPE window>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<style type="text/css" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
+#container {
+  position: relative;
+  left: 500px;
+  display: inline;
+}
+
+#first {
+  position: relative;
+  top: 0px;
+  left: 0px;
+  width: 100px;
+  height: 100px;
+  transform: scaleX(-1);
+  background: red;
+}
+
+#second {
+  position: relative;
+  top: 50px;
+  left: -100px;
+  width: 100px;
+  height: 100px;
+  overflow: hidden;
+}
+
+#transformed {
+  width: 50px;
+  height: 50px;
+  transform: scaleX(-1)
+}
+]]></style>
+
+<box id="container">
+  <box id="first"></box>
+  <box id="second">
+    <image id="transformed"/>
+  </box>
+</box>
+</window>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1483649-1.xul
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!DOCTYPE window>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<style type="text/css" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
+#container {
+  position: relative;
+  left: 500px;
+  display: inline;
+}
+
+#first {
+  position: relative;
+  top: 0px;
+  left: 0px;
+  width: 100px;
+  height: 100px;
+  transform: scaleX(-1);
+  background: red;
+}
+
+#second {
+  position: relative;
+  top: 50px;
+  left: -100px;
+  width: 100px;
+  height: 100px;
+  overflow: hidden;
+}
+
+@keyframes transform-animation {
+  from {
+    transform: scaleX(-1);
+  }
+  to {
+    transform: scaleX(-1);
+  }
+}
+
+#transformed {
+  width: 50px;
+  height: 50px;
+  animation-name: transform-animation;
+  animation-fill-mode: forwards;
+}
+
+]]></style>
+
+<box id="container">
+  <box id="first"></box>
+  <box id="second">
+    <image id="transformed"/>
+  </box>
+</box>
+</window>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2075,8 +2075,9 @@ fuzzy-if(Android,0-66,0-574) fuzzy-if(d2
 pref(layout.css.moz-document.url-prefix-hack.enabled,true) == 1446470.html 1035091-ref.html
 pref(layout.css.moz-document.url-prefix-hack.enabled,false) == 1446470-2.html 1035091-ref.html
 test-pref(layout.css.prefixes.gradients,false) == 1451874.html 1451874-ref.html
 == 1456111-1.html about:blank
 test-pref(layout.css.contain.enabled,false) == 1466008.html 1466008-ref.html
 fuzzy(0-1,0-625) == 1466638-1.html 1466638-1-ref.html
 == bug1472465-1.html bug1472465-1-ref.html
 == 1475971-1.html 1475971-1-ref.html
+== 1483649-1.xul 1483649-1-ref.xul
--- a/netwerk/base/SimpleChannelParent.cpp
+++ b/netwerk/base/SimpleChannelParent.cpp
@@ -34,17 +34,17 @@ SimpleChannelParent::SetParentListener(H
 NS_IMETHODIMP
 SimpleChannelParent::NotifyTrackingProtectionDisabled()
 {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SimpleChannelParent::NotifyTrackingResource()
+SimpleChannelParent::NotifyTrackingResource(bool aIsThirdParty)
 {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SimpleChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                               const nsACString& aProvider,
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -208,32 +208,79 @@ CachedPrefs::~CachedPrefs()
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_SKIP_HOSTNAMES, this);
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_ANNOTATION_TABLE, this);
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_ANNOTATION_WHITELIST, this);
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST, this);
   Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_TABLE, this);
 }
 } // anonymous namespace
 
+static nsresult
+IsThirdParty(nsIChannel* aChannel, bool* aResult)
+{
+  NS_ENSURE_ARG(aResult);
+  *aResult = false;
+
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
+  if (NS_WARN_IF(!thirdPartyUtil)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
+  if (NS_FAILED(rv) || !chan) {
+    LOG(("nsChannelClassifier: Not an HTTP channel"));
+    return NS_OK;
+  }
+  nsCOMPtr<nsIURI> chanURI;
+  rv = aChannel->GetURI(getter_AddRefs(chanURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> topWinURI;
+  rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (!topWinURI) {
+    LOG(("nsChannelClassifier: No window URI\n"));
+  }
+
+  // Third party checks don't work for chrome:// URIs in mochitests, so just
+  // default to isThirdParty = true. We check isThirdPartyWindow to expand
+  // the list of domains that are considered first party (e.g., if
+  // facebook.com includes an iframe from fatratgames.com, all subsources
+  // included in that iframe are considered third-party with
+  // isThirdPartyChannel, even if they are not third-party w.r.t.
+  // facebook.com), and isThirdPartyChannel to prevent top-level navigations
+  // from being detected as third-party.
+  bool isThirdPartyChannel = true;
+  bool isThirdPartyWindow = true;
+  thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow);
+  thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel);
+
+  *aResult = isThirdPartyWindow && isThirdPartyChannel;
+  return NS_OK;
+}
+
 static void
-SetIsTrackingResourceHelper(nsIChannel* aChannel)
+SetIsTrackingResourceHelper(nsIChannel* aChannel, bool aIsThirdParty)
 {
   MOZ_ASSERT(aChannel);
 
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(aChannel, parentChannel);
   if (parentChannel) {
     // This channel is a parent-process proxy for a child process
     // request. We should notify the child process as well.
-    parentChannel->NotifyTrackingResource();
+    parentChannel->NotifyTrackingResource(aIsThirdParty);
   }
 
   RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
   if (httpChannel) {
-    httpChannel->SetIsTrackingResource();
+    httpChannel->SetIsTrackingResource(aIsThirdParty);
   }
 }
 
 static void
 LowerPriorityHelper(nsIChannel* aChannel)
 {
   MOZ_ASSERT(aChannel);
 
@@ -361,58 +408,36 @@ nsChannelClassifier::ShouldEnableTrackin
                                                          bool *result)
 {
     // Should only be called in the parent process.
     MOZ_ASSERT(XRE_IsParentProcess());
 
     NS_ENSURE_ARG(result);
     *result = false;
 
-    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-      services::GetThirdPartyUtil();
-    if (NS_WARN_IF(!thirdPartyUtil)) {
-      return NS_ERROR_FAILURE;
-    }
-
     nsresult rv;
     nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
     if (NS_FAILED(rv) || !chan) {
       LOG(("nsChannelClassifier[%p]: Not an HTTP channel", this));
       return NS_OK;
     }
 
-    nsCOMPtr<nsIURI> topWinURI;
-    rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    if (!topWinURI) {
-      LOG(("nsChannelClassifier[%p]: No window URI\n", this));
-    }
-
     nsCOMPtr<nsIURI> chanURI;
     rv = aChannel->GetURI(getter_AddRefs(chanURI));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Only perform third-party checks for tracking protection
     if (!aAnnotationsOnly) {
-      // Third party checks don't work for chrome:// URIs in mochitests, so just
-      // default to isThirdParty = true. We check isThirdPartyWindow to expand
-      // the list of domains that are considered first party (e.g., if
-      // facebook.com includes an iframe from fatratgames.com, all subsources
-      // included in that iframe are considered third-party with
-      // isThirdPartyChannel, even if they are not third-party w.r.t.
-      // facebook.com), and isThirdPartyChannel to prevent top-level navigations
-      // from being detected as third-party.
-      bool isThirdPartyChannel = true;
-      bool isThirdPartyWindow = true;
-      thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow);
-      thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel);
-      if (!isThirdPartyWindow || !isThirdPartyChannel) {
+      bool isThirdParty = false;
+      rv = IsThirdParty(aChannel, &isThirdParty);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        LOG(("nsChannelClassifier[%p]: IsThirdParty() failed", this));
+        return NS_OK;
+      }
+      if (!isThirdParty) {
         *result = false;
         if (LOG_ENABLED()) {
           nsCString spec = chanURI->GetSpecOrDefault();
           spec.Truncate(std::min(spec.Length(), sMaxSpecLength));
           LOG(("nsChannelClassifier[%p]: Skipping tracking protection checks "
                "for first party or top-level load channel[%p] with uri %s",
                this, aChannel, spec.get()));
         }
@@ -422,16 +447,22 @@ nsChannelClassifier::ShouldEnableTrackin
 
     if (AddonMayLoad(aChannel, chanURI)) {
         return NS_OK;
     }
 
     nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    nsCOMPtr<nsIURI> topWinURI;
+    rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
     if (!topWinURI && CachedPrefs::GetInstance()->IsAllowListExample()) {
       LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this));
       rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"),
                        nullptr, nullptr, getter_AddRefs(topWinURI));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Take the host/port portion so we can allowlist by site. Also ignore the
@@ -1189,45 +1220,49 @@ TrackingURICallback::OnWhitelistResult(n
   mChannelCallback();
   return NS_OK;
 }
 
 void
 TrackingURICallback::OnTrackerFound(nsresult aErrorCode)
 {
   nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
+  MOZ_ASSERT(channel);
   if (aErrorCode == NS_ERROR_TRACKING_URI &&
       mChannelClassifier->ShouldEnableTrackingProtection()) {
     mChannelClassifier->SetBlockedContent(channel, aErrorCode,
                                           mList, mProvider, mFullHash);
     LOG(("TrackingURICallback[%p]::OnTrackerFound, cancelling channel[%p]",
          mChannelClassifier.get(), channel.get()));
     channel->Cancel(aErrorCode);
   } else {
     MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_ANNOTATION_URI);
     MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation());
 
+    bool isThirdPartyWithTopLevelWinURI = false;
+    nsresult rv = IsThirdParty(channel, &isThirdPartyWithTopLevelWinURI);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      LOG(("TrackingURICallback[%p]::OnTrackerFound IsThirdParty() failed",
+           mChannelClassifier.get()));
+      return; // we'll assume the channel is NOT third-party
+    }
+
     LOG(("TrackingURICallback[%p]::OnTrackerFound, annotating channel[%p]",
          mChannelClassifier.get(), channel.get()));
 
-    // Even with TP disabled, we still want to show the user that there
-    // are unblocked trackers on the site, so notify the UI that we loaded
-    // tracking content. UI code can treat this notification differently
-    // depending on whether TP is enabled or disabled.
-    mChannelClassifier->NotifyTrackingProtectionDisabled(channel);
+    SetIsTrackingResourceHelper(channel, isThirdPartyWithTopLevelWinURI);
 
-    SetIsTrackingResourceHelper(channel);
-    if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
-      nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-        services::GetThirdPartyUtil();
-      bool result = false;
-      if (thirdPartyUtil &&
-          NS_SUCCEEDED(thirdPartyUtil->IsThirdPartyChannel(channel, nullptr,
-                                                           &result)) &&
-          result) {
+    if (isThirdPartyWithTopLevelWinURI) {
+      // Even with TP disabled, we still want to show the user that there
+      // are unblocked trackers on the site, so notify the UI that we loaded
+      // tracking content. UI code can treat this notification differently
+      // depending on whether TP is enabled or disabled.
+      mChannelClassifier->NotifyTrackingProtectionDisabled(channel);
+
+      if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
         LowerPriorityHelper(channel);
       }
     }
   }
 }
 
 } // end of unnamed namespace/
 
--- a/netwerk/base/nsIParentChannel.idl
+++ b/netwerk/base/nsIParentChannel.idl
@@ -45,16 +45,19 @@ interface nsIParentChannel : nsIStreamLi
    */
   [noscript] void setClassifierMatchedInfo(in ACString aList,
                                            in ACString aProvider,
                                            in ACString aFullHash);
 
   /**
    * Called to notify the HttpChannelChild that the resource being loaded
    * is on the tracking protection list.
+   * @param aIsThirdParty
+   *        Whether or not the resourced is considered first-party
+   *        with the URI of the window.
    */
-  [noscript] void notifyTrackingResource();
+  [noscript] void notifyTrackingResource(in bool aIsThirdParty);
 
   /**
    * Called to invoke deletion of the IPC protocol.
    */
   void delete();
 };
--- a/netwerk/protocol/data/DataChannelParent.cpp
+++ b/netwerk/protocol/data/DataChannelParent.cpp
@@ -34,17 +34,17 @@ DataChannelParent::SetParentListener(Htt
 NS_IMETHODIMP
 DataChannelParent::NotifyTrackingProtectionDisabled()
 {
     // Nothing to do.
     return NS_OK;
 }
 
 NS_IMETHODIMP
-DataChannelParent::NotifyTrackingResource()
+DataChannelParent::NotifyTrackingResource(bool aIsThirdParty)
 {
     // Nothing to do.
     return NS_OK;
 }
 
 NS_IMETHODIMP
 DataChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                             const nsACString& aProvider,
--- a/netwerk/protocol/file/FileChannelParent.cpp
+++ b/netwerk/protocol/file/FileChannelParent.cpp
@@ -34,17 +34,17 @@ FileChannelParent::SetParentListener(Htt
 NS_IMETHODIMP
 FileChannelParent::NotifyTrackingProtectionDisabled()
 {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FileChannelParent::NotifyTrackingResource()
+FileChannelParent::NotifyTrackingResource(bool aIsThirdParty)
 {
   // Nothing to do.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                             const nsACString& aProvider,
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -572,17 +572,17 @@ FTPChannelParent::SetParentListener(Http
 NS_IMETHODIMP
 FTPChannelParent::NotifyTrackingProtectionDisabled()
 {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FTPChannelParent::NotifyTrackingResource()
+FTPChannelParent::NotifyTrackingResource(bool aIsThirdParty)
 {
   // One day, this should probably be filled in.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FTPChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
                                            const nsACString& aProvider,
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp
@@ -343,28 +343,29 @@ HttpBackgroundChannelChild::RecvNotifyTr
   // NotifyTrackingProtectionDisabled has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
   mChannelChild->ProcessNotifyTrackingProtectionDisabled();
 
   return IPC_OK();
 }
 
 IPCResult
-HttpBackgroundChannelChild::RecvNotifyTrackingResource()
+HttpBackgroundChannelChild::RecvNotifyTrackingResource(const bool& aIsThirdParty)
 {
-  LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource [this=%p]\n", this));
+  LOG(("HttpBackgroundChannelChild::RecvNotifyTrackingResource thirdparty=%d "
+       "[this=%p]\n", static_cast<int>(aIsThirdParty), this));
   MOZ_ASSERT(OnSocketThread());
 
   if (NS_WARN_IF(!mChannelChild)) {
     return IPC_OK();
   }
 
   // NotifyTrackingResource has no order dependency to OnStartRequest.
   // It this be handled as soon as possible
-  mChannelChild->ProcessNotifyTrackingResource();
+  mChannelChild->ProcessNotifyTrackingResource(aIsThirdParty);
 
   return IPC_OK();
 }
 
 IPCResult
 HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(const ClassifierInfo& info)
 {
   LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n", this));
--- a/netwerk/protocol/http/HttpBackgroundChannelChild.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelChild.h
@@ -60,17 +60,17 @@ protected:
   IPCResult RecvFlushedForDiversion() override;
 
   IPCResult RecvDivertMessages() override;
 
   IPCResult RecvOnStartRequestSent() override;
 
   IPCResult RecvNotifyTrackingProtectionDisabled() override;
 
-  IPCResult RecvNotifyTrackingResource() override;
+  IPCResult RecvNotifyTrackingResource(const bool& aIsThirdParty) override;
 
   IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& info) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   virtual ~HttpBackgroundChannelChild();
 
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -375,40 +375,42 @@ HttpBackgroundChannelParent::OnNotifyTra
 
     return NS_SUCCEEDED(rv);
   }
 
   return SendNotifyTrackingProtectionDisabled();
 }
 
 bool
-HttpBackgroundChannelParent::OnNotifyTrackingResource()
+HttpBackgroundChannelParent::OnNotifyTrackingResource(bool aIsThirdParty)
 {
-  LOG(("HttpBackgroundChannelParent::OnNotifyTrackingResource [this=%p]\n", this));
+  LOG(("HttpBackgroundChannelParent::OnNotifyTrackingResource thirdparty=%d "
+       "[this=%p]\n", static_cast<int>(aIsThirdParty), this));
   AssertIsInMainProcess();
 
   if (NS_WARN_IF(!mIPCOpened)) {
     return false;
   }
 
   if (!IsOnBackgroundThread()) {
     MutexAutoLock lock(mBgThreadMutex);
     nsresult rv = mBackgroundThread->Dispatch(
-      NewRunnableMethod(
+      NewRunnableMethod<bool>(
         "net::HttpBackgroundChannelParent::OnNotifyTrackingResource",
         this,
-        &HttpBackgroundChannelParent::OnNotifyTrackingResource),
+        &HttpBackgroundChannelParent::OnNotifyTrackingResource,
+        aIsThirdParty),
       NS_DISPATCH_NORMAL);
 
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
     return NS_SUCCEEDED(rv);
   }
 
-  return SendNotifyTrackingResource();
+  return SendNotifyTrackingResource(aIsThirdParty);
 }
 
 bool
 HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(const nsACString& aList,
                                                         const nsACString& aProvider,
                                                         const nsACString& aFullHash)
 {
   LOG(("HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n", this));
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -65,17 +65,17 @@ public:
   // To send FlushedForDiversion and DivertMessages messages
   // over background channel.
   bool OnDiversion();
 
   // To send NotifyTrackingProtectionDisabled message over background channel.
   bool OnNotifyTrackingProtectionDisabled();
 
   // To send NotifyTrackingResource message over background channel.
-  bool OnNotifyTrackingResource();
+  bool OnNotifyTrackingResource(bool aIsThirdParty);
 
   // To send SetClassifierMatchedInfo message over background channel.
   bool OnSetClassifierMatchedInfo(const nsACString& aList,
                                   const nsACString& aProvider,
                                   const nsACString& aFullHash);
 
 protected:
   void ActorDestroy(ActorDestroyReason aWhy) override;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -166,17 +166,18 @@ HttpBaseChannel::HttpBaseChannel()
   , mRequestContextID(0)
   , mContentWindowId(0)
   , mTopLevelOuterContentWindowId(0)
   , mAltDataLength(0)
   , mChannelId(0)
   , mReqContentLength(0U)
   , mStatus(NS_OK)
   , mCanceled(false)
-  , mIsTrackingResource(false)
+  , mIsFirstPartyTrackingResource(false)
+  , mIsThirdPartyTrackingResource(false)
   , mLoadFlags(LOAD_NORMAL)
   , mCaps(0)
   , mClassOfService(0)
   , mUpgradeToSecure(false)
   , mApplyConversion(true)
   , mIsPending(false)
   , mWasOpened(false)
   , mRequestObserversCalled(false)
@@ -313,20 +314,28 @@ HttpBaseChannel::ReleaseMainThreadOnlyRe
     nsCOMPtr<nsISupports> nonTailRemover(new NonTailRemover(mRequestContext));
     arrayToRelease.AppendElement(nonTailRemover.forget());
   }
 
   NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
 }
 
 void
-HttpBaseChannel::SetIsTrackingResource()
-{
-  LOG(("HttpBaseChannel::SetIsTrackingResource %p", this));
-  mIsTrackingResource = true;
+HttpBaseChannel::SetIsTrackingResource(bool aIsThirdParty)
+{
+  LOG(("HttpBaseChannel::SetIsTrackingResource thirdparty=%d %p",
+       static_cast<int>(aIsThirdParty), this));
+
+  if (aIsThirdParty) {
+    MOZ_ASSERT(!mIsFirstPartyTrackingResource);
+    mIsThirdPartyTrackingResource = true;
+  } else {
+    MOZ_ASSERT(!mIsThirdPartyTrackingResource);
+    mIsFirstPartyTrackingResource = true;
+  }
 }
 
 nsresult
 HttpBaseChannel::Init(nsIURI *aURI,
                       uint32_t aCaps,
                       nsProxyInfo *aProxyInfo,
                       uint32_t aProxyResolveFlags,
                       nsIURI *aProxyURI,
@@ -1547,28 +1556,49 @@ NS_IMETHODIMP HttpBaseChannel::SetTopLev
 {
   mContentWindowId = aWindowId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetIsTrackingResource(bool* aIsTrackingResource)
 {
-  *aIsTrackingResource = mIsTrackingResource;
+  MOZ_ASSERT(!(mIsFirstPartyTrackingResource && mIsThirdPartyTrackingResource));
+  *aIsTrackingResource =
+    mIsThirdPartyTrackingResource || mIsFirstPartyTrackingResource;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetIsThirdPartyTrackingResource(bool* aIsTrackingResource)
+{
+  MOZ_ASSERT(!(mIsFirstPartyTrackingResource && mIsThirdPartyTrackingResource));
+  *aIsTrackingResource = mIsThirdPartyTrackingResource;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpBaseChannel::OverrideTrackingResource(bool aIsTracking)
-{
-  LOG(("HttpBaseChannel::OverrideTrackingResource(%d) %p "
-       "mIsTrackingResource=%d",
-      (int) aIsTracking, this, (int) mIsTrackingResource));
-
-  mIsTrackingResource = aIsTracking;
+HttpBaseChannel::OverrideTrackingFlagsForDocumentCookieAccessor(nsIHttpChannel* aDocumentChannel)
+{
+  LOG(("HttpBaseChannel::OverrideTrackingFlagsForDocumentCookieAccessor() %p "
+       "mIsFirstPartyTrackingResource=%d  mIsThirdPartyTrackingResource=%d",
+       this, static_cast<int>(mIsFirstPartyTrackingResource),
+       static_cast<int>(mIsThirdPartyTrackingResource)));
+
+  // The semantics we'd like to achieve here are that document.cookie
+  // should follow the same rules that the document is subject to with
+  // regards to content blocking. Therefore we need to propagate the
+  // same flags from the document channel to the fake channel here.
+  if (aDocumentChannel->GetIsThirdPartyTrackingResource()) {
+    mIsThirdPartyTrackingResource = true;
+  } else {
+    mIsFirstPartyTrackingResource = true;
+  }
+
+  MOZ_ASSERT(!(mIsFirstPartyTrackingResource && mIsThirdPartyTrackingResource));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetTransferSize(uint64_t *aTransferSize)
 {
   *aTransferSize = mTransferSize;
   return NS_OK;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -206,17 +206,18 @@ public:
   NS_IMETHOD GetProtocolVersion(nsACString & aProtocolVersion) override;
   NS_IMETHOD GetChannelId(uint64_t *aChannelId) override;
   NS_IMETHOD SetChannelId(uint64_t aChannelId) override;
   NS_IMETHOD GetTopLevelContentWindowId(uint64_t *aContentWindowId) override;
   NS_IMETHOD SetTopLevelContentWindowId(uint64_t aContentWindowId) override;
   NS_IMETHOD GetTopLevelOuterContentWindowId(uint64_t *aWindowId) override;
   NS_IMETHOD SetTopLevelOuterContentWindowId(uint64_t aWindowId) override;
   NS_IMETHOD GetIsTrackingResource(bool* aIsTrackingResource) override;
-  NS_IMETHOD OverrideTrackingResource(bool aIsTracking) override;
+  NS_IMETHOD GetIsThirdPartyTrackingResource(bool* aIsTrackingResource) override;
+  NS_IMETHOD OverrideTrackingFlagsForDocumentCookieAccessor(nsIHttpChannel* aDocumentChannel) override;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) override;
   NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) override;
   NS_IMETHOD GetRequestVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD GetResponseVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD SetCookie(const char *aCookieHeader) override;
   NS_IMETHOD GetThirdPartyFlags(uint32_t *aForce) override;
@@ -373,17 +374,17 @@ public: /* Necko internal use only... */
     DoApplyContentConversions(nsIStreamListener *aNextListener,
                               nsIStreamListener **aNewNextListener);
 
     // Callback on STS thread called by CopyComplete when NS_AsyncCopy()
     // is finished. This function works as a proxy function to dispatch
     // |EnsureUploadStreamIsCloneableComplete| to main thread.
     virtual void OnCopyComplete(nsresult aStatus);
 
-    void SetIsTrackingResource();
+    void SetIsTrackingResource(bool aIsThirdParty);
 
     const uint64_t& ChannelId() const
     {
       return mChannelId;
     }
 
     void InternalSetUploadStream(nsIInputStream *uploadStream)
     {
@@ -620,17 +621,18 @@ protected:
   uint64_t mChannelId;
   uint64_t mReqContentLength;
 
   Atomic<nsresult, ReleaseAcquire>  mStatus;
 
   // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
   // is canceled on main thread.
   Atomic<bool, ReleaseAcquire> mCanceled;
-  Atomic<bool, ReleaseAcquire> mIsTrackingResource;
+  Atomic<bool, ReleaseAcquire> mIsFirstPartyTrackingResource;
+  Atomic<bool, ReleaseAcquire> mIsThirdPartyTrackingResource;
 
   uint32_t                          mLoadFlags;
   uint32_t                          mCaps;
   uint32_t                          mClassOfService;
 
   uint32_t                          mUpgradeToSecure            : 1;
   uint32_t                          mApplyConversion            : 1;
   uint32_t                          mIsPending                  : 1;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2035,22 +2035,23 @@ HttpChannelChild::ProcessNotifyTrackingP
       "nsChannelClassifier::NotifyTrackingProtectionDisabled",
       [self]() {
         nsChannelClassifier::NotifyTrackingProtectionDisabled(self);
       }),
     NS_DISPATCH_NORMAL);
 }
 
 void
-HttpChannelChild::ProcessNotifyTrackingResource()
+HttpChannelChild::ProcessNotifyTrackingResource(bool aIsThirdParty)
 {
-  LOG(("HttpChannelChild::ProcessNotifyTrackingResource [this=%p]\n", this));
+  LOG(("HttpChannelChild::ProcessNotifyTrackingResource thirdparty=%d "
+       "[this=%p]\n", static_cast<int>(aIsThirdParty), this));
   MOZ_ASSERT(OnSocketThread());
 
-  SetIsTrackingResource();
+  SetIsTrackingResource(aIsThirdParty);
 }
 
 void
 HttpChannelChild::FlushedForDiversion()
 {
   LOG(("HttpChannelChild::FlushedForDiversion [this=%p]\n", this));
   MOZ_RELEASE_ASSERT(mDivertingToParent);
 
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -248,17 +248,17 @@ private:
   void ProcessOnStopRequest(const nsresult& aStatusCode,
                             const ResourceTimingStruct& aTiming,
                             const nsHttpHeaderArray& aResponseTrailers);
   void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
   void ProcessOnStatus(const nsresult& aStatus);
   void ProcessFlushedForDiversion();
   void ProcessDivertMessages();
   void ProcessNotifyTrackingProtectionDisabled();
-  void ProcessNotifyTrackingResource();
+  void ProcessNotifyTrackingResource(bool aIsThirdParty);
   void ProcessSetClassifierMatchedInfo(const nsCString& aList,
                                        const nsCString& aProvider,
                                        const nsCString& aFullHash);
 
   // Return true if we need to tell the parent the size of unreported received
   // data
   bool NeedToReportBytesRead();
   int32_t mUnreportBytesRead = 0;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1819,22 +1819,23 @@ HttpChannelParent::SetClassifierMatchedI
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
     Unused << mBgParent->OnSetClassifierMatchedInfo(aList, aProvider, aFullHash);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpChannelParent::NotifyTrackingResource()
+HttpChannelParent::NotifyTrackingResource(bool aIsThirdParty)
 {
-  LOG(("HttpChannelParent::NotifyTrackingResource [this=%p]\n", this));
+  LOG(("HttpChannelParent::NotifyTrackingResource thirdparty=%d [this=%p]\n",
+       static_cast<int>(aIsThirdParty), this));
   if (!mIPCClosed) {
     MOZ_ASSERT(mBgParent);
-    Unused << mBgParent->OnNotifyTrackingResource();
+    Unused << mBgParent->OnNotifyTrackingResource(aIsThirdParty);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelParent::Delete()
 {
   if (!mIPCClosed)
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -99,17 +99,23 @@ NullHttpChannel::SetTopLevelOuterContent
 
 NS_IMETHODIMP
 NullHttpChannel::GetIsTrackingResource(bool* aIsTrackingResource)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-NullHttpChannel::OverrideTrackingResource(bool aIsTracking)
+NullHttpChannel::GetIsThirdPartyTrackingResource(bool* aIsTrackingResource)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::OverrideTrackingFlagsForDocumentCookieAccessor(nsIHttpChannel* aDocumentChannel)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetTransferSize(uint64_t *aTransferSize)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
+++ b/netwerk/protocol/http/PHttpBackgroundChannel.ipdl
@@ -53,17 +53,17 @@ child:
   // OnDataAvailable and OnStopRequest messages in the queue back to the parent.
   async DivertMessages();
 
   // Tell the child that tracking protection was disabled for this load.
   async NotifyTrackingProtectionDisabled();
 
   // Tell the child that the resource being loaded is on the tracking
   // protection list.
-  async NotifyTrackingResource();
+  async NotifyTrackingResource(bool aIsThirdParty);
 
   // Tell the child information of matched URL againts SafeBrowsing list
   async SetClassifierMatchedInfo(ClassifierInfo info);
 
   async __delete__();
 
 };
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -621,21 +621,21 @@ nsHttpChannel::Connect()
         LOG(("Resuming from cache is not supported yet"));
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     if (ShouldIntercept()) {
         return RedirectToInterceptedChannel();
     }
 
-    bool isTrackingResource = mIsTrackingResource; // is atomic
+    bool isTrackingResource = mIsThirdPartyTrackingResource; // is atomic
     LOG(("nsHttpChannel %p tracking resource=%d, cos=%u",
           this, isTrackingResource, mClassOfService));
 
-    if (isTrackingResource && IsThirdPartyChannel()) {
+    if (isTrackingResource) {
         AddClassFlags(nsIClassOfService::Tail);
     }
 
     if (WaitingForTailUnblock()) {
         MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
         mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock;
         return NS_OK;
     }
@@ -680,20 +680,16 @@ nsHttpChannel::CheckFastBlocked()
 
     if (!sFastBlockInited) {
         sFastBlockInited = true;
         Preferences::AddBoolVarCache(&sIsContentBlockingEnabled, "browser.contentblocking.enabled");
         Preferences::AddBoolVarCache(&sIsFastBlockEnabled, "browser.fastblock.enabled");
         Preferences::AddUintVarCache(&sFastBlockTimeout, "browser.fastblock.timeout");
     }
 
-    if (!IsThirdPartyChannel()) {
-        return false;
-    }
-
     TimeStamp timestamp;
     if (NS_FAILED(GetNavigationStartTimeStamp(&timestamp))) {
         return false;
     }
 
     if (!sIsContentBlockingEnabled || !sIsFastBlockEnabled ||
         IsContentPolicyTypeWhitelistedForFastBlock(mLoadInfo) ||
         !timestamp) {
@@ -710,47 +706,24 @@ nsHttpChannel::CheckFastBlocked()
 
     LOG(("FastBlock %s (%lf) [this=%p]\n",
          isFastBlocking ? "timeout" : "passed",
          duration.ToMilliseconds(),
          this));
     return isFastBlocking;
 }
 
-bool
-nsHttpChannel::IsThirdPartyChannel()
-{
-    if (mIsThirdPartyChannel) {
-        return *mIsThirdPartyChannel;
-    }
-
-    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
-    if (!thirdPartyUtil) {
-        return false;
-    }
-
-    bool isThirdPartyChannel;
-    if (NS_FAILED(thirdPartyUtil->IsThirdPartyChannel(this,
-                                                      nullptr,
-                                                      &isThirdPartyChannel))) {
-        return false;
-    }
-
-    mIsThirdPartyChannel.emplace(isThirdPartyChannel);
-    return *mIsThirdPartyChannel;
-}
-
 nsresult
 nsHttpChannel::ConnectOnTailUnblock()
 {
     nsresult rv;
 
     LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this));
 
-    bool isTrackingResource = mIsTrackingResource; // is atomic
+    bool isTrackingResource = mIsThirdPartyTrackingResource; // is atomic
     if (isTrackingResource && CheckFastBlocked()) {
         Unused << AsyncAbort(NS_ERROR_ABORT);
         CloseCacheEntry(false);
         return NS_OK;
     }
 
     // Consider opening a TCP connection right away.
     SpeculativeConnect();
@@ -2333,29 +2306,22 @@ nsHttpChannel::ProcessResponse()
     // If that's null, though, we'll fall back to mReferrer just in case (this
     // is especially useful in xpcshell tests, where we don't have an actual
     // pageload to get a referrer from).
     nsCOMPtr<nsIURI> referrer = GetReferringPage();
     if (!referrer) {
         referrer = mReferrer;
     }
 
-    // We consider top-level tracking resource as non-tracking if not in 3rd
-    // party context.
-    bool isThirdPartyAndTrackingResource = false;
-    if (mIsTrackingResource) {
-        isThirdPartyAndTrackingResource = IsThirdPartyChannel();
-    }
-
     if (referrer) {
         nsCOMPtr<nsILoadContextInfo> lci = GetLoadContextInfo(this);
         mozilla::net::Predictor::UpdateCacheability(referrer, mURI, httpStatus,
                                                     mRequestHead, mResponseHead,
                                                     lci,
-                                                    isThirdPartyAndTrackingResource);
+                                                    mIsThirdPartyTrackingResource);
     }
 
     // Only allow 407 (authentication required) to continue
     if (mTransaction && mTransaction->ProxyConnectFailed() && httpStatus != 407) {
         return ProcessFailedProxyConnect(httpStatus);
     }
 
     MOZ_ASSERT(!mCachedContentIsValid || mRaceCacheWithNetwork,
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -27,17 +27,16 @@
 #include "AutoClose.h"
 #include "nsIStreamListener.h"
 #include "nsISupportsPrimitives.h"
 #include "nsICorsPreflightCallback.h"
 #include "AlternateServices.h"
 #include "nsIRaceCacheWithNetwork.h"
 #include "mozilla/extensions/PStreamFilterParent.h"
 #include "mozilla/Mutex.h"
-#include "mozilla/Maybe.h"
 
 class nsDNSPrefetch;
 class nsICancelable;
 class nsIHttpChannelAuthProvider;
 class nsInputStreamPump;
 class nsISSLStatus;
 
 namespace mozilla { namespace net {
@@ -656,20 +655,16 @@ private:
     // Called on untail when tailed during AsyncOpen execution.
     nsresult AsyncOpenOnTailUnblock();
     // Called on untail when tailed because of being a tracking resource.
     nsresult ConnectOnTailUnblock();
 
     // Check if current channel should be canceled by FastBlock rules.
     bool CheckFastBlocked();
 
-    // This caches the result of mozIThirdPartyUtil::IsThirdPartyChannel.
-    bool IsThirdPartyChannel();
-    Maybe<bool> mIsThirdPartyChannel;
-
     nsCString mUsername;
 
     // If non-null, warnings should be reported to this object.
     RefPtr<HttpChannelSecurityWarningReporter> mWarningReporter;
 
     RefPtr<ADivertableParentChannel> mParentChannel;
 
     // True if the channel is reading from cache.
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -474,30 +474,46 @@ interface nsIHttpChannel : nsIChannel
      */
     [must_use] attribute uint64_t topLevelContentWindowId;
 
     /**
      * Returns true if the channel has loaded a resource that is on the tracking
      * protection list.  This is only available if the
      * privacy.trackingprotection.annotate_channels pref is set and its value
      * should only be relied on after the channel has established a connection.
-     * Note that, if the privacy.trackingprotection.annotate_channels pref is set, also
-     * top-level channels could be marked as tracking resource.
+     *
+     * Note that top-level channels could be marked as tracking
+     * resource. In order to identify third-party tracking resources
+     * specifically, use isThirdPartyTrackingResource.
      */
     [infallible] readonly attribute boolean isTrackingResource;
 
     /**
-     * This method is used in order to override the tracking status of an HTTP
-     * channel.  This should only be called by Gecko under certain circumstances
-     * when Gecko can guarantee that the channel classifier service will not be
+     * Returns true if the channel has loaded a resource that is on the tracking
+     * protection list and is considered third-party with the top window URI.
+     * This is only available if the privacy.trackingprotection.annotate_channels
+     * pref is set and its value should only be relied on after the channel has
+     * established a connection.
+     */
+    [infallible] readonly attribute boolean isThirdPartyTrackingResource;
+
+    /**
+     * This method is used by the document.cookie call site in order
+     * to override the tracking status of an HTTP channel. This should
+     * only be called by Gecko under certain circumstances when Gecko
+     * can guarantee that the channel classifier service will not be
      * determining the tracking status of the channel.
      *
-     * Please avoid calling this API if you're unsure whether you should be using it.
+     * @param aDocumentChannel
+     *        The document channel from which to copy the tracking flags
+     *
+     * Please avoid calling this API if you're unsure whether you
+     * should be using it.
      */
-    [noscript] void overrideTrackingResource(in boolean aIsTracking);
+    [noscript] void overrideTrackingFlagsForDocumentCookieAccessor(in nsIHttpChannel aDocumentChannel);
 
     /**
      * ID of the top-level outer content window. Identifies this channel's
      * top-level window it comes from.
      *
      * NOTE: The setter of this attribute is currently for xpcshell test only.
      *       Don't alter it otherwise.
      */
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -829,20 +829,27 @@ nsViewSourceChannel::SetTopLevelOuterCon
 NS_IMETHODIMP
 nsViewSourceChannel::GetIsTrackingResource(bool* aIsTrackingResource)
 {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER :
       mHttpChannel->GetIsTrackingResource(aIsTrackingResource);
 }
 
 NS_IMETHODIMP
-nsViewSourceChannel::OverrideTrackingResource(bool aIsTracking)
+nsViewSourceChannel::GetIsThirdPartyTrackingResource(bool* aIsTrackingResource)
 {
   return !mHttpChannel ? NS_ERROR_NULL_POINTER :
-      mHttpChannel->OverrideTrackingResource(aIsTracking);
+      mHttpChannel->GetIsThirdPartyTrackingResource(aIsTrackingResource);
+}
+
+NS_IMETHODIMP
+nsViewSourceChannel::OverrideTrackingFlagsForDocumentCookieAccessor(nsIHttpChannel* aDocumentChannel)
+{
+  return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+      mHttpChannel->OverrideTrackingFlagsForDocumentCookieAccessor(aDocumentChannel);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetRequestMethod(nsACString & aRequestMethod)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->GetRequestMethod(aRequestMethod);
 }
--- a/netwerk/test/unit/test_trackingProtection_annotateChannels.js
+++ b/netwerk/test/unit/test_trackingProtection_annotateChannels.js
@@ -14,47 +14,45 @@ ChromeUtils.import("resource://testing-c
 // testing the lowest priority case by setting both prefs to true
 // (test_trackingProtection_annotateChannels_wrap2.js).  These wrapper scripts
 // are also in charge of creating the profile directory and setting up the URL
 // classifier test tables.
 //
 // Below where we need to take different actions based on the process type we're
 // in, we use runtime.processType to take the correct actions.
 
-const runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+const runtime = Services.appinfo;
 if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT) {
   do_get_profile();
 }
 
-const topWindowURI = NetUtil.newURI("http://www.example.com/");
+const defaultTopWindowURI = NetUtil.newURI("http://www.example.com/");
 
 function listener(tracking, priority, throttleable, nextTest) {
   this._tracking = tracking;
   this._priority = priority;
   this._throttleable = throttleable;
   this._nextTest = nextTest;
 }
 listener.prototype = {
   onStartRequest: function(request, context) {
     Assert.equal(request.QueryInterface(Ci.nsIHttpChannel).isTrackingResource,
-                 this._tracking);
+                 this._tracking, "tracking flag");
     Assert.equal(request.QueryInterface(Ci.nsISupportsPriority).priority,
-                 this._priority);
+                 this._priority, "channel priority");
     if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT && this._tracking) {
       Assert.equal(!!(request.QueryInterface(Ci.nsIClassOfService).classFlags &
                        Ci.nsIClassOfService.Throttleable),
-                   this._throttleable);
+                   this._throttleable, "throttleable flag");
     }
     request.cancel(Cr.NS_ERROR_ABORT);
     this._nextTest();
   },
-  onDataAvailable: function(request, context, stream, offset, count) {
-  },
-  onStopRequest: function(request, context, status) {
-  }
+  onDataAvailable: (request, context, stream, offset, count) => {},
+  onStopRequest: (request, context, status) => {},
 };
 
 var httpServer;
 var normalOrigin, trackingOrigin;
 var testPriorityMap;
 var currentTest;
 // When this test is running in e10s mode, the parent process is in charge of
 // setting the prefs for us, so here we merely read our prefs, and if they have
@@ -87,185 +85,215 @@ function setup_test() {
 
 function doPriorityTest() {
   if (!testPriorityMap.length) {
     runTests();
     return;
   }
 
   currentTest = testPriorityMap.shift();
-  var channel = makeChannel(currentTest.path, currentTest.loadingPrincipal);
+
+  // Let's be explicit about what we're testing!
+  Assert.ok("loadingPrincipal" in currentTest, "check for incomplete test case");
+  Assert.ok("topWindowURI" in currentTest, "check for incomplete test case");
+
+  var channel = makeChannel(currentTest.path, currentTest.loadingPrincipal, currentTest.topWindowURI);
   channel.asyncOpen2(new listener(currentTest.expectedTracking,
                                   currentTest.expectedPriority,
                                   currentTest.expectedThrottleable,
                                   doPriorityTest));
 }
 
-function makeChannel(path, loadingPrincipal) {
+function makeChannel(path, loadingPrincipal, topWindowURI) {
   var chan;
 
   if (loadingPrincipal) {
     chan = NetUtil.newChannel({
       uri: path,
-      loadingPrincipal: loadingPrincipal,
+      loadingPrincipal,
       securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
       contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
     });
   } else {
     chan = NetUtil.newChannel({
       uri: path,
       loadUsingSystemPrincipal: true,
     });
   }
   chan.QueryInterface(Ci.nsIHttpChannel);
   chan.requestMethod = "GET";
   chan.loadFlags |= Ci.nsIChannel.LOAD_CLASSIFY_URI;
-  chan.QueryInterface(Ci.nsIHttpChannelInternal).setTopWindowURIIfUnknown(topWindowURI);
+  if (topWindowURI) {
+    chan.QueryInterface(Ci.nsIHttpChannelInternal).setTopWindowURIIfUnknown(topWindowURI);
+  }
   return chan;
 }
 
-var tests =[
+var tests = [
   // Create the HTTP server.
   setup_test,
 
   // Add the test table into tracking protection table.
   function addTestTrackers() {
     if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT) {
       UrlClassifierTestUtils.addTestTrackers().then(() => {
         runTests();
       });
     } else {
       runTests();
     }
   },
 
-  // With the pref off, the priority of channel should be normal.
-  function setupNormalPriority() {
+  // Annotations OFF, normal loading principal, topWinURI of example.com
+  // => trackers should not be de-prioritized
+  function setupAnnotationsOff() {
     if (skipNormalPriority) {
       runTests();
       return;
     }
     if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT) {
       Services.prefs.setBoolPref("privacy.trackingprotection.annotate_channels", false);
       Services.prefs.setBoolPref("privacy.trackingprotection.lower_network_priority", false);
     }
+    var principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(normalOrigin);
     testPriorityMap = [
       {
         path: normalOrigin + "/innocent.css",
+        loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: normalOrigin + "/innocent.js",
+        loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: trackingOrigin + "/evil.css",
+        loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: trackingOrigin + "/evil.js",
+        loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedThrottleable: false, // ignored since tracking==false
       },
     ];
     // We add the doPriorityTest test here so that it only gets injected in the
     // test list if we're not skipping over this test.
     tests.unshift(doPriorityTest);
     runTests();
   },
 
-  // With the pref on, the priority of channel should be lowest.
-  function setupLowestPriority() {
+  // Annotations ON, normal loading principal, topWinURI of example.com
+  // => trackers should be de-prioritized
+  function setupAnnotationsOn() {
     if (skipLowestPriority) {
       runTests();
       return;
     }
     if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT) {
       Services.prefs.setBoolPref("privacy.trackingprotection.annotate_channels", true);
       Services.prefs.setBoolPref("privacy.trackingprotection.lower_network_priority", true);
     }
     var principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(normalOrigin);
     testPriorityMap = [
       {
         path: normalOrigin + "/innocent.css",
         loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: true,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: normalOrigin + "/innocent.js",
         loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: true,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: trackingOrigin + "/evil.css",
         loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: true,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_LOWEST,
         expectedThrottleable: true,
       },
       {
         path: trackingOrigin + "/evil.js",
         loadingPrincipal: principal,
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: true,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_LOWEST,
         expectedThrottleable: true,
       },
     ];
     // We add the doPriorityTest test here so that it only gets injected in the
     // test list if we're not skipping over this test.
     tests.unshift(doPriorityTest);
     runTests();
   },
 
-  // With the pref on, but not top-level requests, the priority of channel
-  // should be normal.
-  function setupTopLevelNormalPriority() {
+  // Annotations ON, system loading principal, topWinURI of example.com
+  // => trackers should be de-prioritized
+  function setupAnnotationsOnSystemPrincipal() {
     if (skipLowestPriority) {
       runTests();
       return;
     }
     if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT) {
       Services.prefs.setBoolPref("privacy.trackingprotection.annotate_channels", true);
       Services.prefs.setBoolPref("privacy.trackingprotection.lower_network_priority", true);
     }
     testPriorityMap = [
       {
         path: normalOrigin + "/innocent.css",
+        loadingPrincipal: null, // system principal
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: normalOrigin + "/innocent.js",
+        loadingPrincipal: null, // system principal
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: false,
         expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedThrottleable: false, // ignored since tracking==false
       },
       {
         path: trackingOrigin + "/evil.css",
+        loadingPrincipal: null, // system principal
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: true,
-        expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedPriority: Ci.nsISupportsPriority.PRIORITY_LOWEST,
+        expectedThrottleable: true,
       },
       {
         path: trackingOrigin + "/evil.js",
+        loadingPrincipal: null, // system principal
+        topWindowURI: defaultTopWindowURI,
         expectedTracking: true,
-        expectedPriority: Ci.nsISupportsPriority.PRIORITY_NORMAL,
-        expectedThrottleable: false,
+        expectedPriority: Ci.nsISupportsPriority.PRIORITY_LOWEST,
+        expectedThrottleable: true,
       },
     ];
     // We add the doPriorityTest test here so that it only gets injected in the
     // test list if we're not skipping over this test.
     tests.unshift(doPriorityTest);
     runTests();
   },
 
@@ -273,18 +301,17 @@ var tests =[
     httpServer.stop(do_test_finished);
     if (runtime.processType == runtime.PROCESS_TYPE_DEFAULT) {
       UrlClassifierTestUtils.cleanupTestTrackers();
     }
     runTests();
   }
 ];
 
-function runTests()
-{
+function runTests() {
   if (!tests.length) {
     do_test_finished();
     return;
   }
 
   var test = tests.shift();
   test();
 }
--- a/security/sandbox/mac/SandboxPolicies.h
+++ b/security/sandbox/mac/SandboxPolicies.h
@@ -790,22 +790,24 @@ static const char flashPluginSandboxRule
       (home-library-regex "/Application Support/Macromedia/ss\.(cfg|cfn|sgn)$"))
 
   (allow file-read*
       (literal "/Library/Preferences/com.apple.security.plist")
       (subpath "/private/var/db/mds"))
   ; Tests revealed file-write-{data,create,flags} required for some encrypted
   ; video playback. Allowing file-write* to match system profiles.
   (allow file-read* file-write*
-      (cache-literal "/mds/mds.lock")
-      (cache-literal "/mds/mdsDirectory.db_")
-      (cache-literal "/mds/mdsDirectory.db_")
-      (cache-literal "/mds/mdsObject.db")
-      (cache-literal "/mds/mdsObject.db_")
-      (require-all (vnode-type REGULAR-FILE)))
+      (require-all
+          (vnode-type REGULAR-FILE)
+          (require-any
+              (cache-literal "/mds/mds.lock")
+              (cache-literal "/mds/mdsDirectory.db")
+              (cache-literal "/mds/mdsDirectory.db_")
+              (cache-literal "/mds/mdsObject.db")
+              (cache-literal "/mds/mdsObject.db_"))))
 
   (allow network-bind (local ip))
 
   (deny file-write-create (vnode-type SYMLINK))
 )SANDBOX_LITERAL";
 
 }
 
--- a/servo/support/gecko/nsstring/src/conversions.rs
+++ b/servo/support/gecko/nsstring/src/conversions.rs
@@ -325,45 +325,45 @@ impl nsACString {
             if worst_case <= inline_capacity {
                 Some(worst_case)
             } else {
                 None
             }
         } else {
             None
         };
-        let (filled, num_ascii, mut handle) = if worst_case_needed.is_none() &&
+        let (filled, read, mut handle) = if worst_case_needed.is_none() &&
                                                  long_string_stars_with_basic_latin(other) {
             let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?;
             let mut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? };
-            let num_ascii = copy_basic_latin_to_ascii(other, &mut handle.as_mut_slice()[old_len..]);
-            let left = other.len() - num_ascii;
+            let (read, written) = convert_utf16_to_utf8_partial(other, &mut handle.as_mut_slice()[old_len..]);
+            let left = other.len() - read;
             if left == 0 {
-                return Ok(handle.finish(old_len + num_ascii, true));
+                return Ok(handle.finish(old_len + written, true));
             }
-            let filled = old_len + num_ascii;
+            let filled = old_len + written;
             let needed = times_three_plus_one(left).ok_or(())?;
             let new_len = filled.checked_add(needed).ok_or(())?;
             unsafe {
                 handle.restart_bulk_write(new_len, filled, false)?;
             }
-            (filled, num_ascii, handle)
+            (filled, read, handle)
         } else {
             // Started with non-ASCII. Compute worst case
             let needed = if let Some(n) = worst_case_needed {
                 n
             } else {
                 times_three_plus_one(other.len()).ok_or(())?
             };
             let new_len = old_len.checked_add(needed).ok_or(())?;
             let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
             (old_len, 0, handle)
         };
         let written =
-            convert_utf16_to_utf8(&other[num_ascii..], &mut handle.as_mut_slice()[filled..]);
+            convert_utf16_to_utf8(&other[read..], &mut handle.as_mut_slice()[filled..]);
         Ok(handle.finish(filled + written, true))
     }
 
     /// Convert a potentially-invalid UTF-16 string into valid UTF-8
     /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
     /// replace the content of this string with the conversion result.
     pub fn assign_utf16_to_utf8(&mut self, other: &[u16]) {
         self.fallible_append_utf16_to_utf8_impl(other, 0)
@@ -572,17 +572,17 @@ impl nsACString {
     );
 
     fn fallible_append_latin1_to_utf8_impl(
         &mut self,
         other: &[u8],
         old_len: usize,
         maybe_num_ascii: Option<usize>,
     ) -> Result<BulkWriteOk, ()> {
-        let (filled, num_ascii, mut handle) = if let Some(num_ascii) = maybe_num_ascii {
+        let (filled, read, mut handle) = if let Some(num_ascii) = maybe_num_ascii {
             // Wrapper checked for ASCII
             let left = other.len() - num_ascii;
             let filled = old_len + num_ascii;
             let needed = left.checked_mul(2).ok_or(())?;
             let new_len = filled.checked_add(needed).ok_or(())?;
             let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
             if num_ascii != 0 {
                 (&mut handle.as_mut_slice()[old_len..filled]).copy_from_slice(&other[..num_ascii]);
@@ -600,43 +600,43 @@ impl nsACString {
                 None
             };
             if worst_case_needed.is_none() && long_string_starts_with_ascii(other) {
                 // Wrapper didn't check for ASCII, so let's see if `other` starts with ASCII
                 // `other` starts with ASCII, so let's first size the buffer
                 // with optimism that it's ASCII-only.
                 let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?;
                 let mut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? };
-                let num_ascii = copy_ascii_to_ascii(other, &mut handle.as_mut_slice()[old_len..]);
-                let left = other.len() - num_ascii;
-                let filled = old_len + num_ascii;
+                let (read, written) = convert_latin1_to_utf8_partial(other, &mut handle.as_mut_slice()[old_len..]);
+                let left = other.len() - read;
+                let filled = old_len + written;
                 if left == 0 {
-                    // `other` was all ASCII
+                    // `other` fit in the initial allocation
                     return Ok(handle.finish(filled, true));
                 }
                 let needed = left.checked_mul(2).ok_or(())?;
                 let new_len = filled.checked_add(needed).ok_or(())?;
                 unsafe {
                     handle.restart_bulk_write(new_len, filled, false)?;
                 }
-                (filled, num_ascii, handle)
+                (filled, read, handle)
             } else {
                 // Started with non-ASCII. Assume worst case.
                 let needed = if let Some(n) = worst_case_needed {
                     n
                 } else {
                     other.len().checked_mul(2).ok_or(())?
                 };
                 let new_len = old_len.checked_add(needed).ok_or(())?;
                 let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
                 (old_len, 0, handle)
             }
         };
         let written =
-            convert_latin1_to_utf8(&other[num_ascii..], &mut handle.as_mut_slice()[filled..]);
+            convert_latin1_to_utf8(&other[read..], &mut handle.as_mut_slice()[filled..]);
         Ok(handle.finish(filled + written, true))
     }
 
     /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
     /// into UTF-8 and replace the content of this string with the conversion result.
     pub fn assign_latin1_to_utf8<T: Latin1StringLike + ?Sized>(&mut self, other: &T) {
         self.fallible_append_latin1_to_utf8_check(other, 0)
             .expect("Out of memory");
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -945,31 +945,16 @@ media:
       - nohlmeier@mozilla.com
     release_channel_collection: opt-in
     record_in_processes:
       - main
       - content
 
 # The following section contains content process base counters.
 dom.contentprocess:
-  troubled_due_to_memory:
-    bug_numbers:
-      - 1305091
-    description: >
-      The number of content processes that were marked as troubled because
-      it was running low on virtual memory.
-    expires: "58"
-    kind: uint
-    notification_emails:
-      - benjamin@smedbergs.us
-      - mconley@mozilla.com
-    release_channel_collection: opt-in
-    record_in_processes:
-      - 'main'
-
   buildID_mismatch:
     bug_numbers:
       - 1366808
     description: >
       The number of times the about:restartrequired page appeared due to a
       buildID mismatch between the parent and the content processes.
     expires: never
     kind: uint
--- a/tools/fuzzing/ipc/ProtocolFuzzer.h
+++ b/tools/fuzzing/ipc/ProtocolFuzzer.h
@@ -10,16 +10,49 @@
 #include "chrome/common/ipc_message.h"
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/ContentParent.h"
 
 namespace mozilla {
 namespace ipc {
 
+class ProtocolFuzzerHelper
+{
+public:
+  static mozilla::dom::ContentParent* CreateContentParent(
+    mozilla::dom::ContentParent* aOpener,
+    const nsAString& aRemoteType);
+
+  template<typename T>
+  static void AddShmemToProtocol(T* aProtocol,
+                                 Shmem::SharedMemory* aSegment,
+                                 int32_t aId)
+  {
+    GetToplevelState(aProtocol)->mShmemMap.AddWithID(aSegment, aId);
+  }
+
+  template<typename T>
+  static void RemoveShmemFromProtocol(T* aProtocol, int32_t aId)
+  {
+    GetToplevelState(aProtocol)->mShmemMap.Remove(aId);
+  }
+
+private:
+  template<typename T>
+  static mozilla::ipc::IToplevelProtocol::ToplevelState* GetToplevelState(
+    T* aProtocol)
+  {
+    static_assert(std::is_base_of<mozilla::ipc::IToplevelProtocol, T>::value,
+                  "Only ToplevelProtocols are supported for now");
+    return static_cast<mozilla::ipc::IToplevelProtocol::ToplevelState*>(
+      static_cast<mozilla::ipc::IToplevelProtocol*>(aProtocol)->mState.get());
+  }
+};
+
 template<typename T>
 void
 FuzzProtocol(T* aProtocol,
              const uint8_t* aData,
              size_t aSize,
              const nsTArray<nsCString>& aIgnoredMessageTypes)
 {
   while (true) {
@@ -32,35 +65,68 @@ FuzzProtocol(T* aProtocol,
     IPC::Message m(reinterpret_cast<const char*>(aData), msg_size);
     aSize -= msg_size;
     aData += msg_size;
 
     // We ignore certain message types
     if (aIgnoredMessageTypes.Contains(m.name())) {
       continue;
     }
+
+    uint8_t num_shmems = 0;
+    if (aSize) {
+      num_shmems = *aData;
+      aData++;
+      aSize--;
+
+      for (uint32_t i = 0; i < num_shmems; i++) {
+        if (aSize < sizeof(uint16_t)) {
+          break;
+        }
+        size_t shmem_size = *reinterpret_cast<const uint16_t*>(aData);
+        aData += sizeof(uint16_t);
+        aSize -= sizeof(uint16_t);
+
+        if (shmem_size > aSize) {
+          break;
+        }
+        RefPtr<Shmem::SharedMemory> segment(
+          Shmem::Alloc(Shmem::PrivateIPDLCaller(),
+                       shmem_size,
+                       SharedMemory::TYPE_BASIC,
+                       false));
+        if (!segment) {
+          break;
+        }
+
+        Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), i + 1);
+        memcpy(shmem.get<uint8_t>(), aData, shmem_size);
+        ProtocolFuzzerHelper::AddShmemToProtocol(
+          aProtocol, segment.forget().take(), i + 1);
+
+        aData += shmem_size;
+        aSize -= shmem_size;
+      }
+    }
     // TODO: attach |m.header().num_fds| file descriptors to |m|. MVP can be
     // empty files, next implementation maybe read a length header from |data|
-    // and then that many bytes. Probably need similar handling for shmem,
-    // other types?
+    // and then that many bytes.
 
     if (m.is_sync()) {
       nsAutoPtr<IPC::Message> reply;
       aProtocol->OnMessageReceived(m, *getter_Transfers(reply));
     } else {
       aProtocol->OnMessageReceived(m);
     }
+    for (uint32_t i = 0; i < num_shmems; i++) {
+      Shmem::SharedMemory* segment = aProtocol->LookupSharedMemory(i + 1);
+      Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
+      ProtocolFuzzerHelper::RemoveShmemFromProtocol(aProtocol, i + 1);
+    }
   }
 }
 
 nsTArray<nsCString> LoadIPCMessageBlacklist(const char* aPath);
 
-class ProtocolFuzzerHelper
-{
-public:
-  static mozilla::dom::ContentParent* CreateContentParent(
-    mozilla::dom::ContentParent* aOpener,
-    const nsAString& aRemoteType);
-};
 }
 }
 
 #endif
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -425,17 +425,17 @@ NS_IMETHODIMP nsExtProtocolChannel::Noti
 NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedInfo(const nsACString& aList,
                                                              const nsACString& aProvider,
                                                              const nsACString& aFullHash)
 {
   // nothing to do
   return NS_OK;
 }
 
-NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingResource()
+NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingResource(bool aIsThirdParty)
 {
   // nothing to do
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::Delete()
 {
   // nothing to do
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/AvailableMemoryTracker.h"
 
 #if defined(XP_WIN)
 #include "nsExceptionHandler.h"
+#include "nsICrashReporter.h"
 #include "nsIMemoryReporter.h"
 #include "nsMemoryPressure.h"
 #endif
 
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIRunnable.h"
 #include "nsISupports.h"
@@ -41,29 +42,30 @@ Atomic<uint32_t, MemoryOrdering::Relaxed
 class nsAvailableMemoryWatcher final : public nsIObserver,
                                        public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
 
+  nsAvailableMemoryWatcher();
   nsresult Init();
 
 private:
   // Fire a low-memory notification if we have less than this many bytes of
   // virtual address space available.
 #if defined(HAVE_64BIT_BUILD)
   static const size_t kLowVirtualMemoryThreshold = 0;
 #else
   static const size_t kLowVirtualMemoryThreshold = 256 * 1024 * 1024;
 #endif
 
-  // Fire a low-memory notification if we have less than this many bytes of commit
-  // space (physical memory plus page file) left.
+  // Fire a low-memory notification if we have less than this many bytes of
+  // commit space (physical memory plus page file) left.
   static const size_t kLowCommitSpaceThreshold = 256 * 1024 * 1024;
 
   // Fire a low-memory notification if we have less than this many bytes of
   // physical memory available on the whole machine.
   static const size_t kLowPhysicalMemoryThreshold = 0;
 
   // Don't fire a low-memory notification because of low available physical
   // memory or low commit space more often than this interval.
@@ -75,32 +77,42 @@ private:
   // Observer topics we subscribe to, see below.
   static const char* const kObserverTopics[];
 
   static bool IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat);
   static bool IsCommitSpaceLow(const MEMORYSTATUSEX& aStat);
   static bool IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat);
 
   ~nsAvailableMemoryWatcher() {};
+  bool OngoingMemoryPressure() { return mUnderMemoryPressure; }
   void AdjustPollingInterval(const bool aLowMemory);
   void SendMemoryPressureEvent();
+  void MaybeSaveMemoryReport();
   void Shutdown();
 
   nsCOMPtr<nsITimer> mTimer;
   bool mUnderMemoryPressure;
+  bool mSavedReport;
 };
 
 const char* const nsAvailableMemoryWatcher::kObserverTopics[] = {
   "quit-application",
   "user-interaction-active",
   "user-interaction-inactive",
 };
 
 NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcher, nsIObserver, nsITimerCallback)
 
+nsAvailableMemoryWatcher::nsAvailableMemoryWatcher()
+  : mTimer(nullptr)
+  , mUnderMemoryPressure(false)
+  , mSavedReport(false)
+{
+}
+
 nsresult
 nsAvailableMemoryWatcher::Init()
 {
   mTimer = NS_NewTimer();
 
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   MOZ_ASSERT(observerService);
 
@@ -168,29 +180,43 @@ nsAvailableMemoryWatcher::IsPhysicalMemo
   }
 
   return false;
 }
 
 void
 nsAvailableMemoryWatcher::SendMemoryPressureEvent()
 {
-    MemoryPressureState state = mUnderMemoryPressure ? MemPressure_Ongoing
-                                                     : MemPressure_New;
+    MemoryPressureState state = OngoingMemoryPressure() ? MemPressure_Ongoing
+                                                        : MemPressure_New;
     NS_DispatchEventualMemoryPressure(state);
 }
 
 void
+nsAvailableMemoryWatcher::MaybeSaveMemoryReport()
+{
+  if (!mSavedReport && OngoingMemoryPressure()) {
+    nsCOMPtr<nsICrashReporter> cr =
+      do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+    if (cr) {
+      if (NS_SUCCEEDED(cr->SaveMemoryReport())) {
+        mSavedReport = true;
+      }
+    }
+  }
+}
+
+void
 nsAvailableMemoryWatcher::AdjustPollingInterval(const bool aLowMemory)
 {
   if (aLowMemory) {
     // We entered a low-memory state, wait for a longer interval before polling
     // again as there's no point in rapidly sending further notifications.
     mTimer->SetDelay(kLowMemoryNotificationIntervalMS);
-  } else if (mUnderMemoryPressure) {
+  } else if (OngoingMemoryPressure()) {
     // We were under memory pressure but we're not anymore, resume polling at
     // a faster pace.
     mTimer->SetDelay(kPollingIntervalMS);
   }
 }
 
 // Timer callback, polls memory stats to detect low-memory conditions. This
 // will send memory-pressure events if memory is running low and adjust the
@@ -205,16 +231,19 @@ nsAvailableMemoryWatcher::Notify(nsITime
   if (success) {
     bool lowMemory =
       IsVirtualMemoryLow(stat) ||
       IsCommitSpaceLow(stat) ||
       IsPhysicalMemoryLow(stat);
 
     if (lowMemory) {
       SendMemoryPressureEvent();
+      MaybeSaveMemoryReport();
+    } else {
+      mSavedReport = false; // Save a new report if memory gets low again
     }
 
     AdjustPollingInterval(lowMemory);
     mUnderMemoryPressure = lowMemory;
   }
 
   return NS_OK;
 }
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -36,25 +36,22 @@
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/SystemGroup.h"
 #include "nsXPCOMPrivate.h"
 #include "mozilla/ChaosMode.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ScriptSettings.h"
-#include "nsICrashReporter.h"
 #include "nsThreadSyncDispatch.h"
 #include "nsServiceManagerUtils.h"
 #include "GeckoProfiler.h"
 #include "InputEventStatistics.h"
 #include "ThreadEventTarget.h"
 
-#include "mozilla/dom/ContentChild.h"
-
 #ifdef XP_LINUX
 #ifdef __GLIBC__
 #include <gnu/libc-version.h>
 #endif
 #include <sys/mman.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sched.h>
@@ -588,81 +585,16 @@ nsThread::InitCommon()
   }
 
   OffTheBooksMutexAutoLock mal(ThreadListMutex());
   ThreadList().insertBack(this);
 }
 
 //-----------------------------------------------------------------------------
 
-// Tell the crash reporter to save a memory report if our heuristics determine
-// that an OOM failure is likely to occur soon.
-// Memory usage will not be checked more than every 30 seconds or saved more
-// than every 3 minutes
-// If |aShouldSave == kForceReport|, a report will be saved regardless of
-// whether the process is low on memory or not. However, it will still not be
-// saved if a report was saved less than 3 minutes ago.
-bool
-nsThread::SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)
-{
-  // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
-  // but save memory reports (expensive, ~75ms) less frequently.
-  const size_t kLowMemoryCheckSeconds = 30;
-  const size_t kLowMemorySaveSeconds = 3 * 60;
-
-  static TimeStamp nextCheck = TimeStamp::NowLoRes()
-    + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
-  static bool recentlySavedReport = false; // Keeps track of whether a report
-                                           // was saved last time we checked
-
-  // Are we checking again too soon?
-  TimeStamp now = TimeStamp::NowLoRes();
-  if ((aShouldSave == ShouldSaveMemoryReport::kMaybeReport ||
-      recentlySavedReport) && now < nextCheck) {
-    return false;
-  }
-
-  bool needMemoryReport = (aShouldSave == ShouldSaveMemoryReport::kForceReport);
-#ifdef XP_WIN // XXX implement on other platforms as needed
-  // If the report is forced there is no need to check whether it is necessary
-  if (aShouldSave != ShouldSaveMemoryReport::kForceReport) {
-    const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
-    MEMORYSTATUSEX statex;
-    statex.dwLength = sizeof(statex);
-    if (GlobalMemoryStatusEx(&statex)) {
-      if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
-        needMemoryReport = true;
-      }
-    }
-  }
-#endif
-
-  if (needMemoryReport) {
-    if (XRE_IsContentProcess()) {
-      dom::ContentChild* cc = dom::ContentChild::GetSingleton();
-      if (cc) {
-        cc->SendNotifyLowMemory();
-      }
-    } else {
-      nsCOMPtr<nsICrashReporter> cr =
-        do_GetService("@mozilla.org/toolkit/crash-reporter;1");
-      if (cr) {
-        cr->SaveMemoryReport();
-      }
-    }
-    recentlySavedReport = true;
-    nextCheck = now + TimeDuration::FromSeconds(kLowMemorySaveSeconds);
-  } else {
-    recentlySavedReport = false;
-    nextCheck = now + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
-  }
-
-  return recentlySavedReport;
-}
-
 #ifdef MOZ_CANARY
 int sCanaryOutputFD = -1;
 #endif
 
 nsThread::nsThread(NotNull<SynchronizedEventQueue*> aQueue,
                    MainThreadFlag aMainThread,
                    uint32_t aStackSize)
   : mEvents(aQueue.get())
@@ -1420,20 +1352,16 @@ nsThread::DoMainThreadSpecificProcessing
                               mpPending == MemPressure_New ? u"low-memory" :
                               u"low-memory-ongoing");
         }
       } else {
         NS_WARNING("Can't get observer service!");
       }
     }
   }
-
-  if (!ShuttingDown()) {
-    SaveMemoryReportNearOOM(ShouldSaveMemoryReport::kMaybeReport);
-  }
 }
 
 NS_IMETHODIMP
 nsThread::GetEventTarget(nsIEventTarget** aEventTarget)
 {
   nsCOMPtr<nsIEventTarget> target = this;
   target.forget(aEventTarget);
   return NS_OK;
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -95,24 +95,16 @@ public:
 
   uint32_t
   RecursionDepth() const;
 
   void ShutdownComplete(NotNull<struct nsThreadShutdownContext*> aContext);
 
   void WaitForAllAsynchronousShutdowns();
 
-  enum class ShouldSaveMemoryReport
-  {
-    kMaybeReport,
-    kForceReport
-  };
-
-  static bool SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave);
-
   static const uint32_t kRunnableNameBufSize = 1000;
   static mozilla::Array<char, kRunnableNameBufSize> sMainThreadRunnableName;
 
   void EnableInputEventPrioritization()
   {
     EventQueue()->EnableInputEventPrioritization();
   }