Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Wed, 04 Jul 2018 19:30:07 +0300
changeset 814180 6c0fa9a675c91390ca27664ffb626c56e8afea4d
parent 814179 a07cf0515fe3ab55353749a088429484d8cb8bb9 (current diff)
parent 814112 40423bb8aa180813c4eb1cc58dcee9f0ad440841 (diff)
child 814181 ea4f65b8dc6737bc1640b329fe1c4f94cb0961b7
child 814209 0550477d1cdff62dab4bb1bab11283b32890ea78
child 814213 264a521b1efe5f3e308e2db1da3ac4dcb40f6c4b
child 814223 5b2fbbd4e2a9bde60063dcd180326a766159df09
child 814229 2a1704239e0fca8337b0d8d1c74ce79243e594bc
child 814230 e2b5fe81a4c5c696425583a04395f2ae79aeaccc
child 814306 5cc3bcfab31763f0c3b6508777375b9cab8a8b91
child 814314 799ffce9689aef524f33afb009365d07eda5153d
child 814327 acc2bfcdb230c052ca6d1aedb8b44534d772aecb
child 814336 edaa0d579c3528c932b6abdf5a4c79ae5f64e319
child 814341 eee9f7e33fe95116b7155f41bbc16912378e944e
child 814356 53dc1670c1c4104f02a7f08729d3674220e68e21
child 814379 f1224a9c32f5d8cc837f6c63a8c04a116d0a345e
child 814389 217b59dc70d1e022046a6832d213ff1f6ba838de
child 814473 5e5801a2afbbcae154a5cd5638701ebe3d6ad23c
child 814500 44fadb0beaed92ff7d8ec85ffea577f6716b7772
child 814714 9d2f1254dae55037481ce3894025e3e0963263ff
child 815041 16ebedb9fa84ae04e823767eda48a5260f72faf9
child 815865 4021e1a901b3294cbf26f2180f02cf04f163e042
push id115123
push userjdescottes@mozilla.com
push dateWed, 04 Jul 2018 17:42:29 +0000
reviewersmerge
milestone63.0a1
Merge inbound to mozilla-central. a=merge
browser/base/jar.mn
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-window.css
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@import url("chrome://browser/content/browser.css");
+@import url("chrome://browser/content/downloads/downloads.css");
+@import url("chrome://browser/content/places/places.css");
+@import url("chrome://browser/content/usercontext/usercontext.css");
+@import url("chrome://browser/skin/controlcenter/panel.css");
+@import url("chrome://browser/skin/customizableui/panelUI.css");
+@import url("chrome://browser/skin/downloads/downloads.css");
+@import url("chrome://browser/skin/searchbar.css");
+@import url("chrome://browser/skin/places/places.css");
+@import url("chrome://browser/skin/places/editBookmark.css");
+@import url("chrome://browser/skin/");
+@import url("chrome://browser/content/tabbrowser.css");
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1,28 +1,17 @@
 #filter substitution
 <?xml version="1.0"?>
 # -*- Mode: HTML -*-
 #
 # 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/.
 
-<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
-<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
-<?xml-stylesheet href="chrome://browser/skin/searchbar.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/editBookmark.css"?>
-<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/tabbrowser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/browser-window.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/compacttheme.css" type="text/css" alternate="yes" title="Light/Dark"?>
 
 # All DTD information is stored in a separate file so that it can be shared by
 # hiddenWindow.xul.
 <!DOCTYPE window [
 #include browser-doctype.inc
 ]>
 
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -50,16 +50,17 @@ browser.jar:
         content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
         content/browser/browser-sidebar.js            (content/browser-sidebar.js)
         content/browser/browser-siteIdentity.js       (content/browser-siteIdentity.js)
         content/browser/browser-sync.js               (content/browser-sync.js)
         content/browser/browser-tabsintitlebar.js       (content/browser-tabsintitlebar.js)
         content/browser/browser-thumbnails.js         (content/browser-thumbnails.js)
         content/browser/browser-trackingprotection.js (content/browser-trackingprotection.js)
         content/browser/browser-webrender.js          (content/browser-webrender.js)
+        content/browser/browser-window.css            (content/browser-window.css)
         content/browser/tab-content.js                (content/tab-content.js)
         content/browser/content.js                    (content/content.js)
         content/browser/defaultthemes/1.header.jpg    (content/defaultthemes/1.header.jpg)
         content/browser/defaultthemes/1.icon.jpg      (content/defaultthemes/1.icon.jpg)
         content/browser/defaultthemes/1.preview.jpg   (content/defaultthemes/1.preview.jpg)
         content/browser/defaultthemes/2.header.jpg    (content/defaultthemes/2.header.jpg)
         content/browser/defaultthemes/2.icon.jpg      (content/defaultthemes/2.icon.jpg)
         content/browser/defaultthemes/2.preview.jpg   (content/defaultthemes/2.preview.jpg)
--- a/devtools/server/actors/highlighters/flexbox.js
+++ b/devtools/server/actors/highlighters/flexbox.js
@@ -58,16 +58,19 @@ const JUSTIFY_CONTENT = "justify-content
 /**
  * The FlexboxHighlighter is the class that overlays a visual canvas on top of
  * display: [inline-]flex elements.
  *
  * Available Options:
  * - showAlignment(isShown)
  *   @param  {Boolean} isShown
  *   Shows the alignment in the flexbox highlighter.
+ * - showFlexBasis(isShown)
+ *   @param  {Boolean} isShown
+ *   Shows the flex basis in the flexbox highlighter.
  */
 class FlexboxHighlighter extends AutoRefreshHighlighter {
   constructor(highlighterEnv) {
     super(highlighterEnv);
 
     this.ID_CLASS_PREFIX = "flexbox-";
 
     this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
@@ -531,17 +534,17 @@ class FlexboxHighlighter extends AutoRef
     this.ctx.stroke();
     this.ctx.restore();
   }
 
   /**
    * Renders the flex basis for a given flex item.
    */
   renderFlexItemBasis(flexItem, left, top, right, bottom, boundsWidth) {
-    if (!this.computedStyle) {
+    if (!this.options.showFlexBasis || !this.computedStyle) {
       return;
     }
 
     let basis = this.flexBasis;
 
     if (basis.endsWith("px")) {
       right = Math.round(left + parseFloat(basis));
     } else if (basis.endsWith("%")) {
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -272,18 +272,18 @@ Decoder::FinalStatus() const
                             ShouldReportError());
 }
 
 DecoderTelemetry
 Decoder::Telemetry() const
 {
   MOZ_ASSERT(mIterator);
   return DecoderTelemetry(SpeedHistogram(),
-                          mIterator->ByteCount(),
-                          mIterator->ChunkCount(),
+                          mIterator ? mIterator->ByteCount() : 0,
+                          mIterator ? mIterator->ChunkCount() : 0,
                           mDecodeTime);
 }
 
 nsresult
 Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
                        const gfx::IntRect& aFrameRect,
                        gfx::SurfaceFormat aFormat,
                        uint8_t aPaletteDepth,
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1771,17 +1771,17 @@ RasterImage::NotifyDecodeComplete(const 
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
                             int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
 
       if (mAnimationState) {
         Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_TIME,
                               int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
       }
 
-      if (aTelemetry.mSpeedHistogram) {
+      if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
         Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
       }
     }
   }
 
   // Only act on errors if we have no usable frames from the decoder.
   if (aStatus.mHadError &&
       (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -4620,42 +4620,32 @@ struct GCChunkHasher {
         return k == l;
     }
 };
 
 class js::gc::MarkingValidator
 {
   public:
     explicit MarkingValidator(GCRuntime* gc);
-    ~MarkingValidator();
     void nonIncrementalMark(AutoGCSession& session);
     void validate();
 
   private:
     GCRuntime* gc;
     bool initialized;
 
-    typedef HashMap<Chunk*, ChunkBitmap*, GCChunkHasher, SystemAllocPolicy> BitmapMap;
+    using BitmapMap = HashMap<Chunk*, UniquePtr<ChunkBitmap>, GCChunkHasher, SystemAllocPolicy>;
     BitmapMap map;
 };
 
 js::gc::MarkingValidator::MarkingValidator(GCRuntime* gc)
   : gc(gc),
     initialized(false)
 {}
 
-js::gc::MarkingValidator::~MarkingValidator()
-{
-    if (!map.initialized())
-        return;
-
-    for (BitmapMap::Range r(map.all()); !r.empty(); r.popFront())
-        js_delete(r.front().value());
-}
-
 void
 js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session)
 {
     /*
      * Perform a non-incremental mark for all collecting zones and record
      * the results for later comparison.
      *
      * Currently this does not validate gray marking.
@@ -4672,22 +4662,23 @@ js::gc::MarkingValidator::nonIncremental
     /* Wait for off-thread parsing which can allocate. */
     HelperThreadState().waitForAllThreads();
 
     /* Save existing mark bits. */
     {
         AutoLockGC lock(runtime);
         for (auto chunk = gc->allNonEmptyChunks(lock); !chunk.done(); chunk.next()) {
             ChunkBitmap* bitmap = &chunk->bitmap;
-            ChunkBitmap* entry = js_new<ChunkBitmap>();
+            auto entry = MakeUnique<ChunkBitmap>();
             if (!entry)
                 return;
 
             memcpy((void*)entry->bitmap, (void*)bitmap->bitmap, sizeof(bitmap->bitmap));
-            if (!map.putNew(chunk, entry))
+
+            if (!map.putNew(chunk, std::move(entry)))
                 return;
         }
     }
 
     /*
      * Temporarily clear the weakmaps' mark flags for the compartments we are
      * collecting.
      */
@@ -4778,17 +4769,17 @@ js::gc::MarkingValidator::nonIncremental
         gc->marker.setMarkColorBlack();
     }
 
     /* Take a copy of the non-incremental mark state and restore the original. */
     {
         AutoLockGC lock(runtime);
         for (auto chunk = gc->allNonEmptyChunks(lock); !chunk.done(); chunk.next()) {
             ChunkBitmap* bitmap = &chunk->bitmap;
-            ChunkBitmap* entry = map.lookup(chunk)->value();
+            ChunkBitmap* entry = map.lookup(chunk)->value().get();
             Swap(*entry, *bitmap);
         }
     }
 
     for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
         WeakMapBase::unmarkZone(zone);
         AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!zone->gcWeakKeys().clear())
@@ -4821,17 +4812,17 @@ js::gc::MarkingValidator::validate()
     gc->waitBackgroundSweepEnd();
 
     AutoLockGC lock(gc->rt);
     for (auto chunk = gc->allNonEmptyChunks(lock); !chunk.done(); chunk.next()) {
         BitmapMap::Ptr ptr = map.lookup(chunk);
         if (!ptr)
             continue;  /* Allocated after we did the non-incremental mark. */
 
-        ChunkBitmap* bitmap = ptr->value();
+        ChunkBitmap* bitmap = ptr->value().get();
         ChunkBitmap* incBitmap = &chunk->bitmap;
 
         for (size_t i = 0; i < ArenasPerChunk; i++) {
             if (chunk->decommittedArenas.get(i))
                 continue;
             Arena* arena = &chunk->arenas[i];
             if (!arena->allocated())
                 continue;
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -672,20 +672,22 @@ AddressOf(SymbolicAddress imm, ABIFuncti
         *abiType = Args_General3;
         return FuncCast(Instance::wake, *abiType);
       case SymbolicAddress::MemCopy:
         *abiType = Args_General4;
         return FuncCast(Instance::memCopy, *abiType);
       case SymbolicAddress::MemFill:
         *abiType = Args_General4;
         return FuncCast(Instance::memFill, *abiType);
+#ifdef ENABLE_WASM_GC
       case SymbolicAddress::PostBarrier:
         *abiType = Args_General2;
         static_assert(sizeof(PostBarrierArg) == sizeof(uint32_t), "passed arg is a u32");
         return FuncCast(Instance::postBarrier, *abiType);
+#endif
 #if defined(JS_CODEGEN_MIPS32)
       case SymbolicAddress::js_jit_gAtomic64Lock:
         return &js::jit::gAtomic64Lock;
 #endif
       case SymbolicAddress::Limit:
         break;
     }
 
@@ -754,17 +756,19 @@ wasm::NeedsBuiltinThunk(SymbolicAddress 
       case SymbolicAddress::CurrentMemory:
       case SymbolicAddress::WaitI32:
       case SymbolicAddress::WaitI64:
       case SymbolicAddress::Wake:
       case SymbolicAddress::CoerceInPlace_JitEntry:
       case SymbolicAddress::ReportInt64JSCall:
       case SymbolicAddress::MemCopy:
       case SymbolicAddress::MemFill:
+#ifdef ENABLE_WASM_GC
       case SymbolicAddress::PostBarrier:
+#endif
         return true;
       case SymbolicAddress::Limit:
         break;
     }
 
     MOZ_CRASH("unexpected symbolic address");
 }
 
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -1262,18 +1262,20 @@ ThunkedNativeToDescription(SymbolicAddre
       case SymbolicAddress::CoerceInPlace_JitEntry:
         return "out-of-line coercion for jit entry arguments (in wasm)";
       case SymbolicAddress::ReportInt64JSCall:
         return "jit call to int64 wasm function";
       case SymbolicAddress::MemCopy:
         return "call to native memory.copy function";
       case SymbolicAddress::MemFill:
         return "call to native memory.fill function";
+#ifdef ENABLE_WASM_GC
       case SymbolicAddress::PostBarrier:
         return "call to native GC postbarrier (in wasm)";
+#endif
 #if defined(JS_CODEGEN_MIPS32)
       case SymbolicAddress::js_jit_gAtomic64Lock:
         MOZ_CRASH();
 #endif
       case SymbolicAddress::Limit:
         break;
     }
     return "?";
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -473,16 +473,17 @@ Instance::memFill(Instance* instance, ui
         // else fall through to failure case
     }
 
     JSContext* cx = TlsContext.get();
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
     return -1;
 }
 
+#ifdef ENABLE_WASM_GC
 /* static */ void
 Instance::postBarrier(Instance* instance, PostBarrierArg arg)
 {
     gc::Cell** cell = nullptr;
     switch (arg.type()) {
       case PostBarrierArg::Type::Global: {
         const GlobalDesc& global = instance->metadata().globals[arg.globalIndex()];
         MOZ_ASSERT(!global.isConstant());
@@ -494,16 +495,17 @@ Instance::postBarrier(Instance* instance
         cell = (gc::Cell**) globalAddr;
         break;
       }
     }
 
     MOZ_ASSERT(cell);
     TlsContext.get()->runtime()->gc.storeBuffer().putCell(cell);
 }
+#endif // ENABLE_WASM_GC
 
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    SharedCode code,
                    UniqueDebugState debug,
                    UniqueTlsData tlsDataIn,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
@@ -657,17 +659,19 @@ Instance::init(JSContext* cx)
         }
     }
 
     JitRuntime* jitRuntime = cx->runtime()->getJitRuntime(cx);
     if (!jitRuntime)
         return false;
     jsJitArgsRectifier_ = jitRuntime->getArgumentsRectifier();
     jsJitExceptionHandler_ = jitRuntime->getExceptionTail();
+#ifdef ENABLE_WASM_GC
     preBarrierCode_ = jitRuntime->preBarrier(MIRType::Object);
+#endif
     return true;
 }
 
 Instance::~Instance()
 {
     realm_->wasm.unregisterInstance(*this);
 
     const FuncImportVector& funcImports = metadata(code().stableTier()).funcImports;
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -174,17 +174,19 @@ class Instance
     static int32_t callImport_ref(Instance*, int32_t, int32_t, uint64_t*);
     static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
     static uint32_t currentMemory_i32(Instance* instance);
     static int32_t wait_i32(Instance* instance, uint32_t byteOffset, int32_t value, int64_t timeout);
     static int32_t wait_i64(Instance* instance, uint32_t byteOffset, int64_t value, int64_t timeout);
     static int32_t wake(Instance* instance, uint32_t byteOffset, int32_t count);
     static int32_t memCopy(Instance* instance, uint32_t destByteOffset, uint32_t srcByteOffset, uint32_t len);
     static int32_t memFill(Instance* instance, uint32_t byteOffset, uint32_t value, uint32_t len);
+#ifdef ENABLE_WASM_GC
     static void postBarrier(Instance* instance, PostBarrierArg arg);
+#endif
 };
 
 typedef UniquePtr<Instance> UniqueInstance;
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_instance_h
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1976,17 +1976,19 @@ enum class SymbolicAddress
     Int64ToDouble,
     GrowMemory,
     CurrentMemory,
     WaitI32,
     WaitI64,
     Wake,
     MemCopy,
     MemFill,
+#ifdef ENABLE_WASM_GC
     PostBarrier,
+#endif
 #if defined(JS_CODEGEN_MIPS32)
     js_jit_gAtomic64Lock,
 #endif
     Limit
 };
 
 bool
 IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode);
--- a/testing/geckodriver/README.md
+++ b/testing/geckodriver/README.md
@@ -626,23 +626,23 @@ ensure you put this in your [mozconfig]:
 
 You build geckodriver with the `./mach build testing/geckodriver`
 command, run tests with `./mach test testing/geckodriver`, and run
 the built executable with `./mach geckodriver -- --other --flags`.
 
 [Rust]: https://www.rust-lang.org/
 [Mozilla]: https://www.mozilla.org/en-US/
 [webdriver crate]: https://crates.io/crates/webdriver
-[commands]: https://docs.rs/webdriver/newest/webdriver/command/index.html
-[responses]: https://docs.rs/webdriver/newest/webdriver/response/index.html
+[commands]: https://docs.rs/webdriver/newest/webdriver/command/
+[responses]: https://docs.rs/webdriver/newest/webdriver/response/
 [errors]: https://docs.rs/webdriver/newest/webdriver/error/enum.ErrorStatus.html
-[Marionette protocol]: https://firefox-source-docs.mozilla.org/testing/marionette/marionette/Protocol.html
-[WebDriver]: https://w3c.github.io/webdriver/webdriver-spec.html
+[Marionette protocol]: https://firefox-source-docs.mozilla.org/testing/marionette/doc/marionette/Protocol.html
+[WebDriver]: https://w3c.github.io/webdriver/
 [FirefoxDriver]: https://github.com/SeleniumHQ/selenium/wiki/FirefoxDriver
-[Marionette]: https://firefox-source-docs.mozilla.org/testing/marionette/marionette/
+[Marionette]: https://firefox-source-docs.mozilla.org/testing/marionette/doc/marionette/
 [Firefox CI]: https://treeherder.mozilla.org/
 [mozconfig]: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Configuring_Build_Options
 
 
 Contact
 =======
 
 The mailing list for geckodriver discussion is
new file mode 100644
--- /dev/null
+++ b/testing/geckodriver/doc/Building.md
@@ -0,0 +1,91 @@
+Building geckodriver
+====================
+
+geckodriver is written in Rust, a systems programming language
+from Mozilla.  Crucially, it relies on the [webdriver crate] to
+provide the HTTPD and do most of the heavy lifting of marshalling
+the WebDriver protocol. geckodriver translates WebDriver [commands],
+[responses], and [errors] to the [Marionette protocol], and acts
+as a proxy between [WebDriver] and [Marionette].
+
+geckodriver is an optional build component when building Firefox,
+which means it is not built by default when invoking `./mach build`.
+To enable building of geckodriver, ensure to put this in your [mozconfig]:
+
+	ac_add_options --enable-geckodriver
+
+Because we use geckodriver in testing, particularly as part of the
+Web Platform Tests, it _is_ built by default in the [Firefox CI].
+A regular `-b do -p all -u none -t none` try syntax will build
+geckodriver on all the supported platforms.  The build will be part
+of the `B` task.
+
+If you use artifact builds you may also build geckodriver using cargo:
+
+	% cd testing/geckodriver
+	% cargo build
+	…
+	   Compiling geckodriver v0.21.0 (file:///home/ato/src/gecko/testing/geckodriver)
+	…
+	    Finished dev [optimized + debuginfo] target(s) in 7.83s
+
+Because all Rust code in central shares the same cargo workspace,
+the binary will be put in the `$(topsrcdir)/target` directory.
+
+You can run your freshly built geckodriver this way:
+
+	% ./mach geckodriver -- --other --flags
+
+And run its unit tests like this:
+
+	% ./mach test testing/geckodriver
+
+Or by invoking cargo in its subdirectory:
+
+	% cd testing/geckodriver
+	% cargo test
+
+[Rust]: https://www.rust-lang.org/
+[webdriver crate]: https://crates.io/crates/webdriver
+[commands]: https://docs.rs/webdriver/newest/webdriver/command/
+[responses]: https://docs.rs/webdriver/newest/webdriver/response/
+[errors]: https://docs.rs/webdriver/newest/webdriver/error/enum.ErrorStatus.html
+[Marionette protocol]: /testing/marionette/doc/marionette/Protocol.html
+[WebDriver]: https://w3c.github.io/webdriver/
+[Marionette]: /testing/marionette/doc/marionette
+[mozconfig]: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Configuring_Build_Options
+[Firefox CI]: https://treeherder.mozilla.org/
+
+
+Making geckodriver part of the official build
+---------------------------------------------
+
+There is a long-term intention to include geckodriver as part of
+the regular Firefox build.
+
+When we migrated geckodriver from GitHub into central it was originally
+enabled by default in all local builds.  Since this was one of the
+very first Rust components to land in central, support for Rust was a
+little bit experimental and we discovered a couple of different problems:
+
+  (1) Not all developers had Rust installed, and support for Rust
+      in the tree was experimental.  This caused some compile
+      errors in particular on Windows, and at the time developers
+      were not happy with requiring Rust for building Firefox.
+
+  (2) The additional build time induced by including a fairly large
+      Rust component in the default build was considered too high.
+
+At the time of writing, Rust support in central has improved vastly.
+We also require Rust for regular Firefox builds, which addresses
+part of issue 1.
+
+Our working theory is that we now build so much Rust code in central
+that many of the dependencies geckodriver relies on and that takes
+up a lot of time to build will have been built as dependencies for
+other Rust components in the tree, effectively making the additional
+time it takes to build geckodriver less prominent than it was when
+we originally tried including it in the default build.
+
+This work is tracked as part of
+<https://bugzilla.mozilla.org/show_bug.cgi?id=1471281>.
--- a/testing/geckodriver/doc/Releasing.md
+++ b/testing/geckodriver/doc/Releasing.md
@@ -20,23 +20,23 @@ In any case, the steps to release geckod
 
 
 Release new in-tree dependency crates
 -------------------------------------
 
 geckodriver depends on a number of Rust crates that also live in
 central by using relative paths:
 
-    [dependencies]
-    …
-    mozprofile = { path = "../mozbase/rust/mozprofile" }
+	[dependencies]
+	…
+	mozprofile = { path = "../mozbase/rust/mozprofile" }
 	mozrunner = { path = "../mozbase/rust/mozrunner" }
 	mozversion = { path = "../mozbase/rust/mozversion" }

-    webdriver = { path = "../webdriver" }
+	webdriver = { path = "../webdriver" }
 
 Because we need to export the geckodriver source code to the old
 GitHub repository when we release, we first need to publish these
 crates if they have had any changes in the interim since the last
 release.  If they have receieved no changes, you can skip them:
 
   - `testing/mozbase/rust/mozprofile`
   - `testing/mozbase/rust/mozrunner`
--- a/testing/geckodriver/doc/index.rst
+++ b/testing/geckodriver/doc/index.rst
@@ -20,9 +20,10 @@ For users
    CrashReports.md
 
 
 For developers
 ==============
 .. toctree::
    :maxdepth: 1
 
+   Building.md
    Releasing.md
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -4,8 +4,10 @@ support-files =
   page.html
   3rdParty.html
   empty.js
 
 [browser_blockingResources.js]
 [browser_blockingCookies.js]
 support-files = server.sjs
 [browser_blockingMessaging.js]
+[browser_imageCache.js]
+support-files = image.sjs
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_imageCache.js
@@ -0,0 +1,47 @@
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+AntiTracking.runTest("Image cache - should load the image twice.",
+  // blocking callback
+  async _ => {
+    // Let's load the image twice here.
+    let img = document.createElement("img");
+    document.body.appendChild(img);
+    img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs",
+    await new Promise(resolve => { img.onload = resolve; });
+    ok(true, "Image 1 loaded");
+
+    img = document.createElement("img");
+    document.body.appendChild(img);
+    img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs",
+    await new Promise(resolve => { img.onload = resolve; });
+    ok(true, "Image 2 loaded");
+  },
+
+  // non-blocking callback
+  async _ => {
+    // Let's load the image twice here as well.
+    let img = document.createElement("img");
+    document.body.appendChild(img);
+    img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs",
+    await new Promise(resolve => { img.onload = resolve; });
+    ok(true, "Image 3 loaded");
+
+    img = document.createElement("img");
+    document.body.appendChild(img);
+    img.src = "https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs",
+    await new Promise(resolve => { img.onload = resolve; });
+    ok(true, "Image 4 loaded");
+  });
+
+// We still want to see just 2 requests.
+add_task(async _ => {
+  await fetch("https://tracking.example.com/browser/toolkit/components/antitracking/test/browser/image.sjs?result")
+    .then(r => r.text())
+    .then(text => {
+      is(text, 2, "The image should be loaded correctly.");
+    });
+
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+  });
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/image.sjs
@@ -0,0 +1,20 @@
+// A 1x1 PNG image.
+// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+                   "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+
+function handleRequest(aRequest, aResponse) {
+  aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+  if (aRequest.queryString.includes("result")) {
+    aResponse.write(getState("hints") || 0);
+    setState("hints", "0");
+  } else {
+    let hints = parseInt(getState("hints") || 0) + 1;
+    setState("hints", hints.toString());
+
+    aResponse.setHeader("Cache-Control", "max-age=10000", false);
+    aResponse.setHeader("Content-Type", "image/png", false);
+    aResponse.write(IMAGE);
+ }
+}