Merge mozilla-central to inbound. a=merge CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Sat, 03 Mar 2018 11:50:44 +0200
changeset 406442 9a31ff6a188a523f570cc1faa27cd8091fae3055
parent 406441 df8bffb2a6ac34f43c1cfa857a992119b1686fb1 (current diff)
parent 406439 71edaf2bd1b01daffb805627623712bd329fb5f9 (diff)
child 406443 f96ee15e11b1a74d5206c6d0f9a8f54c69aa8745
push id33558
push userrgurzau@mozilla.com
push dateSat, 03 Mar 2018 21:46:37 +0000
treeherdermozilla-central@8cced2a46f73 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.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 inbound. a=merge CLOSED TREE
browser/base/content/test/plugins/browser_pageInfo_plugins.js
browser/extensions/shield-recipe-client/bootstrap.js
browser/extensions/shield-recipe-client/content/AboutPages.jsm
browser/extensions/shield-recipe-client/content/about-studies/about-studies.css
browser/extensions/shield-recipe-client/content/about-studies/about-studies.html
browser/extensions/shield-recipe-client/content/about-studies/about-studies.js
browser/extensions/shield-recipe-client/content/about-studies/common.js
browser/extensions/shield-recipe-client/content/about-studies/img/shield-logo.png
browser/extensions/shield-recipe-client/content/about-studies/shield-studies.js
browser/extensions/shield-recipe-client/content/shield-content-frame.js
browser/extensions/shield-recipe-client/content/shield-content-process.js
browser/extensions/shield-recipe-client/docs/data-collection.rst
browser/extensions/shield-recipe-client/docs/index.rst
browser/extensions/shield-recipe-client/install.rdf.in
browser/extensions/shield-recipe-client/jar.mn
browser/extensions/shield-recipe-client/lib/ActionSandboxManager.jsm
browser/extensions/shield-recipe-client/lib/AddonStudies.jsm
browser/extensions/shield-recipe-client/lib/Addons.jsm
browser/extensions/shield-recipe-client/lib/CleanupManager.jsm
browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
browser/extensions/shield-recipe-client/lib/EventEmitter.jsm
browser/extensions/shield-recipe-client/lib/FilterExpressions.jsm
browser/extensions/shield-recipe-client/lib/Heartbeat.jsm
browser/extensions/shield-recipe-client/lib/LogManager.jsm
browser/extensions/shield-recipe-client/lib/NormandyApi.jsm
browser/extensions/shield-recipe-client/lib/NormandyDriver.jsm
browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
browser/extensions/shield-recipe-client/lib/PreferenceFilters.jsm
browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
browser/extensions/shield-recipe-client/lib/Sampling.jsm
browser/extensions/shield-recipe-client/lib/SandboxManager.jsm
browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm
browser/extensions/shield-recipe-client/lib/Storage.jsm
browser/extensions/shield-recipe-client/lib/TelemetryEvents.jsm
browser/extensions/shield-recipe-client/lib/Uptake.jsm
browser/extensions/shield-recipe-client/lib/Utils.jsm
browser/extensions/shield-recipe-client/moz.build
browser/extensions/shield-recipe-client/skin/osx/Heartbeat.css
browser/extensions/shield-recipe-client/skin/shared/Heartbeat.css
browser/extensions/shield-recipe-client/skin/shared/heartbeat-icon.svg
browser/extensions/shield-recipe-client/skin/shared/heartbeat-star-lit.svg
browser/extensions/shield-recipe-client/skin/shared/heartbeat-star-off.svg
browser/extensions/shield-recipe-client/test/.eslintrc.js
browser/extensions/shield-recipe-client/test/browser/.eslintrc.js
browser/extensions/shield-recipe-client/test/browser/action_server.sjs
browser/extensions/shield-recipe-client/test/browser/browser.ini
browser/extensions/shield-recipe-client/test/browser/browser_ActionSandboxManager.js
browser/extensions/shield-recipe-client/test/browser/browser_AddonStudies.js
browser/extensions/shield-recipe-client/test/browser/browser_Addons.js
browser/extensions/shield-recipe-client/test/browser/browser_CleanupManager.js
browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
browser/extensions/shield-recipe-client/test/browser/browser_EventEmitter.js
browser/extensions/shield-recipe-client/test/browser/browser_FilterExpressions.js
browser/extensions/shield-recipe-client/test/browser/browser_Heartbeat.js
browser/extensions/shield-recipe-client/test/browser/browser_LogManager.js
browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js
browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js
browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
browser/extensions/shield-recipe-client/test/browser/browser_ShieldPreferences.js
browser/extensions/shield-recipe-client/test/browser/browser_ShieldRecipeClient.js
browser/extensions/shield-recipe-client/test/browser/browser_Storage.js
browser/extensions/shield-recipe-client/test/browser/browser_about_preferences.js
browser/extensions/shield-recipe-client/test/browser/browser_about_studies.js
browser/extensions/shield-recipe-client/test/browser/browser_bootstrap.js
browser/extensions/shield-recipe-client/test/browser/fixtures/addon-fixture/manifest.json
browser/extensions/shield-recipe-client/test/browser/fixtures/normandy.xpi
browser/extensions/shield-recipe-client/test/browser/head.js
browser/extensions/shield-recipe-client/test/unit/.eslintrc.js
browser/extensions/shield-recipe-client/test/unit/echo_server.sjs
browser/extensions/shield-recipe-client/test/unit/head_xpc.js
browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/index.json
browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/recipe/signed/index.json
browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/normandy.content-signature.mozilla.org-20210705.dev.chain
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/console-log/implementation/sha384-RGx3rydrSq53UfmW9kFcK0mQYra67XIvZvr4MhmAe--ljiiMQOtgM7Cmca48um3v
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/console-log/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/opt-out-study/implementation/sha384-HM_avYcD00o27ufwU1V7PIBtiuMAXML6MMwlYrDEqDX-XzGVuOfL52RCM680JExN
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/opt-out-study/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/preference-experiment/implementation/sha384-KQgG38GQ7KZAb2VIB48ANQO6nBcxZoLm2ORzUviRT5nAvSywyPjZ5cJIElw6iXIt
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/preference-experiment/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/show-heartbeat/implementation/sha384-dEGiyKPEln8Ns5cQHzGpMIGdirSAAX0X-Kwlu-U3sJ05yNbO-ANij_a6c5SyL7G4
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/show-heartbeat/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/signed/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/classify_client/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/recipe/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/recipe/signed/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/normandy.content-signature.mozilla.org-20210705.dev.chain
browser/extensions/shield-recipe-client/test/unit/query_server.sjs
browser/extensions/shield-recipe-client/test/unit/test_NormandyApi.js
browser/extensions/shield-recipe-client/test/unit/test_Sampling.js
browser/extensions/shield-recipe-client/test/unit/test_SandboxManager.js
browser/extensions/shield-recipe-client/test/unit/test_Utils.js
browser/extensions/shield-recipe-client/test/unit/utils.js
browser/extensions/shield-recipe-client/test/unit/xpcshell.ini
browser/extensions/shield-recipe-client/vendor/LICENSE_THIRDPARTY
browser/extensions/shield-recipe-client/vendor/PropTypes.js
browser/extensions/shield-recipe-client/vendor/React.js
browser/extensions/shield-recipe-client/vendor/ReactDOM.js
browser/extensions/shield-recipe-client/vendor/classnames.js
browser/extensions/shield-recipe-client/vendor/mozjexl.js
browser/modules/CastingApps.jsm
third_party/rust/bincode-0.8.0/.cargo-checksum.json
third_party/rust/bincode-0.8.0/.travis.yml
third_party/rust/bincode-0.8.0/Cargo.toml
third_party/rust/bincode-0.8.0/LICENSE.md
third_party/rust/bincode-0.8.0/changelist.org
third_party/rust/bincode-0.8.0/examples/basic.rs
third_party/rust/bincode-0.8.0/logo.png
third_party/rust/bincode-0.8.0/readme.dev.md
third_party/rust/bincode-0.8.0/readme.md
third_party/rust/bincode-0.8.0/src/de/mod.rs
third_party/rust/bincode-0.8.0/src/de/read.rs
third_party/rust/bincode-0.8.0/src/internal.rs
third_party/rust/bincode-0.8.0/src/lib.rs
third_party/rust/bincode-0.8.0/src/ser/mod.rs
third_party/rust/bincode-0.8.0/tests/test.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -47,17 +47,17 @@ dependencies = [
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "audioipc"
 version = "0.2.1"
 dependencies = [
- "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "cubeb 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -81,17 +81,17 @@ dependencies = [
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "audioipc-server"
-version = "0.2.1"
+version = "0.2.2"
 dependencies = [
  "audioipc 0.2.1",
  "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "cubeb 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -112,26 +112,16 @@ dependencies = [
 
 [[package]]
 name = "binary-space-partition"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bincode"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "bincode"
 version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -424,30 +414,30 @@ dependencies = [
  "syn 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cubeb-core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-backend"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cubeb-core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-core"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cubeb-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-pulse"
@@ -753,17 +743,17 @@ dependencies = [
  "xpcom-gtest 0.1.0",
 ]
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "audioipc-client 0.3.0",
- "audioipc-server 0.2.1",
+ "audioipc-server 0.2.2",
  "cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "cubeb-pulse 0.1.1",
  "cubeb-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_glue 0.1.0",
  "geckoservo 0.0.1",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse_capi 0.10.0",
@@ -2233,17 +2223,16 @@ dependencies = [
 "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
 "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
 "checksum app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29069a9b483f7780aebb55dafb360c6225eefdc1f98c8d336a65148fd10c37b1"
 "checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
 "checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
 "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
 "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
 "checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
-"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e"
 "checksum bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3fb369af639822830328794eba2501b3479652fcd021b2aeb1ed4984202afd"
 "checksum bindgen 0.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1657d607dd7a8e10b3181149f60f3b27ea0eac81058c09a1c791b8f6ead91f19"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
 "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
 "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707"
 "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75"
 "checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"
 "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
@@ -2270,17 +2259,17 @@ dependencies = [
 "checksum crossbeam-epoch 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59796cc6cbbdc6bb319161349db0c3250ec73ec7fcb763a51065ec4e2e158552"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
 "checksum cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a807ac3ab7a217829c2a3b65732b926b2befe6a35f33b4bf8b503692430f223"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
 "checksum cstr-macros 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9f316203d1ea36f4f18316822806f6999aa3dc5ed1adf51e35b77e3b3933d78"
 "checksum cubeb 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed237804b9799d1c29089e6cab3f4b7160186179981a61865feff13d55a902f8"
 "checksum cubeb-backend 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f6d8f189a1cf9cce9aec45eb0aeb1d221514d788b89a1cd6bc2b76110ee4d81"
-"checksum cubeb-core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac5bb9e9d8747ae2c0884c970852c158315d809e33ce7c6a7b42b80b6a376aa"
+"checksum cubeb-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6558e6b99511df1b4e7ce219b26527b0cb0f6f56df2fd5877aa9473b7c7eafb"
 "checksum cubeb-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fab28c0e152330f74bcbec1572c374397458957d5ad50605879352ec562f41aa"
 "checksum darling 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3effd06d4057f275cb7858889f4952920bab78dd8ff0f6e7dfe0c8d2e67ed89"
 "checksum darling_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "167dd3e235c2f1da16a635c282630452cdf49191eb05711de1bcd1d3d5068c00"
 "checksum darling_macro 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c53edaba455f6073a10c27c72440860eb3f60444f8c8660a391032eeae744d82"
 "checksum debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
 "checksum dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a207eb7b40e25d1d28dc679f451d321fb6954b73ceaa47986702575865469461"
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1758,8 +1758,25 @@ pref("browser.chrome.errorReporter.sampl
 pref("browser.chrome.errorReporter.publicKey", "c709cb7a2c0b4f0882fcc84a5af161ec");
 pref("browser.chrome.errorReporter.projectId", "339");
 pref("browser.chrome.errorReporter.submitUrl", "https://sentry.prod.mozaws.net/api/339/store/");
 pref("browser.chrome.errorReporter.logLevel", "Error");
 
 // URL for Learn More link for browser error logging in preferences
 pref("browser.chrome.errorReporter.infoURL",
      "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection");
+
+#ifdef EARLY_BETA_OR_EARLIER
+pref("browser.policies.enabled", true);
+#endif
+
+// Normandy client preferences
+pref("app.normandy.api_url", "https://normandy.cdn.mozilla.net/api/v1");
+pref("app.normandy.dev_mode", false);
+pref("app.normandy.enabled", true);
+pref("app.normandy.logging.level", 50); // Warn
+pref("app.normandy.run_interval_seconds", 86400); // 24 hours
+pref("app.normandy.shieldLearnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield");
+#ifdef MOZ_DATA_REPORTING
+pref("app.shield.optoutstudies.enabled", true);
+#else
+pref("app.shield.optoutstudies.enabled", false);
+#endif
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -8067,37 +8067,41 @@ var gIdentityHandler = {
   },
 
   _createPermissionItem(aPermission) {
     let container = document.createElement("hbox");
     container.setAttribute("class", "identity-popup-permission-item");
     container.setAttribute("align", "center");
 
     let img = document.createElement("image");
-    let classes = "identity-popup-permission-icon " + aPermission.id + "-icon";
+    img.classList.add("identity-popup-permission-icon");
+    if (aPermission.id == "plugin:flash") {
+      img.classList.add("plugin-icon");
+    } else {
+      img.classList.add(aPermission.id + "-icon");
+    }
     if (aPermission.state == SitePermissions.BLOCK)
-      classes += " blocked-permission-icon";
+      img.classList.add("blocked-permission-icon");
 
     if (aPermission.sharingState == Ci.nsIMediaManagerService.STATE_CAPTURE_ENABLED ||
        (aPermission.id == "screen" && aPermission.sharingState &&
         !aPermission.sharingState.includes("Paused"))) {
-      classes += " in-use";
+      img.classList.add("in-use");
 
       // Synchronize control center and identity block blinking animations.
       window.promiseDocumentFlushed(() => {
         let sharingIconBlink = document.getElementById("sharing-icon").getAnimations()[0];
         let imgBlink = img.getAnimations()[0];
         return [sharingIconBlink, imgBlink];
       }).then(([sharingIconBlink, imgBlink]) => {
         if (sharingIconBlink && imgBlink) {
           imgBlink.startTime = sharingIconBlink.startTime;
         }
       });
     }
-    img.setAttribute("class", classes);
 
     let nameLabel = document.createElement("label");
     nameLabel.setAttribute("flex", "1");
     nameLabel.setAttribute("class", "identity-popup-permission-label");
     nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);
 
     let isPolicyPermission = aPermission.scope == SitePermissions.SCOPE_POLICY;
 
@@ -8149,17 +8153,17 @@ var gIdentityHandler = {
     stateLabel.setAttribute("class", "identity-popup-permission-state-label");
     let {state, scope} = aPermission;
     // If the user did not permanently allow this device but it is currently
     // used, set the variables to display a "temporarily allowed" info.
     if (state != SitePermissions.ALLOW && aPermission.sharingState) {
       state = SitePermissions.ALLOW;
       scope = SitePermissions.SCOPE_REQUEST;
     }
-    stateLabel.textContent = SitePermissions.getCurrentStateLabel(state, scope);
+    stateLabel.textContent = SitePermissions.getCurrentStateLabel(state, aPermission.id, scope);
 
     container.appendChild(img);
     container.appendChild(nameLabel);
     container.appendChild(stateLabel);
 
     /* We return the permission item here without a remove button if the permission is a
        SCOPE_POLICY permission. Policy permissions cannot be removed/changed for the duration
        of the browser session. */
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -810,16 +810,18 @@
                   <image data-permission-id="screen" class="blocked-permission-icon screen-icon" role="button"
                          tooltiptext="&urlbar.screenBlocked.tooltip;"/>
                   <image data-permission-id="persistent-storage" class="blocked-permission-icon persistent-storage-icon" role="button"
                          tooltiptext="&urlbar.persistentStorageBlocked.tooltip;"/>
                   <image data-permission-id="popup" class="blocked-permission-icon popup-icon" role="button"
                          tooltiptext="&urlbar.popupBlocked.tooltip;"/>
                   <image data-permission-id="canvas" class="blocked-permission-icon canvas-icon" role="button"
                          tooltiptext="&urlbar.canvasBlocked.tooltip;"/>
+                  <image data-permission-id="plugin:flash" class="blocked-permission-icon plugin-icon" role="button"
+                         tooltiptext="&urlbar.flashPluginBlocked.tooltip;"/>
                   <image data-permission-id="midi" class="blocked-permission-icon midi-icon" role="button"
                          tooltiptext="&urlbar.midiBlocked.tooltip;"/>
                 </box>
                 <box id="notification-popup-box"
                      hidden="true"
                      onmouseover="document.getElementById('identity-box').classList.add('no-hover');"
                      onmouseout="document.getElementById('identity-box').classList.remove('no-hover');"
                      align="center">
--- a/browser/base/content/pageinfo/pageInfo.xul
+++ b/browser/base/content/pageinfo/pageInfo.xul
@@ -44,20 +44,16 @@
     <stringbundle id="browserBundle" src="chrome://browser/locale/browser.properties"/>
   </stringbundleset>
 
   <commandset id="pageInfoCommandSet">
     <command id="cmd_close"     oncommand="window.close();"/>
     <command id="cmd_help"      oncommand="doHelpButton();"/>
     <command id="cmd_copy"      oncommand="doCopy();"/>
     <command id="cmd_selectall" oncommand="doSelectAll();"/>
-
-    <!-- permissions tab -->
-    <command id="cmd_pluginsDef"    oncommand="onCheckboxClick('plugins');"/>
-    <command id="cmd_pluginsToggle" oncommand="onPluginRadioClick(event);"/>
   </commandset>
 
   <keyset id="pageInfoKeySet">
     <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/>
     <key keycode="VK_ESCAPE"                       command="cmd_close"/>
 #ifdef XP_MACOSX
     <key key="."                 modifiers="meta"  command="cmd_close"/>
 #else
@@ -277,33 +273,17 @@
     <!-- Permissions -->
     <vbox id="permPanel">
       <hbox id="permHostBox">
         <label value="&permissionsFor;" control="hostText" />
         <textbox id="hostText" class="header" readonly="true"
                  crop="end" flex="1"/>
       </hbox>
 
-      <vbox id="permList" flex="1">
-        <vbox class="permission" id="perm-plugins-row">
-          <label class="permissionLabel" id="permPluginsLabel"
-                 value="&permPlugins;" control="pluginsRadioGroup"/>
-          <hbox id="permPluginTemplate" role="group" aria-labelledby="permPluginsLabel" align="baseline">
-            <label class="permPluginTemplateLabel"/>
-            <spacer flex="1"/>
-            <radiogroup class="permPluginTemplateRadioGroup" orient="horizontal" command="cmd_pluginsToggle">
-              <radio class="permPluginTemplateRadioDefault" label="&permUseDefault;"/>
-              <radio class="permPluginTemplateRadioAsk" label="&permAskAlways;"/>
-              <radio class="permPluginTemplateRadioAllow" label="&permAllow;"/>
-              <radio class="permPluginTemplateRadioHide" label="&permHide;"/>
-              <radio class="permPluginTemplateRadioBlock" label="&permBlock;"/>
-            </radiogroup>
-          </hbox>
-        </vbox>
-      </vbox>
+      <vbox id="permList" flex="1"/>
       <hbox pack="end">
         <button command="cmd_help" label="&helpButton.label;" dlgtype="help"/>
       </hbox>
     </vbox>
 
     <!-- Security & Privacy -->
     <vbox id="securityPanel">
       <!-- Identity Section -->
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -12,27 +12,23 @@ var gPermPrincipal;
 var gUsageRequest;
 
 // Array of permissionIDs sorted alphabetically by label.
 var gPermissions = SitePermissions.listPermissions().sort((a, b) => {
   let firstLabel = SitePermissions.getPermissionLabel(a);
   let secondLabel = SitePermissions.getPermissionLabel(b);
   return firstLabel.localeCompare(secondLabel);
 });
-gPermissions.push("plugins");
 
 var permissionObserver = {
   observe(aSubject, aTopic, aData) {
     if (aTopic == "perm-changed") {
       var permission = aSubject.QueryInterface(Ci.nsIPermission);
-      if (permission.matchesURI(gPermURI, true)) {
-        if (gPermissions.indexOf(permission.type) > -1)
+      if (permission.matchesURI(gPermURI, true) && gPermissions.includes(permission.type)) {
           initRow(permission.type);
-        else if (permission.type.startsWith("plugin"))
-          setPluginsRadioState();
       }
     }
   }
 };
 
 function onLoadPermission(uri, principal) {
   var permTab = document.getElementById("permTab");
   if (SitePermissions.isSupportedURI(uri)) {
@@ -55,28 +51,28 @@ function onUnloadPermission() {
 
   if (gUsageRequest) {
     gUsageRequest.cancel();
     gUsageRequest = null;
   }
 }
 
 function initRow(aPartId) {
-  if (aPartId == "plugins") {
-    initPluginsRow();
-    return;
-  }
-
   createRow(aPartId);
 
   var checkbox = document.getElementById(aPartId + "Def");
   var command  = document.getElementById("cmd_" + aPartId + "Toggle");
   var {state, scope} = SitePermissions.get(gPermURI, aPartId);
   let defaultState = SitePermissions.getDefault(aPartId);
 
+  // When flash permission state is "Hide", we show it as "Always Ask" in page info.
+  if (aPartId.startsWith("plugin") && state == SitePermissions.PROMPT_HIDE) {
+    defaultState == SitePermissions.UNKNOWN ? state = defaultState : state = SitePermissions.PROMPT;
+  }
+
   if (state != defaultState) {
     checkbox.checked = false;
     command.removeAttribute("disabled");
   } else {
     checkbox.checked = true;
     command.setAttribute("disabled", "true");
   }
 
@@ -153,112 +149,21 @@ function onCheckboxClick(aPartId) {
     var perm = SitePermissions.getDefault(aPartId);
     setRadioState(aPartId, perm);
   } else {
     onRadioClick(aPartId);
     command.removeAttribute("disabled");
   }
 }
 
-function onPluginRadioClick(aEvent) {
-  onRadioClick(aEvent.originalTarget.getAttribute("id").split("#")[0]);
-}
-
 function onRadioClick(aPartId) {
   var radioGroup = document.getElementById(aPartId + "RadioGroup");
   var id = radioGroup.selectedItem.id;
   var permission = parseInt(id.split("#")[1]);
   SitePermissions.set(gPermURI, aPartId, permission);
 }
 
 function setRadioState(aPartId, aValue) {
   var radio = document.getElementById(aPartId + "#" + aValue);
   if (radio) {
     radio.radioGroup.selectedItem = radio;
   }
 }
-
-function fillInPluginPermissionTemplate(aPluginName, aPermissionString) {
-  let permPluginTemplate = document.getElementById("permPluginTemplate").cloneNode(true);
-  permPluginTemplate.setAttribute("permString", aPermissionString);
-  let attrs = [
-    [ ".permPluginTemplateLabel", "value", aPluginName ],
-    [ ".permPluginTemplateRadioGroup", "id", aPermissionString + "RadioGroup" ],
-    [ ".permPluginTemplateRadioDefault", "id", aPermissionString + "#0" ],
-    [ ".permPluginTemplateRadioAsk", "id", aPermissionString + "#3" ],
-    [ ".permPluginTemplateRadioAllow", "id", aPermissionString + "#1" ],
-    // #8 comes from Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET
-    [ ".permPluginTemplateRadioHide", "id", aPermissionString + "#8"],
-    [ ".permPluginTemplateRadioBlock", "id", aPermissionString + "#2" ]
-  ];
-
-  for (let attr of attrs) {
-    permPluginTemplate.querySelector(attr[0]).setAttribute(attr[1], attr[2]);
-  }
-
-  return permPluginTemplate;
-}
-
-function clearPluginPermissionTemplate() {
-  let permPluginTemplate = document.getElementById("permPluginTemplate");
-  permPluginTemplate.hidden = true;
-  permPluginTemplate.removeAttribute("permString");
-  document.querySelector(".permPluginTemplateLabel").removeAttribute("value");
-  document.querySelector(".permPluginTemplateRadioGroup").removeAttribute("id");
-  document.querySelector(".permPluginTemplateRadioAsk").removeAttribute("id");
-  document.querySelector(".permPluginTemplateRadioAllow").removeAttribute("id");
-  document.querySelector(".permPluginTemplateRadioBlock").removeAttribute("id");
-}
-
-function initPluginsRow() {
-  let vulnerableLabel = document.getElementById("browserBundle").getString("pluginActivateVulnerable.label");
-  let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
-
-  let permissionMap = new Map();
-
-  for (let plugin of pluginHost.getPluginTags()) {
-    if (plugin.disabled) {
-      continue;
-    }
-    for (let mimeType of plugin.getMimeTypes()) {
-      let permString = pluginHost.getPermissionStringForType(mimeType);
-      if (!permissionMap.has(permString)) {
-        let name = BrowserUtils.makeNicePluginName(plugin.name);
-        if (permString.startsWith("plugin-vulnerable:")) {
-          name += " \u2014 " + vulnerableLabel;
-        }
-        permissionMap.set(permString, name);
-      }
-    }
-  }
-
-  let entries = Array.from(permissionMap, item => ({ name: item[1], permission: item[0] }));
-
-  entries.sort(function(a, b) {
-    return a.name.localeCompare(b.name);
-  });
-
-  let permissionEntries = entries.map(p => fillInPluginPermissionTemplate(p.name, p.permission));
-
-  let permPluginsRow = document.getElementById("perm-plugins-row");
-  clearPluginPermissionTemplate();
-  if (permissionEntries.length < 1) {
-    permPluginsRow.hidden = true;
-    return;
-  }
-
-  for (let permissionEntry of permissionEntries) {
-    permPluginsRow.appendChild(permissionEntry);
-  }
-
-  setPluginsRadioState();
-}
-
-function setPluginsRadioState() {
-  let box = document.getElementById("perm-plugins-row");
-  for (let permissionEntry of box.childNodes) {
-    if (permissionEntry.hasAttribute("permString")) {
-      let permString = permissionEntry.getAttribute("permString");
-      let permission = SitePermissions.get(gPermURI, permString);
-      setRadioState(permString, permission.state);
-    }
-  }
-}
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -91,13 +91,12 @@ tags = blocklist
 skip-if = !e10s
 tags = blocklist
 [browser_enable_DRM_prompt.js]
 [browser_private_browsing_eme_persistent_state.js]
 [browser_globalplugin_crashinfobar.js]
 skip-if = !crashreporter
 [browser_pluginCrashCommentAndURL.js]
 skip-if = !crashreporter
-[browser_pageInfo_plugins.js]
 [browser_pluginCrashReportNonDeterminism.js]
 skip-if = !crashreporter
 [browser_private_clicktoplay.js]
 [browser_subframe_access_hidden_plugins.js]
deleted file mode 100644
--- a/browser/base/content/test/plugins/browser_pageInfo_plugins.js
+++ /dev/null
@@ -1,187 +0,0 @@
-var gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
-var gPageInfo = null;
-var gNextTest = null;
-var gTestBrowser = null;
-var gPluginHost = Cc["@mozilla.org/plugin/host;1"]
-                    .getService(Ci.nsIPluginHost);
-var gTestPermissionString = gPluginHost.getPermissionStringForType("application/x-test");
-var gSecondTestPermissionString = gPluginHost.getPermissionStringForType("application/x-second-test");
-
-function doOnPageLoad(url, continuation) {
-  gNextTest = continuation;
-  BrowserTestUtils.browserLoaded(gTestBrowser).then(pageLoad);
-  gTestBrowser.loadURI(url);
-}
-
-function pageLoad() {
-  // The plugin events are async dispatched and can come after the load event
-  // This just allows the events to fire before we then go on to test the states
-  executeSoon(gNextTest);
-}
-
-function doOnOpenPageInfo(continuation) {
-  Services.obs.addObserver(pageInfoObserve, "page-info-dialog-loaded");
-  gNextTest = continuation;
-  // An explanation: it looks like the test harness complains about leaked
-  // windows if we don't keep a reference to every window we've opened.
-  // So, don't reuse pointers to opened Page Info windows - simply append
-  // to this list.
-  gPageInfo = BrowserPageInfo(null, "permTab");
-}
-
-function pageInfoObserve(win, topic, data) {
-  Services.obs.removeObserver(pageInfoObserve, "page-info-dialog-loaded");
-  gPageInfo.onFinished.push(() => executeSoon(gNextTest));
-}
-
-function finishTest() {
-  Services.perms.remove(makeURI("http://127.0.0.1:8888/"), gTestPermissionString);
-  Services.perms.remove(makeURI("http://127.0.0.1:8888/"), gSecondTestPermissionString);
-  Services.prefs.clearUserPref("plugins.click_to_play");
-  gBrowser.removeCurrentTab();
-
-  gPageInfo = null;
-  gNextTest = null;
-  gTestBrowser = null;
-  gPluginHost = null;
-
-  executeSoon(finish);
-}
-
-function test() {
-  waitForExplicitFinish();
-  Services.prefs.setBoolPref("plugins.click_to_play", true);
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
-  gTestBrowser = gBrowser.selectedBrowser;
-  Services.perms.remove(makeURI("http://127.0.0.1:8888/"), gTestPermissionString);
-  Services.perms.remove(makeURI("http://127.0.0.1:8888/"), gSecondTestPermissionString);
-  doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart1a);
-}
-
-// The first test plugin is CtP and the second test plugin is enabled.
-function testPart1a() {
-  let testElement = gTestBrowser.contentDocumentAsCPOW.getElementById("test");
-  let objLoadingContent = testElement.QueryInterface(Ci.nsIObjectLoadingContent);
-  ok(!objLoadingContent.activated, "part 1a: Test plugin should not be activated");
-  let secondtest = gTestBrowser.contentDocumentAsCPOW.getElementById("secondtestA");
-  objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
-  ok(objLoadingContent.activated, "part 1a: Second Test plugin should be activated");
-
-  doOnOpenPageInfo(testPart1b);
-}
-
-function testPart1b() {
-  let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
-  let testRadioDefault = gPageInfo.document.getElementById(gTestPermissionString + "#0");
-
-  is(testRadioGroup.selectedItem, testRadioDefault, "part 1b: Test radio group should be set to 'Default'");
-  let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
-  testRadioGroup.selectedItem = testRadioAllow;
-  testRadioAllow.doCommand();
-
-  let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
-  let secondtestRadioDefault = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
-  is(secondtestRadioGroup.selectedItem, secondtestRadioDefault, "part 1b: Second Test radio group should be set to 'Default'");
-  let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#3");
-  secondtestRadioGroup.selectedItem = secondtestRadioAsk;
-  secondtestRadioAsk.doCommand();
-
-  doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart2);
-}
-
-// Now, the Test plugin should be allowed, and the Test2 plugin should be CtP
-function testPart2() {
-  let testElement = gTestBrowser.contentDocumentAsCPOW.getElementById("test").
-    QueryInterface(Ci.nsIObjectLoadingContent);
-  ok(testElement.activated, "part 2: Test plugin should be activated");
-
-  let secondtest = gTestBrowser.contentDocumentAsCPOW.getElementById("secondtestA").
-    QueryInterface(Ci.nsIObjectLoadingContent);
-  ok(!secondtest.activated, "part 2: Second Test plugin should not be activated");
-  is(secondtest.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-     "part 2: Second test plugin should be click-to-play.");
-
-  let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
-  let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
-  is(testRadioGroup.selectedItem, testRadioAllow, "part 2: Test radio group should be set to 'Allow'");
-  let testRadioBlock = gPageInfo.document.getElementById(gTestPermissionString + "#2");
-  testRadioGroup.selectedItem = testRadioBlock;
-  testRadioBlock.doCommand();
-
-  let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
-  let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#3");
-  is(secondtestRadioGroup.selectedItem, secondtestRadioAsk, "part 2: Second Test radio group should be set to 'Always Ask'");
-  let secondtestRadioBlock = gPageInfo.document.getElementById(gSecondTestPermissionString + "#2");
-  secondtestRadioGroup.selectedItem = secondtestRadioBlock;
-  secondtestRadioBlock.doCommand();
-
-  doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart3);
-}
-
-// Now, all the things should be blocked
-function testPart3() {
-  let testElement = gTestBrowser.contentDocumentAsCPOW.getElementById("test").
-    QueryInterface(Ci.nsIObjectLoadingContent);
-  ok(!testElement.activated, "part 3: Test plugin should not be activated");
-  is(testElement.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
-    "part 3: Test plugin should be marked as PLUGIN_DISABLED");
-
-  let secondtest = gTestBrowser.contentDocumentAsCPOW.getElementById("secondtestA").
-    QueryInterface(Ci.nsIObjectLoadingContent);
-
-  ok(!secondtest.activated, "part 3: Second Test plugin should not be activated");
-  is(secondtest.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
-     "part 3: Second test plugin should be marked as PLUGIN_DISABLED");
-
-  // reset permissions
-  Services.perms.remove(makeURI("http://127.0.0.1:8888/"), gTestPermissionString);
-  Services.perms.remove(makeURI("http://127.0.0.1:8888/"), gSecondTestPermissionString);
-  // check that changing the permissions affects the radio state in the
-  // open Page Info window
-  let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
-  let testRadioDefault = gPageInfo.document.getElementById(gTestPermissionString + "#0");
-  is(testRadioGroup.selectedItem, testRadioDefault, "part 3: Test radio group should be set to 'Default'");
-  let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
-  let secondtestRadioDefault = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
-  is(secondtestRadioGroup.selectedItem, secondtestRadioDefault, "part 3: Second Test radio group should be set to 'Default'");
-
-  doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart4a);
-}
-
-// Now test that setting permission directly (as from the popup notification)
-// immediately influences Page Info.
-function testPart4a() {
-  // simulate "allow" from the doorhanger
-  Services.perms.add(gTestBrowser.currentURI, gTestPermissionString, Ci.nsIPermissionManager.ALLOW_ACTION);
-  Services.perms.add(gTestBrowser.currentURI, gSecondTestPermissionString, Ci.nsIPermissionManager.ALLOW_ACTION);
-
-  // check (again) that changing the permissions affects the radio state in the
-  // open Page Info window
-  let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
-  let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
-  is(testRadioGroup.selectedItem, testRadioAllow, "part 4a: Test radio group should be set to 'Allow'");
-  let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
-  let secondtestRadioAllow = gPageInfo.document.getElementById(gSecondTestPermissionString + "#1");
-  is(secondtestRadioGroup.selectedItem, secondtestRadioAllow, "part 4a: Second Test radio group should be set to 'Always Allow'");
-
-  // now close Page Info and see that it opens with the right settings
-  gPageInfo.close();
-  doOnOpenPageInfo(testPart4b);
-}
-
-// check that "always allow" resulted in the radio buttons getting set to allow
-function testPart4b() {
-  let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
-  let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
-  is(testRadioGroup.selectedItem, testRadioAllow, "part 4b: Test radio group should be set to 'Allow'");
-
-  let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
-  let secondtestRadioAllow = gPageInfo.document.getElementById(gSecondTestPermissionString + "#1");
-  is(secondtestRadioGroup.selectedItem, secondtestRadioAllow, "part 4b: Second Test radio group should be set to 'Allow'");
-
-  Services.prefs.setBoolPref("plugins.click_to_play", false);
-  gPageInfo.close();
-  finishTest();
-}
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -13,23 +13,21 @@ var isDevtools = SimpleTest.harnessParam
 var gExceptionPaths = [
   "chrome://browser/content/defaultthemes/",
   "chrome://browser/locale/searchplugins/",
   "resource://app/defaults/blocklists/",
   "resource://app/defaults/pinning/",
   "resource://app/defaults/preferences/",
   "resource://gre/modules/commonjs/",
   "resource://gre/defaults/pref/",
-  "resource://shield-recipe-client/node_modules/jexl/lib/",
 
   // These resources are referenced using relative paths from html files.
   "resource://payments/",
-
-  // https://github.com/mozilla/normandy/issues/577
-  "resource://shield-recipe-client/test/",
+  "resource://normandy-content/shield-content-frame.js",
+  "resource://normandy-content/shield-content-process.js",
 
   // https://github.com/mozilla/activity-stream/issues/3053
   "resource://activity-stream/data/content/tippytop/images/",
   // https://github.com/mozilla/activity-stream/issues/3758
   "resource://activity-stream/prerendered/",
 
   // browser/extensions/pdfjs/content/build/pdf.js#1999
   "resource://pdf.js/web/images/",
@@ -119,20 +117,16 @@ var whitelist = [
   {file: "resource://gre/chrome/en-US/locale/en-US/global-platform/win/intl.properties",
    platforms: ["linux", "macosx"]},
   {file: "resource://gre/chrome/en-US/locale/en-US/global-platform/win/platformKeys.properties",
    platforms: ["linux", "macosx"]},
 
   // browser/extensions/pdfjs/content/web/viewer.js#7450
   {file: "resource://pdf.js/web/debugger.js"},
 
-  // These are used in content processes. They are actually referenced.
-  {file: "resource://shield-recipe-client-content/shield-content-frame.js"},
-  {file: "resource://shield-recipe-client-content/shield-content-process.js"},
-
   // Starting from here, files in the whitelist are bugs that need fixing.
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
   // Bug 1356031 (only used by devtools)
   {file: "chrome://global/skin/icons/error-16.png"},
   // Bug 1348362
   {file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux"]},
--- a/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_shield.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_shield.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 add_task(async function test_policy_disable_shield() {
-  const { RecipeRunner } = ChromeUtils.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", {});
+  const { RecipeRunner } = ChromeUtils.import("resource://normandy/lib/RecipeRunner.jsm", {});
 
-  await SpecialPowers.pushPrefEnv({ set: [["extensions.shield-recipe-client.api_url",
-                                            "https://localhost/selfsupport-dummy/"],
+  await SpecialPowers.pushPrefEnv({ set: [["app.normandy.api_url",
+                                           "https://localhost/selfsupport-dummy/"],
                                           ["datareporting.healthreport.uploadEnabled",
                                             true]]});
 
   ok(RecipeRunner, "RecipeRunner exists");
   RecipeRunner.checkPrefs();
   is(RecipeRunner.enabled, true, "RecipeRunner is enabled");
 
   await setupPolicyEngineWithJson({
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -105,16 +105,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   Integration: "resource://gre/modules/Integration.jsm",
   L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
   LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   LoginHelper: "resource://gre/modules/LoginHelper.jsm",
   LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
+  Normandy: "resource://normandy/Normandy.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PageActions: "resource:///modules/PageActions.jsm",
   PageThumbs: "resource://gre/modules/PageThumbs.jsm",
   PdfJs: "resource://pdf.js/PdfJs.jsm",
   PermissionUI: "resource:///modules/PermissionUI.jsm",
   PingCentre: "resource:///modules/PingCentre.jsm",
   PlacesBackups: "resource://gre/modules/PlacesBackups.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
@@ -705,16 +706,17 @@ BrowserGlue.prototype = {
       name: gBrowserBundle.GetStringFromName("darkTheme.name"),
       description: gBrowserBundle.GetStringFromName("darkTheme.description"),
       iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
       textcolor: "white",
       accentcolor: "black",
       author: vendorShortName,
     });
 
+    Normandy.init();
 
     // Initialize the default l10n resource sources for L10nRegistry.
     let locales = Services.locale.getPackagedLocales();
     const appSource = new FileSource("app", locales, "resource://app/localization/{locale}/");
     L10nRegistry.registerSource(appSource);
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete");
   },
@@ -1051,16 +1053,18 @@ BrowserGlue.prototype = {
     NewTabUtils.uninit();
     AutoCompletePopup.uninit();
     DateTimePickerHelper.uninit();
 
     // Browser errors are only collected on Nightly
     if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_DATA_REPORTING) {
       this.browserErrorReporter.uninit();
     }
+
+    Normandy.uninit();
   },
 
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     if (this._windowsWereRestored) {
       return;
     }
     this._windowsWereRestored = true;
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -8,17 +8,16 @@ DIRS += [
     'activity-stream',
     'aushelper',
     'followonsearch',
     'formautofill',
     'onboarding',
     'pdfjs',
     'pocket',
     'screenshots',
-    'shield-recipe-client',
     'webcompat',
 ]
 
 # Only include the following system add-ons if building Aurora or Nightly
 if not CONFIG['RELEASE_OR_BETA']:
     DIRS += [
         'presentation',
     ]
deleted file mode 100644
--- a/browser/extensions/shield-recipe-client/bootstrap.js
+++ /dev/null
@@ -1,227 +0,0 @@
-/* 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/. */
-"use strict";
-
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/Log.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "LogManager",
-  "resource://shield-recipe-client/lib/LogManager.jsm");
-ChromeUtils.defineModuleGetter(this, "ShieldRecipeClient",
-  "resource://shield-recipe-client/lib/ShieldRecipeClient.jsm");
-ChromeUtils.defineModuleGetter(this, "PreferenceExperiments",
-  "resource://shield-recipe-client/lib/PreferenceExperiments.jsm");
-
-// Act as both a normal bootstrap.js and a JS module so that we can test
-// startup methods without having to install/uninstall the add-on.
-var EXPORTED_SYMBOLS = ["Bootstrap"];
-
-const REASON_APP_STARTUP = 1;
-const UI_AVAILABLE_NOTIFICATION = "sessionstore-windows-restored";
-const STARTUP_EXPERIMENT_PREFS_BRANCH = "extensions.shield-recipe-client.startupExperimentPrefs.";
-const PREF_LOGGING_LEVEL = "extensions.shield-recipe-client.logging.level";
-const BOOTSTRAP_LOGGER_NAME = "extensions.shield-recipe-client.bootstrap";
-const DEFAULT_PREFS = {
-  "extensions.shield-recipe-client.api_url": "https://normandy.cdn.mozilla.net/api/v1",
-  "extensions.shield-recipe-client.dev_mode": false,
-  "extensions.shield-recipe-client.enabled": true,
-  "extensions.shield-recipe-client.startup_delay_seconds": 300,
-  "extensions.shield-recipe-client.logging.level": Log.Level.Warn,
-  "extensions.shield-recipe-client.user_id": "",
-  "extensions.shield-recipe-client.run_interval_seconds": 86400, // 24 hours
-  "extensions.shield-recipe-client.first_run": true,
-  "extensions.shield-recipe-client.shieldLearnMoreUrl": (
-    "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield"
-  ),
-  "app.shield.optoutstudies.enabled": AppConstants.MOZ_DATA_REPORTING,
-};
-
-// Logging
-const log = Log.repository.getLogger(BOOTSTRAP_LOGGER_NAME);
-log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
-log.level = Services.prefs.getIntPref(PREF_LOGGING_LEVEL, Log.Level.Warn);
-
-let studyPrefsChanged = {};
-
-var Bootstrap = {
-  initShieldPrefs(defaultPrefs) {
-    const prefBranch = Services.prefs.getDefaultBranch("");
-    for (const [name, value] of Object.entries(defaultPrefs)) {
-      switch (typeof value) {
-        case "string":
-          prefBranch.setCharPref(name, value);
-          break;
-        case "number":
-          prefBranch.setIntPref(name, value);
-          break;
-        case "boolean":
-          prefBranch.setBoolPref(name, value);
-          break;
-        default:
-          throw new Error(`Invalid default preference type ${typeof value}`);
-      }
-    }
-  },
-
-  initExperimentPrefs() {
-    studyPrefsChanged = {};
-    const defaultBranch = Services.prefs.getDefaultBranch("");
-    const experimentBranch = Services.prefs.getBranch(STARTUP_EXPERIMENT_PREFS_BRANCH);
-
-    for (const prefName of experimentBranch.getChildList("")) {
-      const experimentPrefType = experimentBranch.getPrefType(prefName);
-      const realPrefType = defaultBranch.getPrefType(prefName);
-
-      if (realPrefType !== Services.prefs.PREF_INVALID && realPrefType !== experimentPrefType) {
-        log.error(`Error setting startup pref ${prefName}; pref type does not match.`);
-        continue;
-      }
-
-      // record the value of the default branch before setting it
-      try {
-        switch (realPrefType) {
-          case Services.prefs.PREF_STRING:
-            studyPrefsChanged[prefName] = defaultBranch.getCharPref(prefName);
-            break;
-
-          case Services.prefs.PREF_INT:
-            studyPrefsChanged[prefName] = defaultBranch.getIntPref(prefName);
-            break;
-
-          case Services.prefs.PREF_BOOL:
-            studyPrefsChanged[prefName] = defaultBranch.getBoolPref(prefName);
-            break;
-
-          case Services.prefs.PREF_INVALID:
-            studyPrefsChanged[prefName] = null;
-            break;
-
-          default:
-            // This should never happen
-            log.error(`Error getting startup pref ${prefName}; unknown value type ${experimentPrefType}.`);
-        }
-      } catch (e) {
-        if (e.result === Cr.NS_ERROR_UNEXPECTED) {
-          // There is a value for the pref on the user branch but not on the default branch. This is ok.
-          studyPrefsChanged[prefName] = null;
-        } else {
-          // rethrow
-          throw e;
-        }
-      }
-
-      // now set the new default value
-      switch (experimentPrefType) {
-        case Services.prefs.PREF_STRING:
-          defaultBranch.setCharPref(prefName, experimentBranch.getCharPref(prefName));
-          break;
-
-        case Services.prefs.PREF_INT:
-          defaultBranch.setIntPref(prefName, experimentBranch.getIntPref(prefName));
-          break;
-
-        case Services.prefs.PREF_BOOL:
-          defaultBranch.setBoolPref(prefName, experimentBranch.getBoolPref(prefName));
-          break;
-
-        case Services.prefs.PREF_INVALID:
-          // This should never happen.
-          log.error(`Error setting startup pref ${prefName}; pref type is invalid (${experimentPrefType}).`);
-          break;
-
-        default:
-          // This should never happen either.
-          log.error(`Error getting startup pref ${prefName}; unknown value type ${experimentPrefType}.`);
-      }
-    }
-  },
-
-  observe(subject, topic, data) {
-    if (topic === UI_AVAILABLE_NOTIFICATION) {
-      Services.obs.removeObserver(this, UI_AVAILABLE_NOTIFICATION);
-      this.finishStartup();
-    }
-  },
-
-  install() {
-    // Nothing to do during install
-  },
-
-  startup(data, reason) {
-    // Initialization that needs to happen before the first paint on startup.
-    this.initShieldPrefs(DEFAULT_PREFS);
-    this.initExperimentPrefs();
-
-    // If the app is starting up, wait until the UI is available before finishing
-    // init.
-    if (reason === REASON_APP_STARTUP) {
-      Services.obs.addObserver(this, UI_AVAILABLE_NOTIFICATION);
-    } else {
-      this.finishStartup();
-    }
-  },
-
-  async finishStartup() {
-    await PreferenceExperiments.recordOriginalValues(studyPrefsChanged);
-    ShieldRecipeClient.startup();
-  },
-
-  async shutdown(data, reason) {
-    // Wait for async write operations during shutdown before unloading modules.
-    await ShieldRecipeClient.shutdown(reason);
-
-    // In case the observer didn't run, clean it up.
-    try {
-      Services.obs.removeObserver(this, UI_AVAILABLE_NOTIFICATION);
-    } catch (err) {
-      // It must already be removed!
-    }
-
-    // Unload add-on modules. We don't do this in ShieldRecipeClient so that
-    // modules are not unloaded accidentally during tests.
-    let modules = [
-      "lib/ActionSandboxManager.jsm",
-      "lib/Addons.jsm",
-      "lib/AddonStudies.jsm",
-      "lib/CleanupManager.jsm",
-      "lib/ClientEnvironment.jsm",
-      "lib/FilterExpressions.jsm",
-      "lib/EventEmitter.jsm",
-      "lib/Heartbeat.jsm",
-      "lib/LogManager.jsm",
-      "lib/NormandyApi.jsm",
-      "lib/NormandyDriver.jsm",
-      "lib/PreferenceExperiments.jsm",
-      "lib/RecipeRunner.jsm",
-      "lib/Sampling.jsm",
-      "lib/SandboxManager.jsm",
-      "lib/ShieldPreferences.jsm",
-      "lib/ShieldRecipeClient.jsm",
-      "lib/Storage.jsm",
-      "lib/TelemetryEvents.jsm",
-      "lib/Uptake.jsm",
-      "lib/Utils.jsm",
-    ].map(m => `resource://shield-recipe-client/${m}`);
-    modules = modules.concat([
-      "resource://shield-recipe-client-content/AboutPages.jsm",
-      "resource://shield-recipe-client-vendor/mozjexl.js",
-    ]);
-
-    for (const module of modules) {
-      log.debug(`Unloading ${module}`);
-      Cu.unload(module);
-    }
-  },
-
-  uninstall() {
-    // Do nothing
-  },
-};
-
-// Expose bootstrap methods on the global
-for (const methodName of ["install", "startup", "shutdown", "uninstall"]) {
-  this[methodName] = Bootstrap[methodName].bind(Bootstrap);
-}
deleted file mode 100644
--- a/browser/extensions/shield-recipe-client/install.rdf.in
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-#filter substitution
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>shield-recipe-client@mozilla.org</em:id>
-    <em:type>2</em:type>
-    <em:bootstrap>true</em:bootstrap>
-    <em:unpack>false</em:unpack>
-    <em:version>83</em:version>
-    <em:name>Shield Recipe Client</em:name>
-    <em:description>Client to download and run recipes for SHIELD, Heartbeat, etc.</em:description>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
-        <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-  </Description>
-</RDF>
deleted file mode 100644
--- a/browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm
+++ /dev/null
@@ -1,86 +0,0 @@
-/* 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/. */
-"use strict";
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/Log.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "LogManager",
-  "resource://shield-recipe-client/lib/LogManager.jsm");
-ChromeUtils.defineModuleGetter(this, "RecipeRunner",
-  "resource://shield-recipe-client/lib/RecipeRunner.jsm");
-ChromeUtils.defineModuleGetter(this, "CleanupManager",
-  "resource://shield-recipe-client/lib/CleanupManager.jsm");
-ChromeUtils.defineModuleGetter(this, "PreferenceExperiments",
-  "resource://shield-recipe-client/lib/PreferenceExperiments.jsm");
-ChromeUtils.defineModuleGetter(this, "AboutPages",
-  "resource://shield-recipe-client-content/AboutPages.jsm");
-ChromeUtils.defineModuleGetter(this, "ShieldPreferences",
-  "resource://shield-recipe-client/lib/ShieldPreferences.jsm");
-ChromeUtils.defineModuleGetter(this, "AddonStudies",
-  "resource://shield-recipe-client/lib/AddonStudies.jsm");
-ChromeUtils.defineModuleGetter(this, "TelemetryEvents",
-  "resource://shield-recipe-client/lib/TelemetryEvents.jsm");
-
-var EXPORTED_SYMBOLS = ["ShieldRecipeClient"];
-
-const PREF_LOGGING_LEVEL = "extensions.shield-recipe-client.logging.level";
-const SHIELD_INIT_NOTIFICATION = "shield-init-complete";
-
-let log = null;
-
-/**
- * Handles startup and shutdown of the entire add-on. Bootsrap.js defers to this
- * module for most tasks so that we can more easily test startup and shutdown
- * (bootstrap.js is difficult to import in tests).
- */
-var ShieldRecipeClient = {
-  async startup() {
-    // Setup logging and listen for changes to logging prefs
-    LogManager.configure(Services.prefs.getIntPref(PREF_LOGGING_LEVEL));
-    Services.prefs.addObserver(PREF_LOGGING_LEVEL, LogManager.configure);
-    CleanupManager.addCleanupHandler(
-      () => Services.prefs.removeObserver(PREF_LOGGING_LEVEL, LogManager.configure),
-    );
-    log = LogManager.getLogger("bootstrap");
-
-    try {
-      TelemetryEvents.init();
-    } catch (err) {
-      log.error("Failed to initialize telemetry events:", err);
-    }
-
-    try {
-      await AboutPages.init();
-    } catch (err) {
-      log.error("Failed to initialize about pages:", err);
-    }
-
-    try {
-      await AddonStudies.init();
-    } catch (err) {
-      log.error("Failed to initialize addon studies:", err);
-    }
-
-    try {
-      await PreferenceExperiments.init();
-    } catch (err) {
-      log.error("Failed to initialize preference experiments:", err);
-    }
-
-    try {
-      ShieldPreferences.init();
-    } catch (err) {
-      log.error("Failed to initialize preferences UI:", err);
-    }
-
-    await RecipeRunner.init();
-    Services.obs.notifyObservers(null, SHIELD_INIT_NOTIFICATION);
-  },
-
-  async shutdown(reason) {
-    await CleanupManager.cleanup();
-  },
-};
deleted file mode 100644
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ShieldRecipeClient.js
+++ /dev/null
@@ -1,88 +0,0 @@
-"use strict";
-
-ChromeUtils.import("resource://shield-recipe-client/lib/ShieldRecipeClient.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client-content/AboutPages.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/TelemetryEvents.jsm", this);
-
-function withStubInits(testFunction) {
-  return decorate(
-    withStub(AboutPages, "init"),
-    withStub(AddonStudies, "init"),
-    withStub(PreferenceExperiments, "init"),
-    withStub(RecipeRunner, "init"),
-    withStub(TelemetryEvents, "init"),
-    testFunction
-  );
-}
-
-decorate_task(
-  withStubInits,
-  async function testStartup() {
-    const initObserved = TestUtils.topicObserved("shield-init-complete");
-    await ShieldRecipeClient.startup();
-    ok(AboutPages.init.called, "startup calls AboutPages.init");
-    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
-    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
-    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
-    await initObserved;
-  }
-);
-
-decorate_task(
-  withStubInits,
-  async function testStartupPrefInitFail() {
-    PreferenceExperiments.init.returns(Promise.reject(new Error("oh no")));
-
-    await ShieldRecipeClient.startup();
-    ok(AboutPages.init.called, "startup calls AboutPages.init");
-    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
-    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
-    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
-    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
-  }
-);
-
-decorate_task(
-  withStubInits,
-  async function testStartupAboutPagesInitFail() {
-    AboutPages.init.returns(Promise.reject(new Error("oh no")));
-
-    await ShieldRecipeClient.startup();
-    ok(AboutPages.init.called, "startup calls AboutPages.init");
-    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
-    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
-    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
-    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
-  }
-);
-
-decorate_task(
-  withStubInits,
-  async function testStartupAddonStudiesInitFail() {
-    AddonStudies.init.returns(Promise.reject(new Error("oh no")));
-
-    await ShieldRecipeClient.startup();
-    ok(AboutPages.init.called, "startup calls AboutPages.init");
-    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
-    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
-    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
-    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
-  }
-);
-
-decorate_task(
-  withStubInits,
-  async function testStartupTelemetryEventsInitFail() {
-    TelemetryEvents.init.throws();
-
-    await ShieldRecipeClient.startup();
-    ok(AboutPages.init.called, "startup calls AboutPages.init");
-    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
-    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
-    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
-    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
-  }
-);
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -230,16 +230,17 @@ These should match what Safari and other
 <!ENTITY urlbar.cameraBlocked.tooltip            "You have blocked your camera for this website.">
 <!ENTITY urlbar.microphoneBlocked.tooltip        "You have blocked your microphone for this website.">
 <!ENTITY urlbar.screenBlocked.tooltip            "You have blocked this website from sharing your screen.">
 <!ENTITY urlbar.geolocationBlocked.tooltip       "You have blocked location information for this website.">
 <!ENTITY urlbar.webNotificationsBlocked.tooltip  "You have blocked notifications for this website.">
 <!ENTITY urlbar.persistentStorageBlocked.tooltip "You have blocked persistent storage for this website.">
 <!ENTITY urlbar.popupBlocked.tooltip             "You have blocked pop-ups for this website.">
 <!ENTITY urlbar.canvasBlocked.tooltip            "You have blocked canvas data extraction for this website.">
+<!ENTITY urlbar.flashPluginBlocked.tooltip       "You have blocked this website from using the Adobe Flash plugin.">
 <!ENTITY urlbar.midiBlocked.tooltip              "You have blocked MIDI access for this website.">
 
 <!ENTITY urlbar.openHistoryPopup.tooltip                "Show history">
 
 <!ENTITY searchItem.title             "Search">
 
 <!-- Toolbar items -->
 <!ENTITY homeButton.label             "Home">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -313,31 +313,20 @@ pluginActivationWarning.message=This sit
 
 pluginActivate.learnMore=Learn More…
 # LOCALIZATION NOTE (pluginActivateOutdated.message, pluginActivateOutdated.label):
 # These strings are used when an unsafe plugin has an update available.
 # %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
 pluginActivateOutdated.message=%3$S has prevented the outdated plugin “%1$S” from running on %2$S.
 pluginActivateOutdated.label=Outdated plugin
 pluginActivate.updateLabel=Update now…
-# LOCALIZATION NOTE (pluginActivateVulnerable.message, pluginActivateVulnerable.label):
-# These strings are used when an unsafe plugin has no update available.
+# LOCALIZATION NOTE (pluginActivateVulnerable.message):
+# This string is used when an unsafe plugin has no update available.
 # %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
 pluginActivateVulnerable.message=%3$S has prevented the unsafe plugin “%1$S” from running on %2$S.
-pluginActivateVulnerable.label=Vulnerable plugin!
-pluginActivate.riskLabel=What’s the risk?
-# LOCALIZATION NOTE (pluginActivateBlocked.message): %1$S is the plugin name, %2$S is brandShortName
-pluginActivateBlocked.message=%2$S has blocked “%1$S” for your protection.
-pluginActivateBlocked.label=Blocked for your protection
-pluginActivateDisabled.message=“%S” is disabled.
-pluginActivateDisabled.label=Disabled
-pluginActivateDisabled.manage=Manage plugins…
-pluginEnabled.message=“%S” is enabled on %S.
-pluginEnabledOutdated.message=Outdated plugin “%S” is enabled on %S.
-pluginEnabledVulnerable.message=Insecure plugin “%S” is enabled on %S.
 pluginInfo.unknownPlugin=Unknown
 
 # LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.dtd
 # LOCALIZATION NOTE (pluginActivateNow.label): This button will enable the
 # plugin in the current session for an short time (about an hour), auto-renewed
 # if the site keeps using the plugin.
 pluginActivateNow.label=Allow Now
 pluginActivateNow.accesskey=N
--- a/browser/locales/en-US/chrome/browser/pageInfo.dtd
+++ b/browser/locales/en-US/chrome/browser/pageInfo.dtd
@@ -44,24 +44,17 @@
 <!ENTITY  mediaSaveAs2.accesskey "e">
 <!ENTITY  mediaPreview          "Media Preview:">
 
 <!ENTITY  feedTab               "Feeds">
 <!ENTITY  feedTab.accesskey     "F">
 
 <!ENTITY  permTab               "Permissions">
 <!ENTITY  permTab.accesskey     "P">
-<!ENTITY  permUseDefault        "Use Default">
-<!ENTITY  permAskAlways         "Always ask">
-<!ENTITY  permAllow             "Allow">
-<!ENTITY  permAllowSession      "Allow for Session">
-<!ENTITY  permHide              "Hide">
-<!ENTITY  permBlock             "Block">
 <!ENTITY  permissionsFor        "Permissions for:">
-<!ENTITY  permPlugins           "Activate Plugins">
 
 <!ENTITY  securityTab           "Security">
 <!ENTITY  securityTab.accesskey "S">
 <!ENTITY  securityView.certView "View Certificate">
 <!ENTITY  securityView.accesskey "V">
 <!ENTITY  securityView.unknown   "Unknown">
 
 
--- a/browser/locales/en-US/chrome/browser/sitePermissions.properties
+++ b/browser/locales/en-US/chrome/browser/sitePermissions.properties
@@ -1,25 +1,27 @@
 # 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/.
 
 # LOCALIZATION NOTE (state.current.allowed,
 #                    state.current.allowedForSession,
 #                    state.current.allowedTemporarily,
 #                    state.current.blockedTemporarily,
-#                    state.current.blocked):
+#                    state.current.blocked,
+#                    state.current.hide):
 # This label is used to display active permission states in the site
 # identity popup (which does not have a lot of screen space).
 state.current.allowed = Allowed
 state.current.allowedForSession = Allowed for Session
 state.current.allowedTemporarily = Allowed Temporarily
 state.current.blockedTemporarily = Blocked Temporarily
 state.current.blocked = Blocked
 state.current.prompt = Always Ask
+state.current.hide = Hide Prompt
 
 # LOCALIZATION NOTE (state.multichoice.alwaysAsk,
 #                    state.multichoice.allow,
 #                    state.multichoice.allowForSession,
 #                    state.multichoice.block):
 # Used to label permission state checkboxes in the page info dialog.
 state.multichoice.alwaysAsk = Always Ask
 state.multichoice.allow = Allow
@@ -34,10 +36,11 @@ permission.microphone.label = Use the Mi
 permission.screen.label = Share the Screen
 permission.install.label = Install Add-ons
 permission.popup.label = Open Pop-up Windows
 permission.geo.label = Access Your Location
 permission.shortcuts.label = Override Keyboard Shortcuts
 permission.focus-tab-by-prompt.label = Switch to this Tab
 permission.persistent-storage.label = Store Data in Persistent Storage
 permission.canvas.label = Extract Canvas Data
+permission.flash-plugin.label = Run Adobe Flash
 permission.midi.label = Access MIDI Devices
 permission.midi-sysex.label = Access MIDI Devices with SysEx Support
\ No newline at end of file
deleted file mode 100644
--- a/browser/modules/CastingApps.jsm
+++ /dev/null
@@ -1,162 +0,0 @@
-// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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/. */
-"use strict";
-var EXPORTED_SYMBOLS = ["CastingApps"];
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/SimpleServiceDiscovery.jsm");
-
-
-var CastingApps = {
-  _sendEventToVideo(element, data) {
-    let event = element.ownerDocument.createEvent("CustomEvent");
-    event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(data));
-    element.dispatchEvent(event);
-  },
-
-  makeURI(url, charset, baseURI) {
-    return Services.io.newURI(url, charset, baseURI);
-  },
-
-  getVideo(element) {
-    if (!element) {
-      return null;
-    }
-
-    let extensions = SimpleServiceDiscovery.getSupportedExtensions();
-    let types = SimpleServiceDiscovery.getSupportedMimeTypes();
-
-    // Grab the poster attribute from the <video>
-    let posterURL = element.poster;
-
-    // First, look to see if the <video> has a src attribute
-    let sourceURL = element.src;
-
-    // If empty, try the currentSrc
-    if (!sourceURL) {
-      sourceURL = element.currentSrc;
-    }
-
-    if (sourceURL) {
-      // Use the file extension to guess the mime type
-      let sourceURI = this.makeURI(sourceURL, null, this.makeURI(element.baseURI));
-      if (this.allowableExtension(sourceURI, extensions)) {
-        return { element, source: sourceURI.spec, poster: posterURL, sourceURI};
-      }
-    }
-
-    // Next, look to see if there is a <source> child element that meets
-    // our needs
-    let sourceNodes = element.getElementsByTagName("source");
-    for (let sourceNode of sourceNodes) {
-      let sourceURI = this.makeURI(sourceNode.src, null, this.makeURI(sourceNode.baseURI));
-
-      // Using the type attribute is our ideal way to guess the mime type. Otherwise,
-      // fallback to using the file extension to guess the mime type
-      if (this.allowableMimeType(sourceNode.type, types) || this.allowableExtension(sourceURI, extensions)) {
-        return { element, source: sourceURI.spec, poster: posterURL, sourceURI, type: sourceNode.type };
-      }
-    }
-
-    return null;
-  },
-
-  sendVideoToService(videoElement, service) {
-    if (!service)
-      return;
-
-    let video = this.getVideo(videoElement);
-    if (!video) {
-      return;
-    }
-
-    // Make sure we have a player app for the given service
-    let app = SimpleServiceDiscovery.findAppForService(service);
-    if (!app)
-      return;
-
-    video.title = videoElement.ownerGlobal.top.document.title;
-    if (video.element) {
-      // If the video is currently playing on the device, pause it
-      if (!video.element.paused) {
-        video.element.pause();
-      }
-    }
-
-    app.stop(() => {
-      app.start(started => {
-        if (!started) {
-          Cu.reportError("CastingApps: Unable to start app");
-          return;
-        }
-
-        app.remoteMedia(remoteMedia => {
-          if (!remoteMedia) {
-            Cu.reportError("CastingApps: Failed to create remotemedia");
-            return;
-          }
-
-          this.session = {
-            service,
-            app,
-            remoteMedia,
-            data: {
-              title: video.title,
-              source: video.source,
-              poster: video.poster
-            },
-            videoRef: Cu.getWeakReference(video.element)
-          };
-        }, this);
-      });
-    });
-  },
-
-  getServicesForVideo(videoElement) {
-    let video = this.getVideo(videoElement);
-    if (!video) {
-      return {};
-    }
-
-    let filteredServices = SimpleServiceDiscovery.services.filter(service => {
-      return this.allowableExtension(video.sourceURI, service.extensions) ||
-             this.allowableMimeType(video.type, service.types);
-    });
-
-    return filteredServices;
-  },
-
-  getServicesForMirroring() {
-    return SimpleServiceDiscovery.services.filter(service => service.mirror);
-  },
-
-  // RemoteMedia callback API methods
-  onRemoteMediaStart(remoteMedia) {
-    if (!this.session) {
-      return;
-    }
-
-    remoteMedia.load(this.session.data);
-
-    let video = this.session.videoRef.get();
-    if (video) {
-      this._sendEventToVideo(video, { active: true });
-    }
-  },
-
-  onRemoteMediaStop(remoteMedia) {
-  },
-
-  onRemoteMediaStatus(remoteMedia) {
-  },
-
-  allowableExtension(uri, extensions) {
-    return (uri instanceof Ci.nsIURL) && extensions.includes(uri.fileExtension);
-  },
-
-  allowableMimeType(type, types) {
-    return types.includes(type);
-  }
-};
--- a/browser/modules/ContextMenu.jsm
+++ b/browser/modules/ContextMenu.jsm
@@ -10,17 +10,16 @@ var EXPORTED_SYMBOLS = ["ContextMenu"];
 
 Cu.importGlobalProperties(["URL"]);
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   E10SUtils: "resource://gre/modules/E10SUtils.jsm",
-  CastingApps: "resource:///modules/CastingApps.jsm",
   BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
   PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
   findCssSelector: "resource://gre/modules/css-selector.js",
   SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
   LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -133,21 +133,24 @@ const TemporaryBlockedPermissions = {
  * A module to manage permanent and temporary permissions
  * by URI and browser.
  *
  * Some methods have the side effect of dispatching a "PermissionStateChange"
  * event on changes to temporary permissions, as mentioned in the respective docs.
  */
 var SitePermissions = {
   // Permission states.
+  // PROMPT_HIDE state is only used to show the "Hide Prompt" state in the identity panel
+  // for the "plugin:flash" permission and not in pageinfo.
   UNKNOWN: Services.perms.UNKNOWN_ACTION,
   ALLOW: Services.perms.ALLOW_ACTION,
   BLOCK: Services.perms.DENY_ACTION,
   PROMPT: Services.perms.PROMPT_ACTION,
   ALLOW_COOKIES_FOR_SESSION: Ci.nsICookiePermission.ACCESS_SESSION,
+  PROMPT_HIDE: Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET,
 
   // Permission scopes.
   SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
   SCOPE_TEMPORARY: "{SitePermissions.SCOPE_TEMPORARY}",
   SCOPE_SESSION: "{SitePermissions.SCOPE_SESSION}",
   SCOPE_PERSISTENT: "{SitePermissions.SCOPE_PERSISTENT}",
   SCOPE_POLICY: "{SitePermissions.SCOPE_POLICY}",
 
@@ -533,23 +536,31 @@ var SitePermissions = {
     }
   },
 
   /**
    * Returns the localized label for a permission's current state.
    *
    * @param {SitePermissions state} state
    *        The state to get the label for.
+   * @param {string} id
+   *        The permission to get the state label for.
    * @param {SitePermissions scope} scope (optional)
    *        The scope to get the label for.
    *
    * @return {String|null} the localized label or null if an
    *         unknown state was passed.
    */
-  getCurrentStateLabel(state, scope = null) {
+  getCurrentStateLabel(state, id, scope = null) {
+    // We try to avoid a collision between SitePermissions.PROMPT_HIDE
+    // and SitePermissions.ALLOW_COOKIES_FOR_SESSION which share the same const value.
+    if (id.startsWith("plugin") && state == SitePermissions.PROMPT_HIDE) {
+      return gStringBundle.GetStringFromName("state.current.hide");
+    }
+
     switch (state) {
       case this.PROMPT:
         return gStringBundle.GetStringFromName("state.current.prompt");
       case this.ALLOW:
         if (scope && scope != this.SCOPE_PERSISTENT && scope != this.SCOPE_POLICY)
           return gStringBundle.GetStringFromName("state.current.allowedTemporarily");
         return gStringBundle.GetStringFromName("state.current.allowed");
       case this.ALLOW_COOKIES_FOR_SESSION:
@@ -580,16 +591,18 @@ var gPermissionObject = {
    *
    *  - labelID
    *    Use the given ID instead of the permission name for looking up strings.
    *    e.g. "desktop-notification2" to use permission.desktop-notification2.label
    *
    *  - states
    *    Array of permission states to be exposed to the user.
    *    Defaults to ALLOW, BLOCK and the default state (see getDefault).
+   *    The PROMPT_HIDE state is deliberately excluded from "plugin:flash" since we
+   *    don't want to expose a "Hide Prompt" button to the user through pageinfo.
    */
 
   "image": {
     states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
   },
 
   "cookie": {
     states: [ SitePermissions.ALLOW, SitePermissions.ALLOW_COOKIES_FOR_SESSION, SitePermissions.BLOCK ],
@@ -652,16 +665,21 @@ var gPermissionObject = {
 
   "shortcuts": {
     states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
   },
 
   "canvas": {
   },
 
+  "plugin:flash": {
+    labelID: "flash-plugin",
+    states: [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ],
+  },
+
   "midi": {
     exactHostMatch: true
   },
 
   "midi-sysex": {
     exactHostMatch: true
   }
 };
--- a/browser/modules/test/unit/test_SitePermissions.js
+++ b/browser/modules/test/unit/test_SitePermissions.js
@@ -7,17 +7,17 @@ ChromeUtils.import("resource:///modules/
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const STORAGE_MANAGER_ENABLED = Services.prefs.getBoolPref("browser.storageManager.enabled");
 const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref("privacy.resistFingerprinting");
 const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled");
 
 add_task(async function testPermissionsListing() {
   let expectedPermissions = ["camera", "cookie", "desktop-notification", "focus-tab-by-prompt",
-     "geo", "image", "install", "microphone", "popup", "screen", "shortcuts"];
+     "geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts"];
   if (STORAGE_MANAGER_ENABLED) {
     // The persistent-storage permission is still only pref-on on Nightly
     // so we add it only when it's pref-on.
     // Should remove this checking and add it as default after it is fully pref-on.
     expectedPermissions.push("persistent-storage");
   }
   if (RESIST_FINGERPRINTING_ENABLED) {
     // Canvas permission should be hidden unless privacy.resistFingerprinting
@@ -126,17 +126,17 @@ add_task(async function testExactHostMat
     exactHostMatched.push("canvas");
   }
   if (MIDI_ENABLED) {
     // WebMIDI is only pref'd on in nightly.
     // Should remove this checking and add it as default after it is fully pref-on.
     exactHostMatched.push("midi");
     exactHostMatched.push("midi-sysex");
   }
-  let nonExactHostMatched = ["image", "cookie", "popup", "install", "shortcuts"];
+  let nonExactHostMatched = ["image", "cookie", "plugin:flash", "popup", "install", "shortcuts"];
 
   let permissions = SitePermissions.listPermissions();
   for (let permission of permissions) {
     SitePermissions.set(uri, permission, SitePermissions.ALLOW);
 
     if (exactHostMatched.includes(permission)) {
       // Check that the sub-origin does not inherit the permission from its parent.
       Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.UNKNOWN,
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -234,19 +234,18 @@ html|*#webRTC-previewVideo {
 
 /* PLUGINS */
 
 .plugin-icon {
   list-style-image: url(chrome://mozapps/skin/plugins/plugin.svg);
   transition: fill 1.5s;
 }
 
-.plugin-blocked-icon {
+.plugin-icon.blocked-permission-icon {
   list-style-image: url(chrome://mozapps/skin/plugins/plugin-blocked.svg);
-  transition: fill 1.5s;
 }
 
 #plugin-icon-badge {
   list-style-image: url(chrome://browser/skin/notification-icons/plugin-badge.svg);
   opacity: 0;
   transition: opacity 1.5s;
 }
 
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -137,16 +137,17 @@ skip-if = !e10s # This test is only vali
 [browser_dbg-content-script-sources.js]
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-expressions.js]
 [browser_dbg-expressions-error.js]
 [browser_dbg-iframes.js]
+[browser_dbg-inline-cache.js]
 [browser_dbg-keyboard-navigation.js]
 [browser_dbg-keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
 [browser_dbg-outline.js]
 [browser_dbg-pause-exceptions.js]
 [browser_dbg-pause-ux.js]
 skip-if = os == "win"
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-inline-cache.js
@@ -0,0 +1,132 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * Test loading inline scripts from cache:
+ *   - Load document with inline script
+ *   - Reload inside debugger with toolbox caching disabled
+ *   - Reload inside debugger with toolbox caching enabled
+ */
+
+const server = createTestHTTPServer();
+
+let docValue = 1;
+server.registerPathHandler("/inline-cache.html", (request, response) => {
+  response.setHeader("Content-Type", "text/html");
+  // HTTP should assume cacheable by default.
+  // Toolbox defaults to disabling cache for subsequent loads when open.
+  response.setHeader("Cache-Control", "public, max-age=3600");
+  response.write(`
+    <html>
+    <head>
+    <script>
+      let x = ${docValue};
+    </script>
+    </head>
+    </html>
+  `);
+});
+
+const SOURCE_URL = `http://localhost:${server.identity.primaryPort}/inline-cache.html`;
+
+/**
+ * This is meant to simulate the developer editing the inline source and saving.
+ * Effectively, we change the source during the test at specific controlled points.
+ */
+function makeChanges() {
+  docValue++;
+}
+
+function getPageValue(tab) {
+  return ContentTask.spawn(tab.linkedBrowser, {}, function () {
+    return content.document.querySelector("script").textContent.trim();
+  });
+}
+
+async function reloadTabAndDebugger(tab, dbg) {
+  let navigated = waitForDispatch(dbg, "NAVIGATE");
+  let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  await reload(dbg, "inline-cache.html");
+  return Promise.all([ navigated, loaded ]);
+}
+
+add_task(async function () {
+  info("Load document with inline script");
+  const tab = await addTab(SOURCE_URL);
+  info("Open debugger");
+  clearDebuggerPreferences();
+  const toolbox = await openToolboxForTab(tab, "jsdebugger");
+  const dbg = createDebuggerContext(toolbox);
+  info("Reload tab to ensure debugger finds script");
+  await reloadTabAndDebugger(tab, dbg);
+  let pageValue = await getPageValue(tab);
+  is(pageValue, "let x = 1;", "Content loads from network, has doc value 1");
+  await waitForSource(dbg, "inline-cache.html");
+  await selectSource(dbg, "inline-cache.html");
+  await waitForLoadedSource(dbg, "inline-cache.html");
+  let dbgValue = await findSource(dbg, "inline-cache.html");
+  info(`Debugger text: ${dbgValue.text}`);
+  ok(dbgValue.text.includes(pageValue),
+     "Debugger loads from cache, gets value 1 like page");
+
+  info("Disable HTTP cache for page");
+  await toolbox.target.activeTab.reconfigure({ cacheDisabled: true });
+  makeChanges();
+
+  info("Reload inside debugger with toolbox caching disabled (attempt 1)");
+  await reloadTabAndDebugger(tab, dbg);
+  pageValue = await getPageValue(tab);
+  is(pageValue, "let x = 2;", "Content loads from network, has doc value 2");
+  await waitForLoadedSource(dbg, "inline-cache.html");
+  dbgValue = await findSource(dbg, "inline-cache.html");
+  info(`Debugger text: ${dbgValue.text}`);
+  ok(dbgValue.text.includes(pageValue),
+     "Debugger loads from network, gets value 2 like page");
+
+  makeChanges();
+
+  info("Reload inside debugger with toolbox caching disabled (attempt 2)");
+  await reloadTabAndDebugger(tab, dbg);
+  pageValue = await getPageValue(tab);
+  is(pageValue, "let x = 3;", "Content loads from network, has doc value 3");
+  await waitForLoadedSource(dbg, "inline-cache.html");
+  dbgValue = await findSource(dbg, "inline-cache.html");
+  info(`Debugger text: ${dbgValue.text}`);
+  ok(dbgValue.text.includes(pageValue),
+     "Debugger loads from network, gets value 3 like page");
+
+  info("Enable HTTP cache for page");
+  await toolbox.target.activeTab.reconfigure({ cacheDisabled: false });
+  makeChanges();
+
+  // Even though the HTTP cache is now enabled, Gecko sets the VALIDATE_ALWAYS flag when
+  // reloading the page.  So, it will always make a request to the server for the main
+  // document contents.
+
+  info("Reload inside debugger with toolbox caching enabled (attempt 1)");
+  await reloadTabAndDebugger(tab, dbg);
+  pageValue = await getPageValue(tab);
+  is(pageValue, "let x = 4;", "Content loads from network, has doc value 4");
+  await waitForLoadedSource(dbg, "inline-cache.html");
+  dbgValue = await findSource(dbg, "inline-cache.html");
+  info(`Debugger text: ${dbgValue.text}`);
+  ok(dbgValue.text.includes(pageValue),
+     "Debugger loads from cache, gets value 4 like page");
+
+  makeChanges();
+
+  info("Reload inside debugger with toolbox caching enabled (attempt 2)");
+  await reloadTabAndDebugger(tab, dbg);
+  pageValue = await getPageValue(tab);
+  is(pageValue, "let x = 5;", "Content loads from network, has doc value 5");
+  await waitForLoadedSource(dbg, "inline-cache.html");
+  dbgValue = await findSource(dbg, "inline-cache.html");
+  info(`Debugger text: ${dbgValue.text}`);
+  ok(dbgValue.text.includes(pageValue),
+     "Debugger loads from cache, gets value 5 like page");
+
+  await toolbox.destroy();
+  await removeTab(tab);
+});
--- a/devtools/client/shared/components/Frame.js
+++ b/devtools/client/shared/components/Frame.js
@@ -118,33 +118,37 @@ class Frame extends Component {
 
     if (this.state && this.state.isSourceMapped && this.state.frame) {
       frame = this.state.frame;
       isSourceMapped = this.state.isSourceMapped;
     } else {
       frame = this.props.frame;
     }
 
-    let source = frame.source ? String(frame.source) : "";
+    // If the resource was loaded by browser-loader.js, `frame.source` looks like:
+    // resource://devtools/shared/base-loader.js -> resource://devtools/path/to/file.js .
+    // What's needed is only the last part after " -> ".
+    let source = frame.source
+      ? String(frame.source).split(" -> ").pop()
+      : "";
     let line = frame.line != void 0 ? Number(frame.line) : null;
     let column = frame.column != void 0 ? Number(frame.column) : null;
 
     const { short, long, host } = getSourceNames(source);
     // Reparse the URL to determine if we should link this; `getSourceNames`
     // has already cached this indirectly. We don't want to attempt to
     // link to "self-hosted" and "(unknown)". However, we do want to link
     // to Scratchpad URIs.
     // Source mapped sources might not necessary linkable, but they
     // are still valid in the debugger.
     const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
       || isSourceMapped;
     const elements = [];
     const sourceElements = [];
     let sourceEl;
-
     let tooltip = long;
 
     // Exclude all falsy values, including `0`, as line numbers start with 1.
     if (line) {
       tooltip += `:${line}`;
       // Intentionally exclude 0
       if (column) {
         tooltip += `:${column}`;
@@ -215,17 +219,17 @@ class Frame extends Component {
 
     // If source is not a URL (self-hosted, eval, etc.), don't make
     // it an anchor link, as we can't link to it.
     if (isLinkable) {
       sourceEl = dom.a({
         onClick: e => {
           e.preventDefault();
           e.stopPropagation();
-          onClick(this.getSourceForClick(frame));
+          onClick(this.getSourceForClick({...frame, source}));
         },
         href: source,
         className: "frame-link-source",
         draggable: false,
       }, sourceInnerEl);
     } else {
       sourceEl = dom.span({
         key: "source",
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console.js
@@ -1,246 +1,187 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// Test the basic features of the Browser Console, bug 587757.
+/* import-globals-from head.js */
+
+// Test the basic features of the Browser Console.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
-                 "test/test-console.html?" + Date.now();
+                 "new-console-output/test/mochitest/test-console.html?" +
+                 Date.now();
 const TEST_FILE = "chrome://mochitests/content/browser/devtools/client/" +
-                  "webconsole/test/test-cu-reporterror.js";
+                  "webconsole/new-console-output/test/mochitest/" +
+                  "test-cu-reporterror.js";
 
 const TEST_XHR_ERROR_URI = `http://example.com/404.html?${Date.now()}`;
 
 const TEST_IMAGE = "http://example.com/browser/devtools/client/webconsole/" +
-                   "test/test-image.png";
+                   "new-console-output/test/test-image.png";
 
 const ObjectClient = require("devtools/shared/client/object-client");
 
-add_task(function* () {
-  yield loadTab(TEST_URI);
+add_task(async function () {
+  await addTab(TEST_URI);
 
   let opened = waitForBrowserConsole();
 
   let hud = HUDService.getBrowserConsole();
   ok(!hud, "browser console is not open");
   info("wait for the browser console to open with ctrl-shift-j");
   EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, window);
 
-  hud = yield opened;
+  hud = await opened;
   ok(hud, "browser console opened");
-  yield testMessages(hud);
-  yield testCPOWInspection(hud);
+
+  await setFilterState(hud, {
+    netxhr: true
+  });
+
+  await testMessages(hud);
+  await testCPOWInspection(hud);
+  await resetFilters(hud);
 });
 
-function testMessages(hud) {
+async function testMessages(hud) {
   hud.jsterm.clearOutput(true);
 
   expectUncaughtException();
+
   executeSoon(() => {
-    foobarExceptionBug587757();
+    foobarException();
   });
 
   // Add a message from a chrome window.
-  hud.iframeWindow.console.log("bug587757a");
+  hud.iframeWindow.console.log("message from chrome window");
 
   // Check Cu.reportError stack.
   // Use another js script to not depend on the test file line numbers.
   Services.scriptloader.loadSubScript(TEST_FILE, hud.iframeWindow);
 
-  // Bug 1348885: test that error from nuked globals do not throw
   let sandbox = new Cu.Sandbox(null, {
     wantComponents: false,
     wantGlobalProperties: ["URL", "URLSearchParams"],
   });
-  let error = Cu.evalInSandbox(`
-    new Error("1348885");
-  `, sandbox);
+  let error = Cu.evalInSandbox(`new Error("error from nuked globals");`, sandbox);
   Cu.reportError(error);
   Cu.nukeSandbox(sandbox);
 
   // Add a message from a content window.
-  content.console.log("bug587757b");
+  content.console.log("message from content window");
 
   // Test eval.
   hud.jsterm.execute("document.location.href");
 
   // Test eval frame script
-  hud.jsterm.execute(`
-    gBrowser.selectedBrowser.messageManager.loadFrameScript('data:application/javascript,console.log("framescript-message")', false);
-    "framescript-eval";
-  `);
+  hud.jsterm.execute(
+    `gBrowser.selectedBrowser.messageManager.loadFrameScript(` +
+    `'data:application/javascript,console.log("framescript-message")', false);` +
+    `"framescript-eval";`);
 
   // Check for network requests.
   let xhr = new XMLHttpRequest();
   xhr.onload = () => console.log("xhr loaded, status is: " + xhr.status);
   xhr.open("get", TEST_URI, true);
   xhr.send();
 
   // Check for xhr error.
   let xhrErr = new XMLHttpRequest();
   xhrErr.onload = () => {
     console.log("xhr error loaded, status is: " + xhrErr.status);
   };
   xhrErr.open("get", TEST_XHR_ERROR_URI, true);
   xhrErr.send();
 
   // Check that Fetch requests are categorized as "XHR".
-  fetch(TEST_IMAGE).then(() => { console.log("fetch loaded"); });
+  await fetch(TEST_IMAGE);
+  console.log("fetch loaded");
 
-  return waitForMessages({
-    webconsole: hud,
-    messages: [
-      {
-        name: "chrome window console.log() is displayed",
-        text: "bug587757a",
-        category: CATEGORY_WEBDEV,
-        severity: SEVERITY_LOG,
-      },
-      {
-        name: "Cu.reportError is displayed",
-        text: "bug1141222",
-        category: CATEGORY_JS,
-        severity: SEVERITY_ERROR,
-        stacktrace: [{
-          file: TEST_FILE,
-          line: 2,
-        }, {
-          file: TEST_FILE,
-          line: 4,
-        },
-        // Ignore the rest of the stack,
-        // just assert Cu.reportError call site
-        // and consoleOpened call
-        ]
-      },
-      {
-        name: "Error from nuked global works",
-        text: "1348885",
-        category: CATEGORY_JS,
-        severity: SEVERITY_ERROR,
-      },
-      {
-        name: "content window console.log() is displayed",
-        text: "bug587757b",
-        category: CATEGORY_WEBDEV,
-        severity: SEVERITY_LOG,
-      },
-      {
-        name: "jsterm eval result",
-        text: "browser.xul",
-        category: CATEGORY_OUTPUT,
-        severity: SEVERITY_LOG,
-      },
-      {
-        name: "jsterm eval result 2",
-        text: "framescript-eval",
-        category: CATEGORY_OUTPUT,
-        severity: SEVERITY_LOG,
-      },
-      {
-        name: "frame script message",
-        text: "framescript-message",
-        category: CATEGORY_WEBDEV,
-        severity: SEVERITY_LOG,
-      },
-      {
-        name: "exception message",
-        text: "foobarExceptionBug587757",
-        category: CATEGORY_JS,
-        severity: SEVERITY_ERROR,
-      },
-      {
-        name: "network message",
-        text: "test-console.html",
-        category: CATEGORY_NETWORK,
-        severity: SEVERITY_INFO,
-        isXhr: true,
-      },
-      {
-        name: "xhr error message",
-        text: "404.html",
-        category: CATEGORY_NETWORK,
-        severity: SEVERITY_ERROR,
-        isXhr: true,
-      },
-      {
-        name: "network message",
-        text: "test-image.png",
-        category: CATEGORY_NETWORK,
-        severity: SEVERITY_INFO,
-        isXhr: true,
-      },
-    ],
-  });
+  await checkMessageExists(hud, "message from chrome window");
+  await checkMessageExists(hud,
+    "error thrown from test-cu-reporterror.js via Cu.reportError()");
+  await checkMessageExists(hud, "error from nuked globals");
+  await checkMessageExists(hud, "message from content window");
+  await checkMessageExists(hud, "browser.xul");
+  await checkMessageExists(hud, "framescript-eval");
+  await checkMessageExists(hud, "framescript-message");
+  await checkMessageExists(hud, "foobarException");
+  await checkMessageExists(hud, "test-console.html");
+  await checkMessageExists(hud, "404.html");
+  await checkMessageExists(hud, "test-image.png");
 }
 
-function* testCPOWInspection(hud) {
+async function testCPOWInspection(hud) {
   // Directly request evaluation to get an actor for the selected browser.
   // Note that this doesn't actually render a message, and instead allows us
   // us to assert that inspecting an object doesn't throw in the server.
   // This would be done in a mochitest-chrome suite, but that doesn't run in
   // e10s, so it's harder to get ahold of a CPOW.
-  let cpowEval = yield hud.jsterm.requestEvaluation("gBrowser.selectedBrowser");
+  let cpowEval = await hud.jsterm.requestEvaluation("gBrowser.selectedBrowser");
   info("Creating an ObjectClient with: " + cpowEval.result.actor);
 
   let objectClient = new ObjectClient(hud.jsterm.hud.proxy.client, {
     actor: cpowEval.result.actor,
   });
 
   // Before the fix for Bug 1382833, this wouldn't resolve due to a CPOW error
   // in the ObjectActor.
-  let prototypeAndProperties = yield objectClient.getPrototypeAndProperties();
+  let prototypeAndProperties = await objectClient.getPrototypeAndProperties();
 
   // Just a sanity check to make sure a valid packet came back
   is(prototypeAndProperties.prototype.class, "XBL prototype JSClass",
     "Looks like a valid response");
 
   // The CPOW is in the _contentWindow property.
   let cpow = prototypeAndProperties.ownProperties._contentWindow.value;
 
   // But it's only a CPOW in e10s.
-  let e10sCheck = yield hud.jsterm.requestEvaluation(
+  let e10sCheck = await hud.jsterm.requestEvaluation(
     "Cu.isCrossProcessWrapper(gBrowser.selectedBrowser._contentWindow)");
   if (!e10sCheck.result) {
     is(cpow.class, "Window", "The object is not a CPOW.");
     return;
   }
 
   is(cpow.class, "CPOW: Window", "The CPOW grip has the right class.");
 
   // Check that various protocol request methods work for the CPOW.
-  let response, slice;
   let objClient = new ObjectClient(hud.jsterm.hud.proxy.client, cpow);
 
-  response = yield objClient.getPrototypeAndProperties();
+  let response = await objClient.getPrototypeAndProperties();
   is(Reflect.ownKeys(response.ownProperties).length, 0, "No property was retrieved.");
   is(response.ownSymbols.length, 0, "No symbol property was retrieved.");
   is(response.prototype.type, "null", "The prototype is null.");
 
-  response = yield objClient.enumProperties({ignoreIndexedProperties: true});
-  slice = yield response.iterator.slice(0, response.iterator.count);
+  response = await objClient.enumProperties({ignoreIndexedProperties: true});
+  let slice = await response.iterator.slice(0, response.iterator.count);
   is(Reflect.ownKeys(slice.ownProperties).length, 0, "No property was retrieved.");
 
-  response = yield objClient.enumProperties({});
-  slice = yield response.iterator.slice(0, response.iterator.count);
+  response = await objClient.enumProperties({});
+  slice = await response.iterator.slice(0, response.iterator.count);
   is(Reflect.ownKeys(slice.ownProperties).length, 0, "No property was retrieved.");
 
-  response = yield objClient.getOwnPropertyNames();
+  response = await objClient.getOwnPropertyNames();
   is(response.ownPropertyNames.length, 0, "No property was retrieved.");
 
-  response = yield objClient.getProperty("x");
+  response = await objClient.getProperty("x");
   is(response.descriptor, undefined, "The property does not exist.");
 
-  response = yield objClient.enumSymbols();
-  slice = yield response.iterator.slice(0, response.iterator.count);
+  response = await objClient.enumSymbols();
+  slice = await response.iterator.slice(0, response.iterator.count);
   is(slice.ownSymbols.length, 0, "No symbol property was retrieved.");
 
-  response = yield objClient.getPrototype();
+  response = await objClient.getPrototype();
   is(response.prototype.type, "null", "The prototype is null.");
 
-  response = yield objClient.getDisplayString();
+  response = await objClient.getDisplayString();
   is(response.displayString, "<cpow>", "The CPOW stringifies to <cpow>");
 }
+
+async function checkMessageExists(hud, msg) {
+  info(`Checking "${msg}" was logged`);
+  let message = await waitFor(() => findMessage(hud, msg));
+  ok(message, `"${msg}" was logged`);
+}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/test-cu-reporterror.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/test-cu-reporterror.js
@@ -1,4 +1,4 @@
 function a() {
-  Cu.reportError("bug1141222");
+  Cu.reportError("error thrown from test-cu-reporterror.js via Cu.reportError()");
 }
 a();
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -207,16 +207,23 @@ let SourceActor = ActorClassWithSpec(sou
   get addonPath() {
     return this._addonPath;
   },
 
   get prettyPrintWorker() {
     return this.threadActor.prettyPrintWorker;
   },
 
+  get isCacheEnabled() {
+    if (this.threadActor._parent._getCacheDisabled) {
+      return !this.threadActor._parent._getCacheDisabled();
+    }
+    return true;
+  },
+
   form: function () {
     let source = this.source || this.generatedSource;
     // This might not have a source or a generatedSource because we
     // treat HTML pages with inline scripts as a special SourceActor
     // that doesn't have either
     let introductionUrl = null;
     if (source && source.introductionScript) {
       introductionUrl = source.introductionScript.source.url;
@@ -371,17 +378,20 @@ let SourceActor = ActorClassWithSpec(sou
       }
 
       // Only load the HTML page source from cache (which exists when
       // there are inline sources). Otherwise, we can't trust the
       // cache because we are most likely here because we are
       // fetching the original text for sourcemapped code, and the
       // page hasn't requested it before (if it has, it was a
       // previous debugging session).
-      let loadFromCache = this.isInlineSource;
+      // Additionally, we should only try the cache if it is currently enabled
+      // for the document.  Without this check, the cache may return stale data
+      // that doesn't match the document shown in the browser.
+      let loadFromCache = this.isInlineSource && this.isCacheEnabled;
 
       // Fetch the sources with the same principal as the original document
       let win = this.threadActor._parent.window;
       let principal, cacheKey;
       // On xpcshell, we don't have a window but a Sandbox
       if (!isWorker && win instanceof Ci.nsIDOMWindow) {
         let webNav = win.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation);
--- a/devtools/shared/builtin-modules.js
+++ b/devtools/shared/builtin-modules.js
@@ -13,27 +13,54 @@
  * they would also miss them.
  */
 
 const { Cu, CC, Cc, Ci } = require("chrome");
 const promise = require("resource://gre/modules/Promise.jsm").Promise;
 const jsmScope = require("resource://gre/modules/Services.jsm");
 const { Services } = jsmScope;
 // Steal various globals only available in JSM scope (and not Sandbox one)
-const { ChromeUtils, HeapSnapshot, XMLHttpRequest, console,
-        atob, btoa, TextEncoder, TextDecoder } = Cu.getGlobalForObject(jsmScope);
+const {
+  console,
+  HeapSnapshot,
+} = Cu.getGlobalForObject(jsmScope);
 
 // Create a single Sandbox to access global properties needed in this module.
 // Sandbox are memory expensive, so we should create as little as possible.
-const { CSS, CSSRule, FileReader, indexedDB, InspectorUtils, URL } =
-    Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(), {
-      wantGlobalProperties: [
-        "CSS", "CSSRule", "FileReader", "indexedDB", "InspectorUtils", "URL",
-      ]
-    });
+const {
+  atob,
+  btoa,
+  ChromeUtils,
+  CSS,
+  CSSRule,
+  FileReader,
+  FormData,
+  indexedDB,
+  InspectorUtils,
+  TextDecoder,
+  TextEncoder,
+  URL,
+  XMLHttpRequest,
+} = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(), {
+  wantGlobalProperties: [
+    "atob",
+    "btoa",
+    "ChromeUtils",
+    "CSS",
+    "CSSRule",
+    "FileReader",
+    "FormData",
+    "indexedDB",
+    "TextDecoder",
+    "TextEncoder",
+    "InspectorUtils",
+    "URL",
+    "XMLHttpRequest",
+  ]
+});
 
 /**
  * Defines a getter on a specified object that will be created upon first use.
  *
  * @param object
  *        The object to define the lazy getter on.
  * @param name
  *        The name of the getter to define on object.
@@ -167,26 +194,26 @@ function lazyRequireGetter(obj, property
     },
     configurable: true,
     enumerable: true
   });
 }
 
 // List of pseudo modules exposed to all devtools modules.
 exports.modules = {
-  "Services": Object.create(Services),
+  ChromeUtils,
+  FileReader,
+  HeapSnapshot,
+  InspectorUtils,
   promise,
   // Expose "chrome" Promise, which aren't related to any document
   // and so are never frozen, even if the browser loader module which
   // pull it is destroyed. See bug 1402779.
   Promise,
-  ChromeUtils,
-  HeapSnapshot,
-  InspectorUtils,
-  FileReader,
+  Services: Object.create(Services),
 };
 
 defineLazyGetter(exports.modules, "Debugger", () => {
   // addDebuggerToGlobal only allows adding the Debugger object to a global. The
   // this object is not guaranteed to be a global (in particular on B2G, due to
   // compartment sharing), so add the Debugger object to a sandbox instead.
   let sandbox = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")());
   Cu.evalInSandbox(
@@ -208,57 +235,55 @@ defineLazyGetter(exports.modules, "Timer
 
 defineLazyGetter(exports.modules, "xpcInspector", () => {
   return Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
 });
 
 // List of all custom globals exposed to devtools modules.
 // Changes here should be mirrored to devtools/.eslintrc.
 exports.globals = {
-  isWorker: false,
-  reportError: Cu.reportError,
-  atob: atob,
-  btoa: btoa,
-  console: console,
-  TextEncoder: TextEncoder,
-  TextDecoder: TextDecoder,
-  URL,
+  atob,
+  btoa,
+  console,
   CSS,
   CSSRule,
-  loader: {
-    lazyGetter: defineLazyGetter,
-    lazyImporter: defineLazyModuleGetter,
-    lazyServiceGetter: defineLazyServiceGetter,
-    lazyRequireGetter: lazyRequireGetter,
-    // Defined by Loader.jsm
-    id: null
-  },
-
-  XMLHttpRequest: XMLHttpRequest,
-
-  Node: Ci.nsIDOMNode,
-  Element: Ci.nsIDOMElement,
-  DocumentFragment: Ci.nsIDOMDocumentFragment,
-
   // Make sure `define` function exists.  This allows defining some modules
   // in AMD format while retaining CommonJS compatibility through this hook.
   // JSON Viewer needs modules in AMD format, as it currently uses RequireJS
   // from a content document and can't access our usual loaders.  So, any
   // modules shared with the JSON Viewer should include a define wrapper:
   //
   //   // Make this available to both AMD and CJS environments
   //   define(function(require, exports, module) {
   //     ... code ...
   //   });
   //
   // Bug 1248830 will work out a better plan here for our content module
   // loading needs, especially as we head towards devtools.html.
   define(factory) {
     factory(this.require, this.exports, this.module);
   },
+  DocumentFragment: Ci.nsIDOMDocumentFragment,
+  Element: Ci.nsIDOMElement,
+  FormData,
+  isWorker: false,
+  loader: {
+    lazyGetter: defineLazyGetter,
+    lazyImporter: defineLazyModuleGetter,
+    lazyServiceGetter: defineLazyServiceGetter,
+    lazyRequireGetter: lazyRequireGetter,
+    // Defined by Loader.jsm
+    id: null
+  },
+  Node: Ci.nsIDOMNode,
+  reportError: Cu.reportError,
+  TextDecoder,
+  TextEncoder,
+  URL,
+  XMLHttpRequest,
 };
 // SDK loader copy globals property descriptors on each module global object
 // so that we have to memoize them from here in order to instanciate each
 // global only once.
 // `globals` is a cache object on which we put all global values
 // and we set getters on `exports.globals` returning `globals` values.
 let globals = {};
 function lazyGlobal(name, getter) {
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -478,16 +478,23 @@ MediaEngineRemoteVideoSource::DeliverFra
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(mState == kStarted);
     req_max_width = mCapability.width & 0xffff;
     req_max_height = mCapability.height & 0xffff;
     req_ideal_width = (mCapability.width >> 16) & 0xffff;
     req_ideal_height = (mCapability.height >> 16) & 0xffff;
   }
 
+  if (aProps.rotation() == 90 || aProps.rotation() == 270) {
+    // This frame is rotated, so what was negotiated as width is now height,
+    // and vice versa.
+    std::swap(req_max_width, req_max_height);
+    std::swap(req_ideal_width, req_ideal_height);
+  }
+
   int32_t dst_max_width = std::min(req_max_width, aProps.width());
   int32_t dst_max_height = std::min(req_max_height, aProps.height());
   // This logic works for both camera and screen sharing case.
   // for camera case, req_ideal_width and req_ideal_height is 0.
   // The following snippet will set dst_width to dst_max_width and dst_height to dst_max_height
   int32_t dst_width = std::min(req_ideal_width > 0 ? req_ideal_width : aProps.width(), dst_max_width);
   int32_t dst_height = std::min(req_ideal_height > 0 ? req_ideal_height : aProps.height(), dst_max_height);
 
@@ -538,19 +545,21 @@ MediaEngineRemoteVideoSource::DeliverFra
   if (!image->CopyData(data)) {
     MOZ_ASSERT_UNREACHABLE("We might fail to allocate a buffer, but with this "
                            "being a recycling container that shouldn't happen");
     return 0;
   }
 
 #ifdef DEBUG
   static uint32_t frame_num = 0;
-  LOGFRAME(("frame %d (%dx%d)->(%dx%d); timeStamp %u, ntpTimeMs %" PRIu64 ", renderTimeMs %" PRIu64,
+  LOGFRAME(("frame %d (%dx%d)->(%dx%d); rotation %d, timeStamp %u, "
+            "ntpTimeMs %" PRIu64 ", renderTimeMs %" PRIu64,
             frame_num++, aProps.width(), aProps.height(), dst_width, dst_height,
-            aProps.timeStamp(), aProps.ntpTimeMs(), aProps.renderTimeMs()));
+            aProps.rotation(), aProps.timeStamp(), aProps.ntpTimeMs(),
+            aProps.renderTimeMs()));
 #endif
 
   bool sizeChanged = false;
   {
     MutexAutoLock lock(mMutex);
     // implicitly releases last image
     sizeChanged = (!mImage && image) ||
                   (mImage && image && mImage->GetSize() != image->GetSize());
--- a/js/src/devtools/automation/variants/nojit
+++ b/js/src/devtools/automation/variants/nojit
@@ -1,7 +1,4 @@
 {
     "configure-args": "--disable-ion --enable-warnings-as-errors",
-    "optimize": true,
-    "skip-tests": {
-        "all": ["jstests", "jittest", "jsapitests"]
-    }
+    "optimize": true
 }
--- a/layout/tools/reftest/reftest-preferences.js
+++ b/layout/tools/reftest/reftest-preferences.js
@@ -59,17 +59,17 @@ user_pref("layout.interruptible-reflow.e
 
 // Tell the search service we are running in the US.  This also has the
 // desired side-effect of preventing our geoip lookup.
 user_pref("browser.search.isUS", true);
 user_pref("browser.search.countryCode", "US");
 user_pref("browser.search.geoSpecificDefaults", false);
 
 // Make sure Shield doesn't hit the network.
-user_pref("extensions.shield-recipe-client.api_url", "https://localhost/selfsupport-dummy/");
+user_pref("app.normandy.api_url", "https://localhost/selfsupport-dummy/");
 
 // Make sure Ping Centre doesn't hit the network.
 user_pref("browser.ping-centre.staging.endpoint", "https://localhost");
 user_pref("browser.ping-centre.production.endpoint", "https://localhost");
 
 // use about:blank, not browser.startup.homepage
 user_pref("browser.startup.page", 0);
 
--- a/media/audioipc/README_MOZILLA
+++ b/media/audioipc/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the audioipc-2
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The audioipc-2 git repository is: https://github.com/djg/audioipc-2.git
 
-The git commit ID used was 933fb48b252a10569ba8d598541577c6f2dc308f (2018-02-21 17:13:04 +1000)
+The git commit ID used was 79c1622259b03ef58744da5e6501f95be81272cb (2018-03-03 08:55:52 +1000)
--- a/media/audioipc/audioipc/Cargo.toml
+++ b/media/audioipc/audioipc/Cargo.toml
@@ -4,26 +4,26 @@ version = "0.2.1"
 authors = [
         "Matthew Gregan <kinetik@flim.org>",
         "Dan Glastonbury <dan.glastonbury@gmail.com>"
         ]
 description = "Remote Cubeb IPC"
 
 [dependencies]
 cubeb = "0.4"
-bincode = "0.8"
+bincode = "0.9"
 bytes = "0.4"
 # rayon-core in Gecko uses futures 0.1.13
 futures = "=0.1.13"
 iovec = "0.1"
 libc = "0.2"
 log = "^0.3.6"
 memmap = "0.5.2"
 scoped-tls = "0.1"
 serde = "1.*.*"
 serde_derive = "1.*.*"
 tokio-core = "0.1"
 tokio-io = "0.1"
 tokio-uds = "0.1.7"
 
 [dependencies.error-chain]
 version = "0.11.0"
-default-features = false
\ No newline at end of file
+default-features = false
--- a/media/audioipc/audioipc/src/codec.rs
+++ b/media/audioipc/audioipc/src/codec.rs
@@ -97,17 +97,17 @@ impl<In, Out> LengthDelimitedCodec<In, O
         if buf.len() < n {
             return Ok(None);
         }
 
         let buf = buf.split_to(n).freeze();
 
         trace!("Attempting to decode");
         let msg = try!(deserialize::<Out>(buf.as_ref()).map_err(|e| match *e {
-            bincode::ErrorKind::IoError(e) => e,
+            bincode::ErrorKind::Io(e) => e,
             _ => io::Error::new(io::ErrorKind::Other, *e),
         }));
 
         trace!("... Decoded {:?}", msg);
         Ok(Some(msg))
     }
 }
 
@@ -165,16 +165,16 @@ where
         buf.reserve((encoded_len + 2) as usize);
 
         buf.put_u16::<LittleEndian>(encoded_len as u16);
 
         if let Err(e) =
             serialize_into::<_, Self::In, _>(&mut buf.writer(), &item, Bounded(encoded_len))
         {
             match *e {
-                bincode::ErrorKind::IoError(e) => return Err(e),
+                bincode::ErrorKind::Io(e) => return Err(e),
                 _ => return Err(io::Error::new(io::ErrorKind::Other, *e)),
             }
         }
 
         Ok(())
     }
 }
--- a/media/audioipc/server/Cargo.toml
+++ b/media/audioipc/server/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "audioipc-server"
-version = "0.2.1"
+version = "0.2.2"
 authors = [
         "Matthew Gregan <kinetik@flim.org>",
         "Dan Glastonbury <dan.glastonbury@gmail.com>"
         ]
 description = "Remote cubeb server"
 
 [dependencies]
 audioipc = { path = "../audioipc" }
--- a/media/audioipc/server/src/lib.rs
+++ b/media/audioipc/server/src/lib.rs
@@ -166,30 +166,30 @@ impl CubebServer {
             ServerMessage::StreamInit(ref params) => self.process_stream_init(context, params)
                 .unwrap_or_else(|_| error(cubeb::Error::error())),
 
             ServerMessage::StreamDestroy(stm_tok) => {
                 self.streams.remove(stm_tok);
                 ClientMessage::StreamDestroyed
             }
 
-            ServerMessage::StreamStart(stm_tok) => {
-                let _ = self.streams[stm_tok].start();
-                ClientMessage::StreamStarted
-            }
+            ServerMessage::StreamStart(stm_tok) => self.streams[stm_tok]
+                .start()
+                .map(|_| ClientMessage::StreamStarted)
+                .unwrap_or_else(error),
 
-            ServerMessage::StreamStop(stm_tok) => {
-                let _ = self.streams[stm_tok].stop();
-                ClientMessage::StreamStopped
-            }
+            ServerMessage::StreamStop(stm_tok) => self.streams[stm_tok]
+                .stop()
+                .map(|_| ClientMessage::StreamStopped)
+                .unwrap_or_else(error),
 
-            ServerMessage::StreamResetDefaultDevice(stm_tok) => {
-                let _ = self.streams[stm_tok].reset_default_device();
-                ClientMessage::StreamDefaultDeviceReset
-            }
+            ServerMessage::StreamResetDefaultDevice(stm_tok) => self.streams[stm_tok]
+                .reset_default_device()
+                .map(|_| ClientMessage::StreamDefaultDeviceReset)
+                .unwrap_or_else(error),
 
             ServerMessage::StreamGetPosition(stm_tok) => self.streams[stm_tok]
                 .position()
                 .map(ClientMessage::StreamPosition)
                 .unwrap_or_else(error),
 
             ServerMessage::StreamGetLatency(stm_tok) => self.streams[stm_tok]
                 .latency()
--- a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc
@@ -202,16 +202,23 @@ int32_t VideoCaptureImpl::IncomingFrame(
             return -1;
         }
 
         VideoFrame captureFrame(
             buffer, 0, rtc::TimeMillis(),
             !apply_rotation ? _rotateFrame : kVideoRotation_0);
         captureFrame.set_ntp_time_ms(captureTime);
 
+        // This is one ugly hack to let CamerasParent know what rotation
+        // the frame was captured at. Note that this goes against the intended
+        // meaning of rotation of the frame (how to rotate it before rendering).
+        // We do this so CamerasChild can scale to the proper dimensions
+        // later on in the pipe.
+        captureFrame.set_rotation(_rotateFrame);
+
         DeliverCapturedFrame(captureFrame);
     }
     else // Encoded format
     {
         assert(false);
         return -1;
     }
 
--- a/servo/components/config/basedir.rs
+++ b/servo/components/config/basedir.rs
@@ -12,80 +12,45 @@ use android_injected_glue;
 use std::env;
 #[cfg(target_os = "android")]
 use std::ffi::CStr;
 use std::path::PathBuf;
 #[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
 use xdg;
 
 #[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
-pub fn default_config_dir() -> Option<PathBuf> {
+pub fn default_config_dir() -> PathBuf {
     let xdg_dirs = xdg::BaseDirectories::with_profile("servo", "default").unwrap();
-    let config_dir = xdg_dirs.get_config_home();
-    Some(config_dir)
-}
-
-#[cfg(target_os = "android")]
-#[allow(unsafe_code)]
-pub fn default_config_dir() -> Option<PathBuf> {
-    let dir = unsafe {
-        CStr::from_ptr((*android_injected_glue::get_app().activity).externalDataPath)
-    };
-    Some(PathBuf::from(dir.to_str().unwrap()))
-}
-
-#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
-pub fn default_data_dir() -> Option<PathBuf> {
-    let xdg_dirs = xdg::BaseDirectories::with_profile("servo", "default").unwrap();
-    let data_dir = xdg_dirs.get_data_home();
-    Some(data_dir)
+    xdg_dirs.get_config_home()
 }
 
 #[cfg(target_os = "android")]
 #[allow(unsafe_code)]
-pub fn default_data_dir() -> Option<PathBuf> {
-    let dir = unsafe {
-        CStr::from_ptr((*android_injected_glue::get_app().activity).internalDataPath)
-    };
-    Some(PathBuf::from(dir.to_str().unwrap()))
-}
-
-#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
-pub fn default_cache_dir() -> Option<PathBuf> {
-    let xdg_dirs = xdg::BaseDirectories::with_profile("servo", "default").unwrap();
-    let cache_dir = xdg_dirs.get_cache_home();
-    Some(cache_dir)
-}
-
-#[cfg(target_os = "android")]
-#[allow(unsafe_code)]
-pub fn default_cache_dir() -> Option<PathBuf> {
-    // TODO: Use JNI to call context.getCacheDir().
-    // There is no equivalent function in NDK/NativeActivity.
+pub fn default_config_dir() -> PathBuf {
     let dir = unsafe {
         CStr::from_ptr((*android_injected_glue::get_app().activity).externalDataPath)
     };
-    Some(PathBuf::from(dir.to_str().unwrap()))
+    PathBuf::from(dir.to_str().unwrap())
 }
 
 #[cfg(target_os = "macos")]
-pub fn default_config_dir() -> Option<PathBuf> {
+pub fn default_config_dir() -> PathBuf {
     let mut config_dir = env::home_dir().unwrap();
     config_dir.push("Library");
     config_dir.push("Application Support");
     config_dir.push("Servo");
-    Some(config_dir)
+    config_dir
 }
 
 #[cfg(target_os = "windows")]
-pub fn default_config_dir() -> Option<PathBuf> {
+pub fn default_config_dir() -> PathBuf {
     let mut config_dir = match env::var_os("APPDATA") {
         Some(appdata_path) => PathBuf::from(appdata_path),
         None => {
             let mut dir = env::home_dir().unwrap();
             dir.push("Appdata");
             dir.push("Roaming");
             dir
         }
     };
     config_dir.push("Servo");
-    Some(config_dir)
+    config_dir
 }
--- a/servo/components/config/prefs.rs
+++ b/servo/components/config/prefs.rs
@@ -179,17 +179,17 @@ pub fn read_prefs_from_file<T>(mut file:
 
 pub fn add_user_prefs() {
     match opts::get().config_dir {
         Some(ref config_path) => {
             let mut path = PathBuf::from(config_path);
             init_user_prefs(&mut path);
         }
         None => {
-            let mut path = default_config_dir().unwrap();
+            let mut path = default_config_dir();
             if path.join("prefs.json").exists() {
                 init_user_prefs(&mut path);
             }
         }
     }
 }
 
 fn init_user_prefs(path: &mut PathBuf) {
--- a/servo/components/config/tests/prefs.rs
+++ b/servo/components/config/tests/prefs.rs
@@ -51,17 +51,17 @@ fn test_get_set_reset_extend() {
 #[test]
 fn test_default_config_dir_create_read_write() {
   let json_str = "{\
   \"layout.writing-mode.enabled\": true,\
   \"extra.stuff\": false,\
   \"shell.homepage\": \"https://google.com\"\
 }";
     let mut expected_json = String::new();
-    let config_path = basedir::default_config_dir().unwrap();
+    let config_path = basedir::default_config_dir();
 
     if !config_path.exists() {
       fs::create_dir_all(&config_path).unwrap();
     }
 
     let json_path = config_path.join("test_config.json");
 
     let mut fd = File::create(&json_path).unwrap();
--- a/servo/ports/servo/main.rs
+++ b/servo/ports/servo/main.rs
@@ -253,17 +253,17 @@ fn setup_logging() {}
 /// Blank lines and those beginning with a '#' are ignored.
 /// Each line should be a separate parameter as would be parsed by the shell.
 /// For example, "servo -p 10 http://en.wikipedia.org/wiki/Rust" would take 4 lines.
 fn args() -> Vec<String> {
     use std::error::Error;
     use std::fs::File;
     use std::io::{BufRead, BufReader};
 
-    let mut params_file = config::basedir::default_config_dir().unwrap();
+    let mut params_file = config::basedir::default_config_dir();
     params_file.push("android_params");
     match File::open(params_file.to_str().unwrap()) {
         Ok(f) => {
             let mut vec = Vec::new();
             let file = BufReader::new(&f);
             for line in file.lines() {
                 let l = line.unwrap().trim().to_owned();
                 // ignore blank lines and those that start with a '#'
--- a/testing/geckodriver/CHANGES.md
+++ b/testing/geckodriver/CHANGES.md
@@ -4,33 +4,35 @@ Change log
 All notable changes to this program is documented in this file.
 
 Unreleased
 ----------
 
 ### Added
 
 - New `--jsdebugger` flag to open the Browser Toolbox when Firefox
-  launches.  This is useful for debugging Marionette internals
+  launches.  This is useful for debugging Marionette internals.
 
 - Introduced the temporary, boolean capability
   `moz:useNonSpecCompliantPointerOrigin` to disable the WebDriver
-  conforming behavior of calculating the Pointer Origin
+  conforming behavior of calculating the Pointer Origin.
 
 ### Changed
 
 - HTTP status code for the [`StaleElementReference`] error changed
   from 400 (Bad Request) to 404 (Not Found)
 
 - Backtraces from geckodriver no longer substitute for missing
   Marionette stacktraces
 
 - `Delete Session` now allows Firefox to safely shutdown within 70s before
   force-killing the process
 
+- Changed preference used to disable shield studies to `app.normandy.api_url`.
+
 ### Fixed
 
 - Improved error messages for malformed capabilities
 
 
 0.19.1 (2017-10-30)
 -------------------
 
--- a/testing/geckodriver/src/prefs.rs
+++ b/testing/geckodriver/src/prefs.rs
@@ -147,17 +147,17 @@ lazy_static! {
 
         // Disable metadata caching for installed add-ons by default
         ("extensions.getAddons.cache.enabled", Pref::new(false)),
 
         // Disable intalling any distribution extensions or add-ons
         ("extensions.installDistroAddons", Pref::new(false)),
 
         // Make sure Shield doesn't hit the network.
-        ("extensions.shield-recipe-client.api_url", Pref::new("")),
+        ("app.normandy.api_url", Pref::new("")),
 
         ("extensions.showMismatchUI", Pref::new(false)),
 
         // Turn off extension updates so they do not bother tests
         ("extensions.update.enabled", Pref::new(false)),
         ("extensions.update.notifyUser", Pref::new(false)),
 
         // Make sure opening about:addons will not hit the network
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -48,17 +48,17 @@ class GeckoInstance(object):
         # AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
         "extensions.autoDisableScopes": 0,
         "extensions.enabledScopes": 5,
         # Disable metadata caching for installed add-ons by default
         "extensions.getAddons.cache.enabled": False,
         # Disable intalling any distribution add-ons
         "extensions.installDistroAddons": False,
         # Make sure Shield doesn't hit the network.
-        "extensions.shield-recipe-client.api_url": "",
+        "app.normandy.api_url": "",
         "extensions.showMismatchUI": False,
         # Turn off extension updates so they don't bother tests
         "extensions.update.enabled": False,
         "extensions.update.notifyUser": False,
         # Make sure opening about:addons won"t hit the network
         "extensions.webservice.discoverURL": "http://%(server)s/dummy/discoveryURL",
 
         # Allow the application to have focus even it runs in the background
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -308,17 +308,17 @@ user_pref("browser.uitour.url", "http://
 // Tell the search service we are running in the US.  This also has the desired
 // side-effect of preventing our geoip lookup.
 user_pref("browser.search.isUS", true);
 user_pref("browser.search.countryCode", "US");
 // This will prevent HTTP requests for region defaults.
 user_pref("browser.search.geoSpecificDefaults", false);
 
 // Make sure Shield doesn't hit the network.
-user_pref("extensions.shield-recipe-client.api_url", "");
+user_pref("app.normandy.api_url", "");
 
 // Make sure PingCentre doesn't hit the network.
 user_pref("browser.ping-centre.staging.endpoint", "");
 user_pref("browser.ping-centre.production.endpoint", "");
 
 user_pref("media.eme.enabled", true);
 
 // Set the number of shmems the PChromiumCDM protocol pre-allocates to 0,
--- a/testing/runtimes/mochitest-browser-chrome-e10s.runtimes.json
+++ b/testing/runtimes/mochitest-browser-chrome-e10s.runtimes.json
@@ -163,17 +163,16 @@
     "browser/base/content/test/plugins/browser_CTP_drag_drop.js": 5737,
     "browser/base/content/test/plugins/browser_CTP_favorfallback.js": 4197,
     "browser/base/content/test/plugins/browser_CTP_notificationBar.js": 4022,
     "browser/base/content/test/plugins/browser_CTP_remove_navigate.js": 2812,
     "browser/base/content/test/plugins/browser_blocking.js": 8271,
     "browser/base/content/test/plugins/browser_blocklist_content.js": 3470,
     "browser/base/content/test/plugins/browser_clearplugindata.js": 2972,
     "browser/base/content/test/plugins/browser_iterate_hidden_plugins.js": 3683,
-    "browser/base/content/test/plugins/browser_pageInfo_plugins.js": 3850,
     "browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js": 4903,
     "browser/base/content/test/plugins/browser_pluginnotification.js": 10941,
     "browser/base/content/test/plugins/browser_plugins_added_dynamically.js": 2627,
     "browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js": 4030,
     "browser/base/content/test/plugins/browser_private_clicktoplay.js": 7605,
     "browser/base/content/test/popupNotifications/browser_popupNotification.js": 2717,
     "browser/base/content/test/popupNotifications/browser_popupNotification_2.js": 5048,
     "browser/base/content/test/popupNotifications/browser_popupNotification_3.js": 4707,
--- a/testing/runtimes/mochitest-browser-chrome.runtimes.json
+++ b/testing/runtimes/mochitest-browser-chrome.runtimes.json
@@ -166,17 +166,16 @@
     "browser/base/content/test/plugins/browser_CTP_multi_allow.js": 2872,
     "browser/base/content/test/plugins/browser_CTP_notificationBar.js": 4215,
     "browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js": 1716,
     "browser/base/content/test/plugins/browser_CTP_remove_navigate.js": 2099,
     "browser/base/content/test/plugins/browser_CTP_zoom.js": 2291,
     "browser/base/content/test/plugins/browser_blocking.js": 6301,
     "browser/base/content/test/plugins/browser_clearplugindata.js": 1970,
     "browser/base/content/test/plugins/browser_iterate_hidden_plugins.js": 2260,
-    "browser/base/content/test/plugins/browser_pageInfo_plugins.js": 2521,
     "browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js": 3550,
     "browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js": 3141,
     "browser/base/content/test/plugins/browser_pluginnotification.js": 8135,
     "browser/base/content/test/plugins/browser_plugins_added_dynamically.js": 2075,
     "browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js": 3061,
     "browser/base/content/test/plugins/browser_private_clicktoplay.js": 5946,
     "browser/base/content/test/popupNotifications/browser_popupNotification.js": 2262,
     "browser/base/content/test/popupNotifications/browser_popupNotification_2.js": 5117,
--- a/testing/talos/talos/config.py
+++ b/testing/talos/talos/config.py
@@ -161,17 +161,17 @@ DEFAULTS = dict(
             '/repositoryGetWithPerformanceURL',
         'extensions.getAddons.search.browseURL':
             'http://127.0.0.1/extensions-dummy/repositoryBrowseURL',
         'media.gmp-manager.url':
             'http://127.0.0.1/gmpmanager-dummy/update.xml',
         'media.gmp-manager.updateEnabled': False,
         'extensions.systemAddon.update.url':
             'http://127.0.0.1/dummy-system-addons.xml',
-        'extensions.shield-recipe-client.api_url':
+        'app.normandy.api_url':
             'https://127.0.0.1/selfsupport-dummy/',
         'browser.ping-centre.staging.endpoint':
             'https://127.0.0.1/pingcentre/dummy/',
         'browser.ping-centre.production.endpoint':
             'https://127.0.0.1/pingcentre/dummy/',
         'media.navigator.enabled': True,
         'media.peerconnection.enabled': True,
         'media.navigator.permission.disabled': True,
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -1468,18 +1468,17 @@ try {
   }
 } catch (e) { }
 // We need to avoid hitting the network with certain components.
 try {
   if (runningInParent) {
     _Services.prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
     _Services.prefs.setCharPref("media.gmp-manager.updateEnabled", false);
     _Services.prefs.setCharPref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
-    _Services.prefs.setCharPref("extensions.shield-recipe-client.api_url",
-                                "https://%(server)s/selfsupport-dummy/");
+    _Services.prefs.setCharPref("app.normandy.api_url", "https://%(server)s/selfsupport-dummy/");
     _Services.prefs.setCharPref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy");
     _Services.prefs.setCharPref("browser.search.geoip.url", "https://%(server)s/geoip-dummy");
     _Services.prefs.setCharPref("browser.safebrowsing.downloads.remote.url", "https://%(server)s/safebrowsing-dummy");
   }
 } catch (e) { }
 
 // Make tests run consistently on DevEdition (which has a lightweight theme
 // selected by default).
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/.cargo-checksum.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{"files":{".travis.yml":"f705a11b487bf71c41ebd8223cc1f3cbde0dfdfeea96a100af55e06e93397a1b","Cargo.toml":"b3ef32df664d22cfe4526f0022c8789e8976970b9e0982e1dd52f4f811134515","LICENSE.md":"90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912","changelist.org":"936b58455e1c221539b73b5250302dcd96baa04a5d8536199d3351142addad57","examples/basic.rs":"ef6ab76936c8322b9f89fe8308311339c0bf7b413c5f88b5314b0035d49917a3","logo.png":"ebc5305aae938c1f834cf35302faa8be0f1b7b8c3c3beef5cf6b2f68b9628c35","readme.dev.md":"43bad3bcc13a5c057344d3ba7f64bd2b313f8c133d6afa068108df73e8e8facd","readme.md":"ca48b4a712089d792d449ef6e0e399efaf227dbcfcb141540684a16a2763583b","src/de/mod.rs":"8651e00130bd062e2305dcce8b68d777fff9877688e776b239778e18046dddaf","src/de/read.rs":"5abca51c6f0d93cc144914ed30bf2cfd0074ced09a0de8c3983997aaa471562d","src/internal.rs":"d9448e8467caf4cf24703626dab9e0d9420e98419e323ad7e611e4aeab525e4a","src/lib.rs":"998b85e103f8f5480ffeef43bd8430a66c061011055a053377f37dce32bf9088","src/ser/mod.rs":"0eeb467eeb8189fb935e4996cd45d1f292c401f92b00793907bd428f1bde421d","tests/test.rs":"26598b882a691caa5301a569e56e31567bfba5ffeab6f0ca67ebd95bfae679b0"},"package":"e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e"}
\ No newline at end of file
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/.travis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-language: rust
-rust:
-    - stable
-    - beta
-    - nightly
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "bincode"
-version = "0.8.0"
-authors = ["Ty Overby <ty@pre-alpha.com>", "Francesco Mazzoli <f@mazzo.li>", "David Tolnay <dtolnay@gmail.com>", "Daniel Griffen"]
-
-repository = "https://github.com/TyOverby/bincode"
-documentation = "https://docs.rs/bincode"
-keywords = ["binary", "encode", "decode", "serialize", "deserialize"]
-
-license = "MIT"
-description = "A binary serialization / deserialization strategy that uses Serde for transforming structs into bytes and vice versa!"
-
-[dependencies]
-byteorder = "1.0.0"
-num-traits = "0.1.32"
-serde = "1.*.*"
-
-[dev-dependencies]
-serde_bytes = "0.10.*"
-serde_derive = "1.*.*"
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/LICENSE.md
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Ty Overby
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/changelist.org
+++ /dev/null
@@ -1,27 +0,0 @@
-* 1.0.0
-** Removed depricated rustc-serialize support
- Rustc-serialize was a stopgap until projects like Serde were able to catch up.
- With macros stabilization on its way, we are able to switch to serde without any
- big user-friendliness issues.  Major congratulations to Serde for coming this far!
-
-** Moved Refbox, Strbox and Slicebox into a "refbox" module
- Refbox, Strbox and Slicebox are still an integral piece of bincode, but since
- they are mainly used by power-users, this move will make the crate API more organized
- and easier for new users to understand.
-
-** Upgraded to Serde 0.9.*
- Serde 0.9.* gives us a better API surface area and allows use of procedural macros for
- deriving serialize and deserialize implemenetations.
-
-** Moved serde functions into global module
- Since serde is the only supported serialization mechanism, it makes sense to have these
- functions available at the top level.
-
-** Added the ability to chose your own endian encoding.
- This functionality is located in the endian_choice module.
-
-** Changed SizeLimit to be a trait instead of an enum
- Mostly for performance reasons.
-
-** Removed RefBox / StrBox / SliceBox
- Since rustc-serialize support was phased out, you can use `Cow<T>` with serde.
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/examples/basic.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-#[macro_use]
-extern crate serde_derive;
-extern crate bincode;
-
-use bincode::{serialize, deserialize, Infinite};
-
-#[derive(Serialize, Deserialize, PartialEq)]
-struct Entity {
-    x: f32,
-    y: f32,
-}
-
-#[derive(Serialize, Deserialize, PartialEq)]
-struct World(Vec<Entity>);
-
-fn main() {
-    let world = World(vec![Entity { x: 0.0, y: 4.0 }, Entity { x: 10.0, y: 20.5 }]);
-
-    let encoded: Vec<u8> = serialize(&world, Infinite).unwrap();
-
-    // 8 bytes for the length of the vector, 4 bytes per float.
-    assert_eq!(encoded.len(), 8 + 4 * 4);
-
-    let decoded: World = deserialize(&encoded[..]).unwrap();
-
-    assert!(world == decoded);
-}
deleted file mode 100644
index b2c454d1f2103b1a9279c9c6acc6e6a23bb7b76d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/readme.dev.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Bincode
-
-<img align="right" src="./logo.png" />
-
-[![Build Status](https://travis-ci.org/TyOverby/bincode.svg)](https://travis-ci.org/TyOverby/bincode)
-[![](http://meritbadge.herokuapp.com/bincode)](https://crates.io/crates/bincode)
-
-A compact encoder / decoder pair that uses an binary zero-fluff encoding scheme.
-The size of the encoded object will be the same or smaller than the size that
-the object takes up in memory in a running Rust program.
-
-In addition to exposing two simple funcitons that encode to Vec<u8> and decode
-from Vec<u8>, binary-encode exposes a Reader/Writer API that makes it work
-perfectly with other stream-based apis such as rust files, network streams,
-and the [flate2-rs](https://github.com/alexcrichton/flate2-rs) compression
-library.
-
-[Api Documentation](http://tyoverby.github.io/bincode/bincode/)
-
-## Example
-
-^code(./examples/basic.rs)
-
-
-## Details
-
-The encoding (and thus decoding) proceeds unsurprisingly -- primitive
-types are encoded according to the underlying `Writer`, tuples and
-structs are encoded by encoding their fields one-by-one, and enums are
-encoded by first writing out the tag representing the variant and
-then the contents.
-
-However, there are some implementation details to be aware of:
-
-* `isize`/`usize` are encoded as `i64`/`u64`, for portability.
-* enums variants are encoded as a `u32` instead that as a `uint`.
-  `u32` is enough for all practical uses.
-* `str` is encoded as `(u64, &[u8])`, where the `u64` is the number of
-  bytes contained in the encoded string.
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/readme.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# Bincode
-
-<img align="right" src="./logo.png" />
-
-[![Build Status](https://travis-ci.org/TyOverby/bincode.svg)](https://travis-ci.org/TyOverby/bincode)
-[![](http://meritbadge.herokuapp.com/bincode)](https://crates.io/crates/bincode)
-[![](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)
-
-A compact encoder / decoder pair that uses a binary zero-fluff encoding scheme.
-The size of the encoded object will be the same or smaller than the size that
-the object takes up in memory in a running Rust program.
-
-In addition to exposing two simple functions that encode to Vec<u8> and decode
-from Vec<u8>, binary-encode exposes a Reader/Writer API that makes it work
-perfectly with other stream-based apis such as rust files, network streams,
-and the [flate2-rs](https://github.com/alexcrichton/flate2-rs) compression
-library.
-
-## [Api Documentation](http://tyoverby.github.io/bincode/bincode/)
-
-## Bincode in the wild
-
-* [google/tarpc](https://github.com/google/tarpc): Bincode is used to serialize and deserialize networked RPC messages.
-* [servo/webrender](https://github.com/servo/webrender): Bincode records webrender API calls for record/replay-style graphics debugging.
-* [servo/ipc-channel](https://github.com/servo/ipc-channel): Ipc-Channel uses Bincode to send structs between processes using a channel-like API.
-
-## Example
-```rust
-#[macro_use]
-extern crate serde_derive;
-extern crate bincode;
-
-use bincode::{serialize, deserialize, Infinite};
-
-#[derive(Serialize, Deserialize, PartialEq)]
-struct Entity {
-    x: f32,
-    y: f32,
-}
-
-#[derive(Serialize, Deserialize, PartialEq)]
-struct World(Vec<Entity>);
-
-fn main() {
-    let world = World(vec![Entity { x: 0.0, y: 4.0 }, Entity { x: 10.0, y: 20.5 }]);
-
-    let encoded: Vec<u8> = serialize(&world, Infinite).unwrap();
-
-    // 8 bytes for the length of the vector, 4 bytes per float.
-    assert_eq!(encoded.len(), 8 + 4 * 4);
-
-    let decoded: World = deserialize(&encoded[..]).unwrap();
-
-    assert!(world == decoded);
-}
-```
-
-
-## Details
-
-The encoding (and thus decoding) proceeds unsurprisingly -- primitive
-types are encoded according to the underlying `Writer`, tuples and
-structs are encoded by encoding their fields one-by-one, and enums are
-encoded by first writing out the tag representing the variant and
-then the contents.
-
-However, there are some implementation details to be aware of:
-
-* `isize`/`usize` are encoded as `i64`/`u64`, for portability.
-* enums variants are encoded as a `u32` instead of a `uint`.
-  `u32` is enough for all practical uses.
-* `str` is encoded as `(u64, &[u8])`, where the `u64` is the number of
-  bytes contained in the encoded string.
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/src/de/mod.rs
+++ /dev/null
@@ -1,416 +0,0 @@
-use std::io::Read;
-use std::marker::PhantomData;
-
-use byteorder::{ReadBytesExt, ByteOrder};
-use serde_crate as serde;
-use serde_crate::de::IntoDeserializer;
-use serde_crate::de::Error as DeError;
-use ::SizeLimit;
-use super::{Result, Error, ErrorKind};
-use self::read::BincodeRead;
-
-pub mod read;
-
-/// A Deserializer that reads bytes from a buffer.
-///
-/// This struct should rarely be used.
-/// In most cases, prefer the `decode_from` function.
-///
-/// The ByteOrder that is chosen will impact the endianness that
-/// is used to read integers out of the reader.
-///
-/// ```rust,ignore
-/// let d = Deserializer::new(&mut some_reader, SizeLimit::new());
-/// serde::Deserialize::deserialize(&mut deserializer);
-/// let bytes_read = d.bytes_read();
-/// ```
-pub struct Deserializer<R, S: SizeLimit, E: ByteOrder> {
-    reader: R,
-    size_limit: S,
-    _phantom: PhantomData<E>,
-}
-
-impl<'de, R: BincodeRead<'de>, E: ByteOrder, S: SizeLimit> Deserializer<R, S, E> {
-    /// Creates a new Deserializer with a given `Read`er and a size_limit.
-    pub fn new(r: R, size_limit: S) -> Deserializer<R, S, E> {
-        Deserializer {
-            reader: r,
-            size_limit: size_limit,
-            _phantom: PhantomData
-        }
-    }
-
-    fn read_bytes(&mut self, count: u64) -> Result<()> {
-        self.size_limit.add(count)
-    }
-
-    fn read_type<T>(&mut self) -> Result<()> {
-        use std::mem::size_of;
-        self.read_bytes(size_of::<T>() as u64)
-    }
-
-    fn read_vec(&mut self) -> Result<Vec<u8>> {
-        let len: usize = try!(serde::Deserialize::deserialize(&mut *self));
-        self.read_bytes(len as u64)?;
-        self.reader.get_byte_buffer(len)
-    }
-
-    fn read_string(&mut self) -> Result<String> {
-        String::from_utf8(try!(self.read_vec())).map_err(|err|
-            ErrorKind::InvalidEncoding{
-                desc: "error while decoding utf8 string",
-                detail: Some(format!("Deserialize error: {}", err))
-            }.into())
-    }
-}
-
-macro_rules! impl_nums {
-    ($ty:ty, $dser_method:ident, $visitor_method:ident, $reader_method:ident) => {
-        #[inline]
-        fn $dser_method<V>(self, visitor: V) -> Result<V::Value>
-            where V: serde::de::Visitor<'de>,
-        {
-            try!(self.read_type::<$ty>());
-            let value = try!(self.reader.$reader_method::<E>());
-            visitor.$visitor_method(value)
-        }
-    }
-}
-
-impl<'de, 'a, R, S, E> serde::Deserializer<'de> for &'a mut Deserializer<R, S, E>
-where R: BincodeRead<'de>, S: SizeLimit, E: ByteOrder {
-    type Error = Error;
-
-    #[inline]
-    fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let message = "bincode does not support Deserializer::deserialize";
-        Err(Error::custom(message))
-    }
-
-    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let value: u8 = try!(serde::Deserialize::deserialize(self));
-        match value {
-            1 => visitor.visit_bool(true),
-            0 => visitor.visit_bool(false),
-            value => {
-                Err(ErrorKind::InvalidEncoding{
-                    desc: "invalid u8 when decoding bool",
-                    detail: Some(format!("Expected 0 or 1, got {}", value))
-                }.into())
-            }
-        }
-    }
-
-    impl_nums!(u16, deserialize_u16, visit_u16, read_u16);
-    impl_nums!(u32, deserialize_u32, visit_u32, read_u32);
-    impl_nums!(u64, deserialize_u64, visit_u64, read_u64);
-    impl_nums!(i16, deserialize_i16, visit_i16, read_i16);
-    impl_nums!(i32, deserialize_i32, visit_i32, read_i32);
-    impl_nums!(i64, deserialize_i64, visit_i64, read_i64);
-    impl_nums!(f32, deserialize_f32, visit_f32, read_f32);
-    impl_nums!(f64, deserialize_f64, visit_f64, read_f64);
-
-
-    #[inline]
-    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        try!(self.read_type::<u8>());
-        visitor.visit_u8(try!(self.reader.read_u8()))
-    }
-
-    #[inline]
-    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        try!(self.read_type::<i8>());
-        visitor.visit_i8(try!(self.reader.read_i8()))
-    }
-
-    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        visitor.visit_unit()
-    }
-
-    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        use std::str;
-
-        let error = || {
-            ErrorKind::InvalidEncoding{
-                desc: "Invalid char encoding",
-                detail: None,
-            }.into()
-        };
-
-        let mut buf = [0u8; 4];
-
-        // Look at the first byte to see how many bytes must be read
-        let _ = try!(self.reader.read_exact(&mut buf[..1]));
-        let width = utf8_char_width(buf[0]);
-        if width == 1 { return visitor.visit_char(buf[0] as char) }
-        if width == 0 { return Err(error())}
-
-        if self.reader.read_exact(&mut buf[1..width]).is_err() {
-            return Err(error());
-        }
-
-        let res = try!(str::from_utf8(&buf[..width]).ok().and_then(|s| s.chars().next()).ok_or(error()));
-        visitor.visit_char(res)
-    }
-
-    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let len: usize = try!(serde::Deserialize::deserialize(&mut *self));
-        try!(self.read_bytes(len as u64));
-        self.reader.forward_read_str(len, visitor)
-    }
-
-    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        visitor.visit_string(try!(self.read_string()))
-    }
-
-    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let len: usize = try!(serde::Deserialize::deserialize(&mut *self));
-        try!(self.read_bytes(len as u64));
-        self.reader.forward_read_bytes(len, visitor)
-    }
-
-    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        visitor.visit_byte_buf(try!(self.read_vec()))
-    }
-
-    fn deserialize_enum<V>(self,
-                     _enum: &'static str,
-                     _variants: &'static [&'static str],
-                     visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        impl<'de, 'a, R: 'a, S, E> serde::de::EnumAccess<'de> for &'a mut Deserializer<R, S, E>
-        where R: BincodeRead<'de>, S: SizeLimit, E: ByteOrder {
-            type Error = Error;
-            type Variant = Self;
-
-            fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
-                where V: serde::de::DeserializeSeed<'de>,
-            {
-                let idx: u32 = try!(serde::de::Deserialize::deserialize(&mut *self));
-                let val: Result<_> = seed.deserialize(idx.into_deserializer());
-                Ok((try!(val), self))
-            }
-        }
-
-        visitor.visit_enum(self)
-    }
-
-    fn deserialize_tuple<V>(self,
-                            len: usize,
-                            visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        struct Access<'a, R: Read + 'a, S: SizeLimit + 'a, E: ByteOrder + 'a> {
-            deserializer: &'a mut Deserializer<R, S, E>,
-            len: usize,
-        }
-
-        impl<'de, 'a, 'b: 'a, R: BincodeRead<'de>+ 'b, S: SizeLimit, E: ByteOrder> serde::de::SeqAccess<'de> for Access<'a, R, S, E> {
-            type Error = Error;
-
-            fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
-                where T: serde::de::DeserializeSeed<'de>,
-            {
-                if self.len > 0 {
-                    self.len -= 1;
-                    let value = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.deserializer));
-                    Ok(Some(value))
-                } else {
-                    Ok(None)
-                }
-            }
-
-            fn size_hint(&self) -> Option<usize> {
-                Some(self.len)
-            }
-        }
-
-        visitor.visit_seq(Access { deserializer: self, len: len })
-    }
-
-    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let value: u8 = try!(serde::de::Deserialize::deserialize(&mut *self));
-        match value {
-            0 => visitor.visit_none(),
-            1 => visitor.visit_some(&mut *self),
-            _ => Err(ErrorKind::InvalidEncoding{
-                desc: "invalid tag when decoding Option",
-                detail: Some(format!("Expected 0 or 1, got {}", value))
-            }.into()),
-        }
-    }
-
-    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let len = try!(serde::Deserialize::deserialize(&mut *self));
-
-        self.deserialize_tuple(len, visitor)
-    }
-
-    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        struct Access<'a, R: Read + 'a, S: SizeLimit + 'a, E: ByteOrder + 'a> {
-            deserializer: &'a mut Deserializer<R, S, E>,
-            len: usize,
-        }
-
-        impl<'de, 'a, 'b: 'a, R: BincodeRead<'de> + 'b, S: SizeLimit, E: ByteOrder> serde::de::MapAccess<'de> for Access<'a, R, S, E> {
-            type Error = Error;
-
-            fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
-                where K: serde::de::DeserializeSeed<'de>,
-            {
-                if self.len > 0 {
-                    self.len -= 1;
-                    let key = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.deserializer));
-                    Ok(Some(key))
-                } else {
-                    Ok(None)
-                }
-            }
-
-            fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
-                where V: serde::de::DeserializeSeed<'de>,
-            {
-                let value = try!(serde::de::DeserializeSeed::deserialize(seed, &mut *self.deserializer));
-                Ok(value)
-            }
-
-            fn size_hint(&self) -> Option<usize> {
-                Some(self.len)
-            }
-        }
-
-        let len = try!(serde::Deserialize::deserialize(&mut *self));
-
-        visitor.visit_map(Access { deserializer: self, len: len })
-    }
-
-    fn deserialize_struct<V>(self,
-                       _name: &str,
-                       fields: &'static [&'static str],
-                       visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        self.deserialize_tuple(fields.len(), visitor)
-    }
-
-    fn deserialize_identifier<V>(self,
-                                   _visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let message = "bincode does not support Deserializer::deserialize_identifier";
-        Err(Error::custom(message))
-    }
-
-    fn deserialize_newtype_struct<V>(self,
-                               _name: &str,
-                               visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        visitor.visit_newtype_struct(self)
-    }
-
-    fn deserialize_unit_struct<V>(self,
-                                  _name: &'static str,
-                                  visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        visitor.visit_unit()
-    }
-
-    fn deserialize_tuple_struct<V>(self,
-                                   _name: &'static str,
-                                   len: usize,
-                                   visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        self.deserialize_tuple(len, visitor)
-    }
-
-    fn deserialize_ignored_any<V>(self,
-                                  _visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        let message = "bincode does not support Deserializer::deserialize_ignored_any";
-        Err(Error::custom(message))
-    }
-}
-
-impl<'de, 'a, R, S, E> serde::de::VariantAccess<'de> for &'a mut Deserializer<R, S, E>
-where R: BincodeRead<'de>, S: SizeLimit, E: ByteOrder {
-    type Error = Error;
-
-    fn unit_variant(self) -> Result<()> {
-        Ok(())
-    }
-
-    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
-        where T: serde::de::DeserializeSeed<'de>,
-    {
-        serde::de::DeserializeSeed::deserialize(seed, self)
-    }
-
-    fn tuple_variant<V>(self,
-                      len: usize,
-                      visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        serde::de::Deserializer::deserialize_tuple(self, len, visitor)
-    }
-
-    fn struct_variant<V>(self,
-                       fields: &'static [&'static str],
-                       visitor: V) -> Result<V::Value>
-        where V: serde::de::Visitor<'de>,
-    {
-        serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor)
-    }
-}
-static UTF8_CHAR_WIDTH: [u8; 256] = [
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
-0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
-3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
-4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
-];
-
-fn utf8_char_width(b: u8) -> usize {
-    UTF8_CHAR_WIDTH[b as usize] as usize
-}
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/src/de/read.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use std::io::{Read as IoRead, Result as IoResult, Error as IoError, ErrorKind as IoErrorKind};
-use ::Result;
-use serde_crate as serde;
-
-/// A byte-oriented reading trait that is specialized for
-/// slices and generic readers.
-pub trait BincodeRead<'storage>: IoRead {
-    #[doc(hidden)]
-    fn forward_read_str<V>(&mut self, length: usize, visitor: V) ->  Result<V::Value>
-    where V: serde::de::Visitor<'storage>;
-
-    #[doc(hidden)]
-    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>>;
-
-    #[doc(hidden)]
-    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) ->  Result<V::Value>
-    where V: serde::de::Visitor<'storage>;
-}
-
-/// A BincodeRead implementation for byte slices
-pub struct SliceReader<'storage> {
-    slice: &'storage [u8]
-}
-
-/// A BincodeRead implementation for io::Readers
-pub struct IoReadReader<R> {
-    reader: R,
-    temp_buffer: Vec<u8>,
-}
-
-impl <'storage> SliceReader<'storage> {
-    /// Constructs a slice reader
-    pub fn new(bytes: &'storage [u8]) -> SliceReader<'storage> {
-        SliceReader {
-            slice: bytes,
-        }
-    }
-}
-
-impl <R> IoReadReader<R> {
-    /// Constructs an IoReadReader
-    pub fn new(r: R) -> IoReadReader<R> {
-        IoReadReader {
-            reader: r,
-            temp_buffer: vec![],
-        }
-    }
-}
-
-impl <'storage> IoRead for SliceReader<'storage> {
-    fn read(&mut self, out: & mut [u8]) -> IoResult<usize> {
-        (&mut self.slice).read(out)
-    }
-}
-
-impl <R: IoRead> IoRead for IoReadReader<R> {
-    fn read(&mut self, out: & mut [u8]) -> IoResult<usize> {
-        self.reader.read(out)
-    }
-}
-
-impl <'storage> SliceReader<'storage> {
-    fn unexpected_eof() -> Box<::ErrorKind> {
-        return Box::new(::ErrorKind::IoError(IoError::new(IoErrorKind::UnexpectedEof, "")));
-    }
-}
-
-impl <'storage> BincodeRead<'storage> for SliceReader<'storage> {
-    fn forward_read_str<V>(&mut self, length: usize, visitor: V) ->  Result<V::Value>
-    where V: serde::de::Visitor<'storage> {
-        use ::ErrorKind;
-        if length > self.slice.len() {
-            return Err(SliceReader::unexpected_eof());
-        }
-
-        let string = match ::std::str::from_utf8(&self.slice[..length]) {
-            Ok(s) => s,
-            Err(_) => return Err(Box::new(ErrorKind::InvalidEncoding {
-                desc: "string was not valid utf8",
-                detail: None,
-            })),
-        };
-        let r = visitor.visit_borrowed_str(string);
-        self.slice = &self.slice[length..];
-        r
-    }
-
-    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
-        if length > self.slice.len() {
-            return Err(SliceReader::unexpected_eof());
-        }
-
-        let r = &self.slice[..length];
-        self.slice = &self.slice[length..];
-        Ok(r.to_vec())
-    }
-
-    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) ->  Result<V::Value>
-    where V: serde::de::Visitor<'storage> {
-        if length > self.slice.len() {
-            return Err(SliceReader::unexpected_eof());
-        }
-
-        let r = visitor.visit_borrowed_bytes(&self.slice[..length]);
-        self.slice = &self.slice[length..];
-        r
-    }
-}
-
-impl <R> IoReadReader<R> where R: IoRead {
-    fn fill_buffer(&mut self, length: usize) -> Result<()> {
-        let current_length = self.temp_buffer.len();
-        if length > current_length{
-            self.temp_buffer.reserve_exact(length - current_length);
-            unsafe { self.temp_buffer.set_len(length); }
-        }
-
-        self.reader.read_exact(&mut self.temp_buffer[..length])?;
-        Ok(())
-    }
-}
-
-impl <R> BincodeRead<'static> for IoReadReader<R> where R: IoRead {
-    fn forward_read_str<V>(&mut self, length: usize, visitor: V) ->  Result<V::Value>
-    where V: serde::de::Visitor<'static> {
-        self.fill_buffer(length)?;
-
-        let string = match ::std::str::from_utf8(&self.temp_buffer[..length]) {
-            Ok(s) => s,
-            Err(_) => return Err(Box::new(::ErrorKind::InvalidEncoding {
-                desc: "string was not valid utf8",
-                detail: None,
-            })),
-        };
-
-        let r = visitor.visit_str(string);
-        r
-    }
-
-    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
-        self.fill_buffer(length)?;
-        Ok(self.temp_buffer[..length].to_vec())
-    }
-
-    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) ->  Result<V::Value>
-    where V: serde::de::Visitor<'static> {
-        self.fill_buffer(length)?;
-        let r = visitor.visit_bytes(&self.temp_buffer[..length]);
-        r
-    }
-}
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/src/internal.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-//! A collection of serialization and deserialization functions
-//! that use the `serde` crate for the serializable and deserializable
-//! implementation.
-
-use std::io::{Write, Read};
-use std::io::Error as IoError;
-use std::{error, fmt, result};
-use ::SizeLimit;
-use byteorder::{ByteOrder};
-
-pub use super::de::{
-    Deserializer,
-};
-
-pub use super::ser::{
-    Serializer,
-};
-
-use super::ser::SizeChecker;
-
-use serde_crate as serde;
-
-/// The result of a serialization or deserialization operation.
-pub type Result<T> = result::Result<T, Error>;
-
-/// An error that can be produced during (de)serializing.
-///
-/// If decoding from a Buffer, assume that the buffer has been left
-/// in an invalid state.
-pub type Error = Box<ErrorKind>;
-
-/// The kind of error that can be produced during a serialization or deserialization.
-#[derive(Debug)]
-pub enum ErrorKind {
-    /// If the error stems from the reader/writer that is being used
-    /// during (de)serialization, that error will be stored and returned here.
-    IoError(IoError),
-    /// If the bytes in the reader are not decodable because of an invalid
-    /// encoding, this error will be returned.  This error is only possible
-    /// if a stream is corrupted.  A stream produced from `encode` or `encode_into`
-    /// should **never** produce an InvalidEncoding error.
-    InvalidEncoding {
-        #[allow(missing_docs)]
-        desc: &'static str,
-        #[allow(missing_docs)]
-        detail: Option<String>
-    },
-    /// If (de)serializing a message takes more than the provided size limit, this
-    /// error is returned.
-    SizeLimit,
-    /// Bincode can not encode sequences of unknown length (like iterators).
-    SequenceMustHaveLength,
-    /// A custom error message from Serde.
-    Custom(String)
-}
-
-impl error::Error for ErrorKind {
-    fn description(&self) -> &str {
-        match *self {
-            ErrorKind::IoError(ref err) => error::Error::description(err),
-            ErrorKind::InvalidEncoding{desc, ..} => desc,
-            ErrorKind::SequenceMustHaveLength => "bincode can't encode infinite sequences",
-            ErrorKind::SizeLimit => "the size limit for decoding has been reached",
-            ErrorKind::Custom(ref msg) => msg,
-
-        }
-    }
-
-    fn cause(&self) -> Option<&error::Error> {
-        match *self {
-            ErrorKind::IoError(ref err) => err.cause(),
-            ErrorKind::InvalidEncoding{..} => None,
-            ErrorKind::SequenceMustHaveLength => None,
-            ErrorKind::SizeLimit => None,
-            ErrorKind::Custom(_) => None,
-        }
-    }
-}
-
-impl From<IoError> for Error {
-    fn from(err: IoError) -> Error {
-        ErrorKind::IoError(err).into()
-    }
-}
-
-impl fmt::Display for ErrorKind {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            ErrorKind::IoError(ref ioerr) =>
-                write!(fmt, "IoError: {}", ioerr),
-            ErrorKind::InvalidEncoding{desc, detail: None}=>
-                write!(fmt, "InvalidEncoding: {}", desc),
-            ErrorKind::InvalidEncoding{desc, detail: Some(ref detail)}=>
-                write!(fmt, "InvalidEncoding: {} ({})", desc, detail),
-            ErrorKind::SequenceMustHaveLength =>
-                write!(fmt, "Bincode can only encode sequences and maps that have a knowable size ahead of time."),
-            ErrorKind::SizeLimit =>
-                write!(fmt, "SizeLimit"),
-            ErrorKind::Custom(ref s) =>
-                s.fmt(fmt),
-        }
-    }
-}
-
-impl serde::de::Error for Error {
-    fn custom<T: fmt::Display>(desc: T) -> Error {
-        ErrorKind::Custom(desc.to_string()).into()
-    }
-}
-
-impl serde::ser::Error for Error {
-    fn custom<T: fmt::Display>(msg: T) -> Self {
-        ErrorKind::Custom(msg.to_string()).into()
-    }
-}
-
-/// Serializes an object directly into a `Writer`.
-///
-/// If the serialization would take more bytes than allowed by `size_limit`, an error
-/// is returned and *no bytes* will be written into the `Writer`.
-///
-/// If this returns an `Error` (other than SizeLimit), assume that the
-/// writer is in an invalid state, as writing could bail out in the middle of
-/// serializing.
-pub fn serialize_into<W: ?Sized, T: ?Sized, S, E>(writer: &mut W, value: &T, size_limit: S) -> Result<()>
-    where W: Write, T: serde::Serialize, S: SizeLimit, E: ByteOrder
-{
-    if let Some(limit) = size_limit.limit() {
-        try!(serialized_size_bounded(value, limit).ok_or(ErrorKind::SizeLimit));
-    }
-
-    let mut serializer = Serializer::<_, E>::new(writer);
-    serde::Serialize::serialize(value, &mut serializer)
-}
-
-/// Serializes a serializable object into a `Vec` of bytes.
-///
-/// If the serialization would take more bytes than allowed by `size_limit`,
-/// an error is returned.
-pub fn serialize<T: ?Sized, S, E>(value: &T, size_limit: S) -> Result<Vec<u8>>
-    where T: serde::Serialize, S: SizeLimit, E: ByteOrder
-{
-    let mut writer = match size_limit.limit() {
-        Some(size_limit) => {
-            let actual_size = try!(serialized_size_bounded(value, size_limit).ok_or(ErrorKind::SizeLimit));
-            Vec::with_capacity(actual_size as usize)
-        }
-        None => {
-            let size = serialized_size(value) as usize;
-            Vec::with_capacity(size)
-        }
-    };
-
-    try!(serialize_into::<_, _, _, E>(&mut writer, value, super::Infinite));
-    Ok(writer)
-}
-
-
-struct CountSize {
-    total: u64,
-    limit: Option<u64>,
-}
-
-impl SizeLimit for CountSize {
-    fn add(&mut self, c: u64) -> Result<()> {
-        self.total += c;
-        if let Some(limit) = self.limit {
-            if self.total > limit {
-                return Err(Box::new(ErrorKind::SizeLimit))
-            }
-        }
-        Ok(())
-    }
-
-    fn limit(&self) -> Option<u64> {
-        unreachable!();
-    }
-}
-
-/// Returns the size that an object would be if serialized using bincode.
-///
-/// This is used internally as part of the check for encode_into, but it can
-/// be useful for preallocating buffers if thats your style.
-pub fn serialized_size<T: ?Sized>(value: &T) -> u64
-    where T: serde::Serialize
-{
-    let mut size_counter = SizeChecker {
-        size_limit: CountSize { total: 0, limit: None }
-    };
-
-    value.serialize(&mut size_counter).ok();
-    size_counter.size_limit.total
-}
-
-/// Given a maximum size limit, check how large an object would be if it
-/// were to be serialized.
-///
-/// If it can be serialized in `max` or fewer bytes, that number will be returned
-/// inside `Some`.  If it goes over bounds, then None is returned.
-pub fn serialized_size_bounded<T: ?Sized>(value: &T, max: u64) -> Option<u64>
-    where T: serde::Serialize
-{
-    let mut size_counter = SizeChecker {
-        size_limit: CountSize { total: 0, limit: Some(max) }
-    };
-
-    match value.serialize(&mut size_counter) {
-        Ok(_) => Some(size_counter.size_limit.total),
-        Err(_) => None,
-    }
-}
-
-/// Deserializes an object directly from a `Buffer`ed Reader.
-///
-/// If the provided `SizeLimit` is reached, the deserialization will bail immediately.
-/// A SizeLimit can help prevent an attacker from flooding your server with
-/// a neverending stream of values that runs your server out of memory.
-///
-/// If this returns an `Error`, assume that the buffer that you passed
-/// in is in an invalid state, as the error could be returned during any point
-/// in the reading.
-pub fn deserialize_from<R: ?Sized, T, S, E>(reader: &mut R, size_limit: S) -> Result<T>
-    where R: Read, T: serde::de::DeserializeOwned, S: SizeLimit, E: ByteOrder
-{
-    let reader = ::de::read::IoReadReader::new(reader);
-    let mut deserializer = Deserializer::<_, S, E>::new(reader, size_limit);
-    serde::Deserialize::deserialize(&mut deserializer)
-}
-
-/// Deserializes a slice of bytes into an object.
-///
-/// This method does not have a size-limit because if you already have the bytes
-/// in memory, then you don't gain anything by having a limiter.
-pub fn deserialize<'a, T, E: ByteOrder>(bytes: &'a [u8]) -> Result<T>
-    where T: serde::de::Deserialize<'a>,
-{
-    let reader = ::de::read::SliceReader::new(bytes);
-    let mut deserializer = Deserializer::<_, _, E>::new(reader, super::Infinite);
-    serde::Deserialize::deserialize(&mut deserializer)
-}
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/src/lib.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-#![deny(missing_docs)]
-
-//! `bincode` is a crate for encoding and decoding using a tiny binary
-//! serialization strategy.
-//!
-//! There are simple functions for encoding to `Vec<u8>` and decoding from
-//! `&[u8]`, but the meat of the library is the `encode_into` and `decode_from`
-//! functions which respectively allow encoding into a `std::io::Writer`
-//! and decoding from a `std::io::Buffer`.
-//!
-//! ## Modules
-//! Until "default type parameters" lands, we have an extra module called `endian_choice`
-//! that duplicates all of the core bincode functionality but with the option to choose
-//! which endianness the integers are encoded using.
-//!
-//! The default endianness is little.
-//!
-//! ### Using Basic Functions
-//!
-//! ```rust
-//! extern crate bincode;
-//! use bincode::{serialize, deserialize, Bounded};
-//! fn main() {
-//!     // The object that we will serialize.
-//!     let target = Some("hello world".to_string());
-//!     // The maximum size of the encoded message.
-//!     let limit = Bounded(20);
-//!
-//!     let encoded: Vec<u8>        = serialize(&target, limit).unwrap();
-//!     let decoded: Option<String> = deserialize(&encoded[..]).unwrap();
-//!     assert_eq!(target, decoded);
-//! }
-//! ```
-
-#![crate_name = "bincode"]
-#![crate_type = "rlib"]
-#![crate_type = "dylib"]
-
-extern crate byteorder;
-extern crate num_traits;
-extern crate serde as serde_crate;
-
-mod ser;
-mod de;
-pub mod internal;
-
-pub mod read_types {
-    //! The types that the deserializer uses for optimizations
-    pub use ::de::read::{SliceReader, BincodeRead, IoReadReader};
-}
-
-use std::io::{Read, Write};
-
-pub use internal::{ErrorKind, Error, Result, serialized_size, serialized_size_bounded};
-
-/// A Deserializer that uses LittleEndian byteorder
-pub type Deserializer<W, S> = internal::Deserializer<W, S, byteorder::LittleEndian>;
-/// A Serializer that uses LittleEndian byteorder
-pub type Serializer<W> = internal::Serializer<W, byteorder::LittleEndian>;
-
-/// Deserializes a slice of bytes into an object.
-///
-/// This method does not have a size-limit because if you already have the bytes
-/// in memory, then you don't gain anything by having a limiter.
-pub fn deserialize<'a, T>(bytes: &'a [u8]) -> internal::Result<T>
-    where T: serde_crate::de::Deserialize<'a>,
-{
-    internal::deserialize::<_, byteorder::LittleEndian>(bytes)
-}
-
-/// Deserializes an object directly from a `Buffer`ed Reader.
-///
-/// If the provided `SizeLimit` is reached, the deserialization will bail immediately.
-/// A SizeLimit can help prevent an attacker from flooding your server with
-/// a neverending stream of values that runs your server out of memory.
-///
-/// If this returns an `Error`, assume that the buffer that you passed
-/// in is in an invalid state, as the error could be returned during any point
-/// in the reading.
-pub fn deserialize_from<R: ?Sized, T, S>(reader: &mut R, size_limit: S) -> internal::Result<T>
-    where R: Read, T: serde_crate::de::DeserializeOwned, S: SizeLimit
-{
-    internal::deserialize_from::<_, _, _, byteorder::LittleEndian>(reader, size_limit)
-}
-
-/// Serializes an object directly into a `Writer`.
-///
-/// If the serialization would take more bytes than allowed by `size_limit`, an error
-/// is returned and *no bytes* will be written into the `Writer`.
-///
-/// If this returns an `Error` (other than SizeLimit), assume that the
-/// writer is in an invalid state, as writing could bail out in the middle of
-/// serializing.
-pub fn serialize_into<W: ?Sized, T: ?Sized, S>(writer: &mut W, value: &T, size_limit: S) -> internal::Result<()>
-    where W: Write, T: serde_crate::Serialize, S: SizeLimit
-{
-    internal::serialize_into::<_, _, _, byteorder::LittleEndian>(writer, value, size_limit)
-}
-
-/// Serializes a serializable object into a `Vec` of bytes.
-///
-/// If the serialization would take more bytes than allowed by `size_limit`,
-/// an error is returned.
-pub fn serialize<T: ?Sized, S>(value: &T, size_limit: S) -> internal::Result<Vec<u8>>
-    where T: serde_crate::Serialize, S: SizeLimit
-{
-    internal::serialize::<_, _, byteorder::LittleEndian>(value, size_limit)
-}
-
-/// A limit on the amount of bytes that can be read or written.
-///
-/// Size limits are an incredibly important part of both encoding and decoding.
-///
-/// In order to prevent DOS attacks on a decoder, it is important to limit the
-/// amount of bytes that a single encoded message can be; otherwise, if you
-/// are decoding bytes right off of a TCP stream for example, it would be
-/// possible for an attacker to flood your server with a 3TB vec, causing the
-/// decoder to run out of memory and crash your application!
-/// Because of this, you can provide a maximum-number-of-bytes that can be read
-/// during decoding, and the decoder will explicitly fail if it has to read
-/// any more than that.
-///
-/// On the other side, you want to make sure that you aren't encoding a message
-/// that is larger than your decoder expects.  By supplying a size limit to an
-/// encoding function, the encoder will verify that the structure can be encoded
-/// within that limit.  This verification occurs before any bytes are written to
-/// the Writer, so recovering from an error is easy.
-pub trait SizeLimit {
-    /// Tells the SizeLimit that a certain number of bytes has been
-    /// read or written.  Returns Err if the limit has been exceeded.
-    fn add(&mut self, n: u64) -> Result<()>;
-    /// Returns the hard limit (if one exists)
-    fn limit(&self) -> Option<u64>;
-}
-
-/// A SizeLimit that restricts serialized or deserialized messages from
-/// exceeding a certain byte length.
-#[derive(Copy, Clone)]
-pub struct Bounded(pub u64);
-
-/// A SizeLimit without a limit!
-/// Use this if you don't care about the size of encoded or decoded messages.
-#[derive(Copy, Clone)]
-pub struct Infinite;
-
-impl SizeLimit for Bounded {
-    #[inline(always)]
-    fn add(&mut self, n: u64) -> Result<()> {
-        if self.0 >= n {
-            self.0 -= n;
-            Ok(())
-        } else {
-            Err(Box::new(ErrorKind::SizeLimit))
-        }
-    }
-
-    #[inline(always)]
-    fn limit(&self) -> Option<u64> { Some(self.0) }
-}
-
-impl SizeLimit for Infinite {
-    #[inline(always)]
-    fn add(&mut self, _: u64) -> Result<()> { Ok (()) }
-
-    #[inline(always)]
-    fn limit(&self) -> Option<u64> { None }
-}
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/src/ser/mod.rs
+++ /dev/null
@@ -1,687 +0,0 @@
-use std::io::Write;
-use std::u32;
-use std::marker::PhantomData;
-
-use serde_crate as serde;
-
-use byteorder::{WriteBytesExt, ByteOrder};
-
-use super::{Result, Error, ErrorKind};
-use super::SizeLimit;
-
-/// An Serializer that encodes values directly into a Writer.
-///
-/// The specified byte-order will impact the endianness that is
-/// used during the encoding.
-///
-/// This struct should not be used often.
-/// For most cases, prefer the `encode_into` function.
-pub struct Serializer<W, E: ByteOrder> {
-    writer: W,
-    _phantom: PhantomData<E>,
-}
-
-impl<W: Write, E: ByteOrder> Serializer<W, E> {
-    /// Creates a new Serializer with the given `Write`r.
-    pub fn new(w: W) -> Serializer<W, E> {
-        Serializer {
-            writer: w,
-            _phantom: PhantomData,
-        }
-    }
-}
-
-impl<'a, W: Write, E: ByteOrder> serde::Serializer for &'a mut Serializer<W, E> {
-    type Ok = ();
-    type Error = Error;
-    type SerializeSeq = Compound<'a, W, E>;
-    type SerializeTuple = Compound<'a, W, E>;
-    type SerializeTupleStruct = Compound<'a, W, E>;
-    type SerializeTupleVariant = Compound<'a, W, E>;
-    type SerializeMap = Compound<'a, W, E>;
-    type SerializeStruct = Compound<'a, W, E>;
-    type SerializeStructVariant = Compound<'a, W, E>;
-
-    fn serialize_unit(self) -> Result<()> { Ok(()) }
-
-    fn serialize_unit_struct(self, _: &'static str) -> Result<()> { Ok(()) }
-
-    fn serialize_bool(self, v: bool) -> Result<()> {
-        self.writer.write_u8(if v {1} else {0}).map_err(Into::into)
-    }
-
-    fn serialize_u8(self, v: u8) -> Result<()> {
-        self.writer.write_u8(v).map_err(Into::into)
-    }
-
-    fn serialize_u16(self, v: u16) -> Result<()> {
-        self.writer.write_u16::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_u32(self, v: u32) -> Result<()> {
-        self.writer.write_u32::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_u64(self, v: u64) -> Result<()> {
-        self.writer.write_u64::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_i8(self, v: i8) -> Result<()> {
-        self.writer.write_i8(v).map_err(Into::into)
-    }
-
-    fn serialize_i16(self, v: i16) -> Result<()> {
-        self.writer.write_i16::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_i32(self, v: i32) -> Result<()> {
-        self.writer.write_i32::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_i64(self, v: i64) -> Result<()> {
-        self.writer.write_i64::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_f32(self, v: f32) -> Result<()> {
-        self.writer.write_f32::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_f64(self, v: f64) -> Result<()> {
-        self.writer.write_f64::<E>(v).map_err(Into::into)
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        try!(self.serialize_u64(v.len() as u64));
-        self.writer.write_all(v.as_bytes()).map_err(Into::into)
-    }
-
-    fn serialize_char(self, c: char) -> Result<()> {
-        self.writer.write_all(encode_utf8(c).as_slice()).map_err(Into::into)
-    }
-
-    fn serialize_bytes(self, v: &[u8]) -> Result<()> {
-        try!(self.serialize_u64(v.len() as u64));
-        self.writer.write_all(v).map_err(Into::into)
-    }
-
-    fn serialize_none(self) -> Result<()> {
-        self.writer.write_u8(0).map_err(Into::into)
-    }
-
-    fn serialize_some<T: ?Sized>(self, v: &T) -> Result<()>
-        where T: serde::Serialize,
-    {
-        try!(self.writer.write_u8(1));
-        v.serialize(self)
-    }
-
-    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
-        let len = try!(len.ok_or(ErrorKind::SequenceMustHaveLength));
-        try!(self.serialize_u64(len as u64));
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeTupleStruct> {
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_tuple_variant(self,
-                              _name: &'static str,
-                              variant_index: u32,
-                              _variant: &'static str,
-                              _len: usize) -> Result<Self::SerializeTupleVariant>
-    {
-        try!(self.serialize_u32(variant_index));
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
-        let len = try!(len.ok_or(ErrorKind::SequenceMustHaveLength));
-        try!(self.serialize_u64(len as u64));
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_struct_variant(self,
-                               _name: &'static str,
-                               variant_index: u32,
-                               _variant: &'static str,
-                               _len: usize) -> Result<Self::SerializeStructVariant>
-    {
-        try!(self.serialize_u32(variant_index));
-        Ok(Compound {ser: self})
-    }
-
-    fn serialize_newtype_struct<T: ?Sized>(self,
-                               _name: &'static str,
-                               value: &T) -> Result<()>
-        where T: serde::ser::Serialize,
-    {
-        value.serialize(self)
-    }
-
-    fn serialize_newtype_variant<T: ?Sized>(self,
-                               _name: &'static str,
-                               variant_index: u32,
-                               _variant: &'static str,
-                               value: &T) -> Result<()>
-        where T: serde::ser::Serialize,
-    {
-        try!(self.serialize_u32(variant_index));
-        value.serialize(self)
-    }
-
-    fn serialize_unit_variant(self,
-                          _name: &'static str,
-                          variant_index: u32,
-                          _variant: &'static str) -> Result<()> {
-        self.serialize_u32(variant_index)
-    }
-}
-
-pub struct SizeChecker<S: SizeLimit> {
-    pub size_limit: S,
-}
-
-impl <S: SizeLimit> SizeChecker<S> {
-    pub fn new(size_limit: S) -> SizeChecker<S> {
-        SizeChecker {
-            size_limit: size_limit
-        }
-    }
-
-    fn add_raw(&mut self, size: u64) -> Result<()> {
-        self.size_limit.add(size)
-    }
-
-    fn add_value<T>(&mut self, t: T) -> Result<()> {
-        use std::mem::size_of_val;
-        self.add_raw(size_of_val(&t) as u64)
-    }
-}
-
-impl<'a, S: SizeLimit> serde::Serializer for &'a mut SizeChecker<S> {
-    type Ok = ();
-    type Error = Error;
-    type SerializeSeq = SizeCompound<'a, S>;
-    type SerializeTuple = SizeCompound<'a, S>;
-    type SerializeTupleStruct = SizeCompound<'a, S>;
-    type SerializeTupleVariant = SizeCompound<'a, S>;
-    type SerializeMap = SizeCompound<'a, S>;
-    type SerializeStruct = SizeCompound<'a, S>;
-    type SerializeStructVariant = SizeCompound<'a, S>;
-
-    fn serialize_unit(self) -> Result<()> { Ok(()) }
-
-    fn serialize_unit_struct(self, _: &'static str) -> Result<()> { Ok(()) }
-
-    fn serialize_bool(self, _: bool) -> Result<()> {
-        self.add_value(0 as u8)
-    }
-
-    fn serialize_u8(self, v: u8) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_u16(self, v: u16) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_u32(self, v: u32) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_u64(self, v: u64) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_i8(self, v: i8) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_i16(self, v: i16) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_i32(self, v: i32) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_i64(self, v: i64) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_f32(self, v: f32) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_f64(self, v: f64) -> Result<()> {
-        self.add_value(v)
-    }
-
-    fn serialize_str(self, v: &str) -> Result<()> {
-        try!(self.add_value(0 as u64));
-        self.add_raw(v.len() as u64)
-    }
-
-    fn serialize_char(self, c: char) -> Result<()> {
-        self.add_raw(encode_utf8(c).as_slice().len() as u64)
-    }
-
-    fn serialize_bytes(self, v: &[u8]) -> Result<()> {
-        try!(self.add_value(0 as u64));
-        self.add_raw(v.len() as u64)
-    }
-
-    fn serialize_none(self) -> Result<()> {
-        self.add_value(0 as u8)
-    }
-
-    fn serialize_some<T: ?Sized>(self, v: &T) -> Result<()>
-        where T: serde::Serialize,
-    {
-        try!(self.add_value(1 as u8));
-        v.serialize(self)
-    }
-
-    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
-        let len = try!(len.ok_or(ErrorKind::SequenceMustHaveLength));
-
-        try!(self.serialize_u64(len as u64));
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeTupleStruct> {
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_tuple_variant(self,
-                         _name: &'static str,
-                         variant_index: u32,
-                         _variant: &'static str,
-                         _len: usize) -> Result<Self::SerializeTupleVariant>
-    {
-        try!(self.add_value(variant_index));
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap>
-    {
-        let len = try!(len.ok_or(ErrorKind::SequenceMustHaveLength));
-
-        try!(self.serialize_u64(len as u64));
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_struct_variant(self,
-                               _name: &'static str,
-                               variant_index: u32,
-                               _variant: &'static str,
-                               _len: usize) -> Result<Self::SerializeStructVariant>
-    {
-        try!(self.add_value(variant_index));
-        Ok(SizeCompound {ser: self})
-    }
-
-    fn serialize_newtype_struct<V: serde::Serialize + ?Sized>(self, _name: &'static str, v: &V) -> Result<()> {
-        v.serialize(self)
-    }
-
-    fn serialize_unit_variant(self,
-                          _name: &'static str,
-                          variant_index: u32,
-                          _variant: &'static str) -> Result<()> {
-        self.add_value(variant_index)
-    }
-
-    fn serialize_newtype_variant<V: serde::Serialize + ?Sized>(self,
-                               _name: &'static str,
-                               variant_index: u32,
-                               _variant: &'static str,
-                               value: &V) -> Result<()>
-    {
-        try!(self.add_value(variant_index));
-        value.serialize(self)
-    }
-}
-
-#[doc(hidden)]
-pub struct Compound<'a, W: 'a, E: ByteOrder + 'a> {
-    ser: &'a mut Serializer<W, E>,
-}
-
-impl<'a, W, E> serde::ser::SerializeSeq for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, E> serde::ser::SerializeTuple for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, E> serde::ser::SerializeTupleStruct for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, E> serde::ser::SerializeTupleVariant for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, E> serde::ser::SerializeMap for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_key<K: ?Sized>(&mut self, value: &K) -> Result<()>
-    where K: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-        #[inline]
-    fn serialize_value<V: ?Sized>(&mut self, value: &V) -> Result<()>
-    where V: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, E> serde::ser::SerializeStruct for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, W, E> serde::ser::SerializeStructVariant for Compound<'a, W, E>
-    where W: Write, E: ByteOrder
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-#[doc(hidden)]
-pub struct SizeCompound<'a, S: SizeLimit + 'a> {
-    ser: &'a mut SizeChecker<S>,
-}
-
-impl<'a, S: SizeLimit> serde::ser::SerializeSeq for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, S: SizeLimit> serde::ser::SerializeTuple for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, S: SizeLimit> serde::ser::SerializeTupleStruct for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, S: SizeLimit> serde::ser::SerializeTupleVariant for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, S: SizeLimit + 'a> serde::ser::SerializeMap for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_key<K: ?Sized>(&mut self, value: &K) -> Result<()>
-    where K: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-        #[inline]
-    fn serialize_value<V: ?Sized>(&mut self, value: &V) -> Result<()>
-    where V: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, S: SizeLimit> serde::ser::SerializeStruct for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl<'a, S: SizeLimit> serde::ser::SerializeStructVariant for SizeCompound<'a, S>
-{
-    type Ok = ();
-    type Error = Error;
-
-    #[inline]
-    fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<()>
-    where T: serde::ser::Serialize
-    {
-        value.serialize(&mut *self.ser)
-    }
-
-    #[inline]
-    fn end(self) -> Result<()> {
-        Ok(())
-    }
-}
-const TAG_CONT: u8    = 0b1000_0000;
-const TAG_TWO_B: u8   = 0b1100_0000;
-const TAG_THREE_B: u8 = 0b1110_0000;
-const TAG_FOUR_B: u8  = 0b1111_0000;
-const MAX_ONE_B: u32   =     0x80;
-const MAX_TWO_B: u32   =    0x800;
-const MAX_THREE_B: u32 =  0x10000;
-
-fn encode_utf8(c: char) -> EncodeUtf8 {
-    let code = c as u32;
-    let mut buf = [0; 4];
-    let pos = if code < MAX_ONE_B {
-        buf[3] = code as u8;
-        3
-    } else if code < MAX_TWO_B {
-        buf[2] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
-        buf[3] = (code & 0x3F) as u8 | TAG_CONT;
-        2
-    } else if code < MAX_THREE_B {
-        buf[1] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
-        buf[2] = (code >>  6 & 0x3F) as u8 | TAG_CONT;
-        buf[3] = (code & 0x3F) as u8 | TAG_CONT;
-        1
-    } else {
-        buf[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
-        buf[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT;
-        buf[2] = (code >>  6 & 0x3F) as u8 | TAG_CONT;
-        buf[3] = (code & 0x3F) as u8 | TAG_CONT;
-        0
-    };
-    EncodeUtf8 { buf: buf, pos: pos }
-}
-
-struct EncodeUtf8 {
-    buf: [u8; 4],
-    pos: usize,
-}
-
-impl EncodeUtf8 {
-    fn as_slice(&self) -> &[u8] {
-        &self.buf[self.pos..]
-    }
-}
deleted file mode 100644
--- a/third_party/rust/bincode-0.8.0/tests/test.rs
+++ /dev/null
@@ -1,419 +0,0 @@
-#[macro_use]
-extern crate serde_derive;
-
-extern crate bincode;
-extern crate serde;
-extern crate serde_bytes;
-extern crate byteorder;
-
-use std::fmt::Debug;
-use std::collections::HashMap;
-use std::borrow::Cow;
-
-use bincode::{Infinite, Bounded};
-use bincode::{serialized_size, ErrorKind, Result};
-use bincode::internal::{serialize, deserialize, deserialize_from};
-
-use bincode::serialize as serialize_little;
-use bincode::deserialize as deserialize_little;
-use bincode::deserialize_from as deserialize_from_little;
-
-fn the_same<V>(element: V)
-    where V: serde::Serialize+serde::de::DeserializeOwned+PartialEq+Debug+'static
-{
-    let size = serialized_size(&element);
-
-    {
-        let encoded = serialize_little(&element, Infinite).unwrap();
-        let decoded = deserialize_little(&encoded[..]).unwrap();
-
-        assert_eq!(element, decoded);
-        assert_eq!(size, encoded.len() as u64);
-    }
-
-    {
-        let encoded = serialize::<_, _, byteorder::BigEndian>(&element, Infinite).unwrap();
-        let decoded = deserialize::<_, byteorder::BigEndian>(&encoded[..]).unwrap();
-        let decoded_reader = deserialize_from::<_, _, _, byteorder::BigEndian>(&mut &encoded[..], Infinite).unwrap();
-
-        assert_eq!(element, decoded);
-        assert_eq!(element, decoded_reader);
-        assert_eq!(size, encoded.len() as u64);
-    }
-}
-
-#[test]
-fn test_numbers() {
-    // unsigned positive
-    the_same(5u8);
-    the_same(5u16);
-    the_same(5u32);
-    the_same(5u64);
-    the_same(5usize);
-    // signed positive
-    the_same(5i8);
-    the_same(5i16);
-    the_same(5i32);
-    the_same(5i64);
-    the_same(5isize);
-    // signed negative
-    the_same(-5i8);
-    the_same(-5i16);
-    the_same(-5i32);
-    the_same(-5i64);
-    the_same(-5isize);
-    // floating
-    the_same(-100f32);
-    the_same(0f32);
-    the_same(5f32);
-    the_same(-100f64);
-    the_same(5f64);
-}
-
-#[test]
-fn test_string() {
-    the_same("".to_string());
-    the_same("a".to_string());
-}
-
-#[test]
-fn test_tuple() {
-    the_same((1isize,));
-    the_same((1isize,2isize,3isize));
-    the_same((1isize,"foo".to_string(),()));
-}
-
-#[test]
-fn test_basic_struct() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    struct Easy {
-        x: isize,
-        s: String,
-        y: usize
-    }
-    the_same(Easy{x: -4, s: "foo".to_string(), y: 10});
-}
-
-#[test]
-fn test_nested_struct() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    struct Easy {
-        x: isize,
-        s: String,
-        y: usize
-    }
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    struct Nest {
-        f: Easy,
-        b: usize,
-        s: Easy
-    }
-
-    the_same(Nest {
-        f: Easy {x: -1, s: "foo".to_string(), y: 20},
-        b: 100,
-        s: Easy {x: -100, s: "bar".to_string(), y: 20}
-    });
-}
-
-#[test]
-fn test_struct_newtype() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    struct NewtypeStr(usize);
-
-    the_same(NewtypeStr(5));
-}
-
-#[test]
-fn test_struct_tuple() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    struct TubStr(usize, String, f32);
-
-    the_same(TubStr(5, "hello".to_string(), 3.2));
-}
-
-#[test]
-fn test_option() {
-    the_same(Some(5usize));
-    the_same(Some("foo bar".to_string()));
-    the_same(None::<usize>);
-}
-
-#[test]
-fn test_enum() {
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    enum TestEnum {
-        NoArg,
-        OneArg(usize),
-        Args(usize, usize),
-        AnotherNoArg,
-        StructLike{x: usize, y: f32}
-    }
-    the_same(TestEnum::NoArg);
-    the_same(TestEnum::OneArg(4));
-    //the_same(TestEnum::Args(4, 5));
-    the_same(TestEnum::AnotherNoArg);
-    the_same(TestEnum::StructLike{x: 4, y: 3.14159});
-    the_same(vec![TestEnum::NoArg, TestEnum::OneArg(5), TestEnum::AnotherNoArg,
-                  TestEnum::StructLike{x: 4, y:1.4}]);
-}
-
-#[test]
-fn test_vec() {
-    let v: Vec<u8> = vec![];
-    the_same(v);
-    the_same(vec![1u64]);
-    the_same(vec![1u64,2,3,4,5,6]);
-}
-
-#[test]
-fn test_map() {
-    let mut m = HashMap::new();
-    m.insert(4u64, "foo".to_string());
-    m.insert(0u64, "bar".to_string());
-    the_same(m);
-}
-
-#[test]
-fn test_bool() {
-    the_same(true);
-    the_same(false);
-}
-
-#[test]
-fn test_unicode() {
-    the_same("å".to_string());
-    the_same("aåååååååa".to_string());
-}
-
-#[test]
-fn test_fixed_size_array() {
-    the_same([24u32; 32]);
-    the_same([1u64, 2, 3, 4, 5, 6, 7, 8]);
-    the_same([0u8; 19]);
-}
-
-#[test]
-fn deserializing_errors() {
-    fn isize_invalid_deserialize<T: Debug>(res: Result<T>) {
-        match res.map_err(|e| *e) {
-            Err(ErrorKind::InvalidEncoding{..}) => {},
-            Err(ErrorKind::Custom(ref s)) if s.contains("invalid encoding") => {},
-            Err(ErrorKind::Custom(ref s)) if s.contains("invalid value") => {},
-            other => panic!("Expecting InvalidEncoding, got {:?}", other),
-        }
-    }
-
-    isize_invalid_deserialize(deserialize_little::<bool>(&vec![0xA][..]));
-    isize_invalid_deserialize(deserialize_little::<String>(&vec![1, 0, 0, 0, 0, 0, 0, 0, 0xFF][..]));
-    // Out-of-bounds variant
-    #[derive(Serialize, Deserialize, Debug)]
-    enum Test {
-        One,
-        Two,
-    };
-    isize_invalid_deserialize(deserialize_little::<Test>(&vec![0, 0, 0, 5][..]));
-    isize_invalid_deserialize(deserialize_little::<Option<u8>>(&vec![5, 0][..]));
-}
-
-#[test]
-fn too_big_deserialize() {
-    let serialized = vec![0,0,0,3];
-    let deserialized: Result<u32> = deserialize_from_little::<_, _, _>(&mut &serialized[..], Bounded(3));
-    assert!(deserialized.is_err());
-
-    let serialized = vec![0,0,0,3];
-    let deserialized: Result<u32> = deserialize_from_little::<_, _, _>(&mut &serialized[..], Bounded(4));
-    assert!(deserialized.is_ok());
-}
-
-#[test]
-fn char_serialization() {
-    let chars = "Aa\0☺♪";
-    for c in chars.chars() {
-        let encoded = serialize_little(&c, Bounded(4)).expect("serializing char failed");
-        let decoded: char = deserialize_little(&encoded).expect("deserializing failed");
-        assert_eq!(decoded, c);
-    }
-}
-
-#[test]
-fn too_big_char_deserialize() {
-    let serialized = vec![0x41];
-    let deserialized: Result<char> = deserialize_from_little::<_, _, _>(&mut &serialized[..], Bounded(1));
-    assert!(deserialized.is_ok());
-    assert_eq!(deserialized.unwrap(), 'A');
-}
-
-#[test]
-fn too_big_serialize() {
-    assert!(serialize_little(&0u32, Bounded(3)).is_err());
-    assert!(serialize_little(&0u32, Bounded(4)).is_ok());
-
-    assert!(serialize_little(&"abcde", Bounded(8 + 4)).is_err());
-    assert!(serialize_little(&"abcde", Bounded(8 + 5)).is_ok());
-}
-
-#[test]
-fn test_proxy_encoded_size() {
-    assert!(serialized_size(&0u8) == 1);
-    assert!(serialized_size(&0u16) == 2);
-    assert!(serialized_size(&0u32) == 4);
-    assert!(serialized_size(&0u64) == 8);
-
-    // length isize stored as u64
-    assert!(serialized_size(&"") == 8);
-    assert!(serialized_size(&"a") == 8 + 1);
-
-    assert!(serialized_size(&vec![0u32, 1u32, 2u32]) == 8 + 3 * (4))
-
-}
-
-#[test]
-fn test_serialized_size() {
-    assert!(serialized_size(&0u8) == 1);
-    assert!(serialized_size(&0u16) == 2);
-    assert!(serialized_size(&0u32) == 4);
-    assert!(serialized_size(&0u64) == 8);
-
-    // length isize stored as u64
-    assert!(serialized_size(&"") == 8);
-    assert!(serialized_size(&"a") == 8 + 1);
-
-    assert!(serialized_size(&vec![0u32, 1u32, 2u32]) == 8 + 3 * (4))
-}
-
-#[test]
-fn encode_box() {
-    the_same(Box::new(5));
-}
-
-#[test]
-fn test_cow_serialize() {
-    let large_object = vec![1u32,2,3,4,5,6];
-    let mut large_map = HashMap::new();
-    large_map.insert(1, 2);
-
-
-    #[derive(Serialize, Deserialize, Debug)]
-    enum Message<'a> {
-        M1(Cow<'a, Vec<u32>>),
-        M2(Cow<'a, HashMap<u32, u32>>)
-    }
-
-    // Test 1
-    {
-        let serialized = serialize_little(&Message::M1(Cow::Borrowed(&large_object)), Infinite).unwrap();
-        let deserialized: Message<'static> = deserialize_from_little(&mut &serialized[..], Infinite).unwrap();
-
-        match deserialized {
-            Message::M1(b) => assert!(&b.into_owned() == &large_object),
-            _ => assert!(false)
-        }
-    }
-
-    // Test 2
-    {
-        let serialized = serialize_little(&Message::M2(Cow::Borrowed(&large_map)), Infinite).unwrap();
-        let deserialized: Message<'static> = deserialize_from_little(&mut &serialized[..], Infinite).unwrap();
-
-        match deserialized {
-            Message::M2(b) => assert!(&b.into_owned() == &large_map),
-            _ => assert!(false)
-        }
-    }
-}
-
-#[test]
-fn test_strbox_serialize() {
-    let strx: &'static str = "hello world";
-    let serialized = serialize_little(&Cow::Borrowed(strx), Infinite).unwrap();
-    let deserialized: Cow<'static, String> = deserialize_from_little(&mut &serialized[..], Infinite).unwrap();
-    let stringx: String = deserialized.into_owned();
-    assert!(strx == &stringx[..]);
-}
-
-#[test]
-fn test_slicebox_serialize() {
-    let slice = [1u32, 2, 3 ,4, 5];
-    let serialized = serialize_little(&Cow::Borrowed(&slice[..]), Infinite).unwrap();
-    println!("{:?}", serialized);
-    let deserialized: Cow<'static, Vec<u32>> = deserialize_from_little(&mut &serialized[..], Infinite).unwrap();
-    {
-        let sb: &[u32] = &deserialized;
-        assert!(slice == sb);
-    }
-    let vecx: Vec<u32> = deserialized.into_owned();
-    assert!(slice == &vecx[..]);
-}
-
-#[test]
-fn test_multi_strings_serialize() {
-    assert!(serialize_little(&("foo", "bar", "baz"), Infinite).is_ok());
-}
-
-#[test]
-fn test_oom_protection() {
-    use std::io::Cursor;
-    #[derive(Serialize, Deserialize, PartialEq, Debug)]
-    struct FakeVec {
-        len: u64,
-        byte: u8
-    }
-    let x = serialize_little(&FakeVec { len: 0xffffffffffffffffu64, byte: 1 }, Bounded(10)).unwrap();
-    let y: Result<Vec<u8>> = deserialize_from_little(&mut Cursor::new(&x[..]), Bounded(10));
-    assert!(y.is_err());
-}
-
-#[test]
-fn path_buf() {
-    use std::path::{Path, PathBuf};
-    let path = Path::new("foo").to_path_buf();
-    let serde_encoded = serialize_little(&path, Infinite).unwrap();
-    let decoded: PathBuf = deserialize_little(&serde_encoded).unwrap();
-    assert!(path.to_str() == decoded.to_str());
-}
-
-#[test]
-fn bytes() {
-    use serde_bytes::Bytes;
-
-    let data = b"abc\0123";
-    let s = serialize_little(&data[..], Infinite).unwrap();
-    let s2 = serialize_little(&Bytes::new(data), Infinite).unwrap();
-    assert_eq!(s[..], s2[..]);
-}
-
-#[test]
-fn serde_bytes() {
-    use serde_bytes::ByteBuf;
-    the_same(ByteBuf::from(vec![1,2,3,4,5]));
-}
-
-
-#[test]
-fn endian_difference() {
-    let x = 10u64;
-    let little = serialize_little(&x, Infinite).unwrap();
-    let big = serialize::<_, _, byteorder::BigEndian>(&x, Infinite).unwrap();
-    assert_ne!(little, big);
-}
-
-#[test]
-fn test_zero_copy_parse() {
-    #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
-    struct Foo<'a> {
-        borrowed_str: &'a str,
-        borrowed_bytes: &'a [u8],
-    }
-
-    let f = Foo {
-        borrowed_str: "hi",
-        borrowed_bytes: &[0, 1, 2, 3],
-    };
-    {
-        let encoded = serialize_little(&f, Infinite).unwrap();
-        let out: Foo = deserialize_little(&encoded[..]).unwrap();
-        assert_eq!(out, f);
-    }
-}
--- a/third_party/rust/cubeb-core/.cargo-checksum.json
+++ b/third_party/rust/cubeb-core/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"2fc45d9fc64c5d59fb35be6a68cc26c56843ffa8da258a8a6616d6b9962caa53","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"f4f4764697c65c08bfbf156005c5f40a43bb8b9ae72a4986dc8b5252537de7ec","src/channel.rs":"bc1e970d9a32ffa9f51f285bce511c20f33c4dd56add27d0bb27273e6483aa90","src/context.rs":"c1ab7873702a7b5634a3e83ab4cec9d5db14965fcd17e28d824a739318c0d424","src/device.rs":"490d2e94ecae1e149476c2e8d9aa03c4163987c3efccc962b2d3123e4c09dedf","src/device_collection.rs":"5c492120c0c2ee460d0042320f6ce38711ff38443687bc77b5b9f13369755d68","src/error.rs":"855ff3d3597753f832ecea00e403c71129afd80db3d39456cf3e23cb9aeb91e7","src/ffi_types.rs":"d815d7a80895b5e86907e708dc0219fca4ac4668cde114afee434e7d702a145d","src/format.rs":"5513c537a72af1c222ee7c30b26d4de9d368a69772688b95d88b1a99f6892d5c","src/lib.rs":"6010a5e20b836b8e5c9fba382fde819e6f3c18c0ec2016e6e7e118eabedbcd51","src/log.rs":"c46bae3472043fd076df3229c3421d948a87fae8495c1524b41ab2d8608f612a","src/stream.rs":"8f28a8a7b1ef2567f6764a36ebe2ecfde5f01dcd65befc7599684d82f4a390d6","src/try_call.rs":"020a4c9a795d5c6998a089b45ae0b8132e0421f2a229fe2014afad55c7086d32","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"1ac5bb9e9d8747ae2c0884c970852c158315d809e33ce7c6a7b42b80b6a376aa"}
\ No newline at end of file
+{"files":{"Cargo.toml":"2c1583ea21c913bbd37de181da827b82075300f40e355dcb24455ee5cb3df6d6","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"f4f4764697c65c08bfbf156005c5f40a43bb8b9ae72a4986dc8b5252537de7ec","src/channel.rs":"bc1e970d9a32ffa9f51f285bce511c20f33c4dd56add27d0bb27273e6483aa90","src/context.rs":"c1ab7873702a7b5634a3e83ab4cec9d5db14965fcd17e28d824a739318c0d424","src/device.rs":"490d2e94ecae1e149476c2e8d9aa03c4163987c3efccc962b2d3123e4c09dedf","src/device_collection.rs":"f6d0c1628cc34b524f86b84a1e1c79971c3f64ebc4ac64eeb10a1330bbe8c238","src/error.rs":"855ff3d3597753f832ecea00e403c71129afd80db3d39456cf3e23cb9aeb91e7","src/ffi_types.rs":"d815d7a80895b5e86907e708dc0219fca4ac4668cde114afee434e7d702a145d","src/format.rs":"5513c537a72af1c222ee7c30b26d4de9d368a69772688b95d88b1a99f6892d5c","src/lib.rs":"6010a5e20b836b8e5c9fba382fde819e6f3c18c0ec2016e6e7e118eabedbcd51","src/log.rs":"c46bae3472043fd076df3229c3421d948a87fae8495c1524b41ab2d8608f612a","src/stream.rs":"28fce5a49384437f5106b0575aebfb09d7b1875e4adb53d57102d79666bbab43","src/try_call.rs":"231bfa3f3448f7531427bb228beb2bcd4fd711f0b13d2d8f412af013470f40c7","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"a6558e6b99511df1b4e7ce219b26527b0cb0f6f56df2fd5877aa9473b7c7eafb"}
\ No newline at end of file
--- a/third_party/rust/cubeb-core/Cargo.toml
+++ b/third_party/rust/cubeb-core/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cubeb-core"
-version = "0.4.2"
+version = "0.4.3"
 authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
 description = "Common types and definitions for cubeb rust and C bindings. Not intended for direct use.\n"
 homepage = "https://github.com/djg/cubeb-rs"
 keywords = ["cubeb"]
 categories = ["api-bindings"]
 license = "ISC"
 repository = "https://github.com/djg/cubeb-rs"
 [dependencies.bitflags]
--- a/third_party/rust/cubeb-core/src/device_collection.rs
+++ b/third_party/rust/cubeb-core/src/device_collection.rs
@@ -22,17 +22,17 @@ impl<'ctx> DeviceCollection<'ctx> {
     pub fn as_ptr(&self) -> *mut CType {
         &self.0 as *const CType as *mut CType
     }
 }
 
 impl<'ctx> Drop for DeviceCollection<'ctx> {
     fn drop(&mut self) {
         unsafe {
-            let _ = try_call!(ffi::cubeb_device_collection_destroy(
+            let _ = call!(ffi::cubeb_device_collection_destroy(
                 self.1.as_ptr(),
                 &mut self.0
             ));
         }
     }
 }
 
 impl<'ctx> ::std::ops::Deref for DeviceCollection<'ctx> {
--- a/third_party/rust/cubeb-core/src/stream.rs
+++ b/third_party/rust/cubeb-core/src/stream.rs
@@ -126,27 +126,27 @@ ffi_type_heap! {
     fn drop = ffi::cubeb_stream_destroy;
     pub struct Stream;
     pub struct StreamRef;
 }
 
 impl StreamRef {
     /// Start playback.
     pub fn start(&self) -> Result<()> {
-        unsafe { try_call!(ffi::cubeb_stream_start(self.as_ptr())) }
+        unsafe { call!(ffi::cubeb_stream_start(self.as_ptr())) }
     }
 
     /// Stop playback.
     pub fn stop(&self) -> Result<()> {
-        unsafe { try_call!(ffi::cubeb_stream_stop(self.as_ptr())) }
+        unsafe { call!(ffi::cubeb_stream_stop(self.as_ptr())) }
     }
 
     /// Reset stream to the default device.
     pub fn reset_default_device(&self) -> Result<()> {
-        unsafe { try_call!(ffi::cubeb_stream_reset_default_device(self.as_ptr())) }
+        unsafe { call!(ffi::cubeb_stream_reset_default_device(self.as_ptr())) }
     }
 
     /// Get the current stream playback position.
     pub fn position(&self) -> Result<u64> {
         let mut position = 0u64;
         unsafe {
             let _ = try_call!(ffi::cubeb_stream_get_position(self.as_ptr(), &mut position));
         }
@@ -161,28 +161,28 @@ impl StreamRef {
         unsafe {
             let _ = try_call!(ffi::cubeb_stream_get_latency(self.as_ptr(), &mut latency));
         }
         Ok(latency)
     }
 
     /// Set the volume for a stream.
     pub fn set_volume(&self, volume: f32) -> Result<()> {
-        unsafe { try_call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
+        unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
     }
 
     /// If the stream is stereo, set the left/right panning. If the stream is mono,
     /// this has no effect.
     ///
     /// panning a number from -1.0 to 1.0. -1.0 means that the stream is
     /// fully mixed in the left channel, 1.0 means the stream is fully
     /// mixed in the right channel. 0.0 is equal power in the right
     /// and left channel (default).
     pub fn set_panning(&self, panning: f32) -> Result<()> {
-        unsafe { try_call!(ffi::cubeb_stream_set_panning(self.as_ptr(), panning)) }
+        unsafe { call!(ffi::cubeb_stream_set_panning(self.as_ptr(), panning)) }
     }
 
     /// Get the current output device for this stream.
     pub fn current_device(&self) -> Result<&DeviceRef> {
         let mut device: *mut ffi::cubeb_device = ptr::null_mut();
         unsafe {
             let _ = try_call!(ffi::cubeb_stream_get_current_device(
                 self.as_ptr(),
@@ -190,30 +190,30 @@ impl StreamRef {
             ));
             Ok(DeviceRef::from_ptr(device))
         }
     }
 
     /// Destroy a cubeb_device structure.
     pub fn device_destroy(&self, device: DeviceRef) -> Result<()> {
         unsafe {
-            try_call!(ffi::cubeb_stream_device_destroy(
+            call!(ffi::cubeb_stream_device_destroy(
                 self.as_ptr(),
                 device.as_ptr()
             ))
         }
     }
 
     /// Set a callback to be notified when the output device changes.
     pub fn register_device_changed_callback(
         &self,
         callback: ffi::cubeb_device_changed_callback,
     ) -> Result<()> {
         unsafe {
-            try_call!(ffi::cubeb_stream_register_device_changed_callback(
+            call!(ffi::cubeb_stream_register_device_changed_callback(
                 self.as_ptr(),
                 callback
             ))
         }
     }
 
     pub fn user_ptr(&self) -> *mut c_void {
         unsafe { ffi::cubeb_stream_user_ptr(self.as_ptr()) }
--- a/third_party/rust/cubeb-core/src/try_call.rs
+++ b/third_party/rust/cubeb-core/src/try_call.rs
@@ -9,13 +9,19 @@ use std::os::raw::c_int;
 
 pub fn cvt_r(ret: c_int) -> Result<(), Error> {
     match ret {
         n if n < 0 => Err(unsafe { Error::from_raw(n) }),
         _ => Ok(()),
     }
 }
 
-macro_rules! try_call {
+macro_rules! call {
     (ffi::$p:ident ($($e:expr),*)) => ({
         ::try_call::cvt_r(ffi::$p($($e),*))
     })
 }
+
+macro_rules! try_call {
+    (ffi::$p:ident ($($e:expr),*)) => ({
+        try!(::try_call::cvt_r(ffi::$p($($e),*)))
+    })
+}
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -108,8 +108,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     EXTRA_COMPONENTS += [
         'nsDefaultCLH.js',
         'nsDefaultCLH.manifest',
     ]
 
 if CONFIG['NIGHTLY_BUILD'] and CONFIG['MOZ_BUILD_APP'] == 'browser':
     DIRS += ['payments']
+
+if CONFIG['MOZ_BUILD_APP'] == 'browser':
+    DIRS += ['normandy']
new file mode 100644
--- /dev/null
+++ b/toolkit/components/normandy/Normandy.jsm
@@ -0,0 +1,227 @@
+/* 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/. */
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+ChromeUtils.import("resource://gre/modules/Log.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AboutPages: "resource://normandy-content/AboutPages.jsm",
+  AddonStudies: "resource://normandy/lib/AddonStudies.jsm",
+  CleanupManager: "resource://normandy/lib/CleanupManager.jsm",
+  LogManager: "resource://normandy/lib/LogManager.jsm",
+  PreferenceExperiments: "resource://normandy/lib/PreferenceExperiments.jsm",
+  RecipeRunner: "resource://normandy/lib/RecipeRunner.jsm",
+  ShieldPreferences: "resource://normandy/lib/ShieldPreferences.jsm",
+  TelemetryEvents: "resource://normandy/lib/TelemetryEvents.jsm",
+});
+
+var EXPORTED_SYMBOLS = ["Normandy"];
+
+const UI_AVAILABLE_NOTIFICATION = "sessionstore-windows-restored";
+const BOOTSTRAP_LOGGER_NAME = "app.normandy.bootstrap";
+const SHIELD_INIT_NOTIFICATION = "shield-init-complete";
+
+const PREF_PREFIX = "app.normandy";
+const LEGACY_PREF_PREFIX = "extensions.shield-recipe-client";
+const STARTUP_EXPERIMENT_PREFS_BRANCH = `${PREF_PREFIX}.startupExperimentPrefs.`;
+const PREF_LOGGING_LEVEL = `${PREF_PREFIX}.logging.level`;
+
+// Logging
+const log = Log.repository.getLogger(BOOTSTRAP_LOGGER_NAME);
+log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
+log.level = Services.prefs.getIntPref(PREF_LOGGING_LEVEL, Log.Level.Warn);
+
+let studyPrefsChanged = {};
+
+var Normandy = {
+  init() {
+    // Initialization that needs to happen before the first paint on startup.
+    this.migrateShieldPrefs();
+    this.initExperimentPrefs();
+
+    // Wait until the UI is available before finishing initialization.
+    Services.obs.addObserver(this, UI_AVAILABLE_NOTIFICATION);
+  },
+
+  observe(subject, topic, data) {
+    if (topic === UI_AVAILABLE_NOTIFICATION) {
+      Services.obs.removeObserver(this, UI_AVAILABLE_NOTIFICATION);
+      this.finishInit();
+    }
+  },
+
+  async finishInit() {
+    await PreferenceExperiments.recordOriginalValues(studyPrefsChanged);
+
+    // Setup logging and listen for changes to logging prefs
+    LogManager.configure(Services.prefs.getIntPref(PREF_LOGGING_LEVEL, Log.Level.Warn));
+    Services.prefs.addObserver(PREF_LOGGING_LEVEL, LogManager.configure);
+    CleanupManager.addCleanupHandler(
+      () => Services.prefs.removeObserver(PREF_LOGGING_LEVEL, LogManager.configure),
+    );
+
+    try {
+      TelemetryEvents.init();
+    } catch (err) {
+      log.error("Failed to initialize telemetry events:", err);
+    }
+
+    try {
+      await AboutPages.init();
+    } catch (err) {
+      log.error("Failed to initialize about pages:", err);
+    }
+
+    try {
+      await AddonStudies.init();
+    } catch (err) {
+      log.error("Failed to initialize addon studies:", err);
+    }
+
+    try {
+      await PreferenceExperiments.init();
+    } catch (err) {
+      log.error("Failed to initialize preference experiments:", err);
+    }
+
+    try {
+      ShieldPreferences.init();
+    } catch (err) {
+      log.error("Failed to initialize preferences UI:", err);
+    }
+
+    await RecipeRunner.init();
+    Services.obs.notifyObservers(null, SHIELD_INIT_NOTIFICATION);
+  },
+
+  async uninit() {
+    await CleanupManager.cleanup();
+    Services.prefs.removeObserver(PREF_LOGGING_LEVEL, LogManager.configure);
+
+    // In case the observer didn't run, clean it up.
+    try {
+      Services.obs.removeObserver(this, UI_AVAILABLE_NOTIFICATION);
+    } catch (err) {
+      // It must already be removed!
+    }
+  },
+
+  migrateShieldPrefs() {
+    const legacyBranch = Services.prefs.getBranch(LEGACY_PREF_PREFIX + ".");
+    const newBranch = Services.prefs.getBranch(PREF_PREFIX + ".");
+
+    for (const prefName of legacyBranch.getChildList("")) {
+      const legacyPrefType = legacyBranch.getPrefType(prefName);
+      const newPrefType = newBranch.getPrefType(prefName);
+
+      // If new preference exists and is not the same as the legacy pref, skip it
+      if (newPrefType !== Services.prefs.PREF_INVALID && newPrefType !== legacyPrefType) {
+        log.error(`Error migrating normandy pref ${prefName}; pref type does not match.`);
+        continue;
+      }
+
+      // Now move the value over. If it matches the default, this will be a no-op
+      switch (legacyPrefType) {
+        case Services.prefs.PREF_STRING:
+          newBranch.setCharPref(prefName, legacyBranch.getCharPref(prefName));
+          break;
+
+        case Services.prefs.PREF_INT:
+          newBranch.setIntPref(prefName, legacyBranch.getIntPref(prefName));
+          break;
+
+        case Services.prefs.PREF_BOOL:
+          newBranch.setBoolPref(prefName, legacyBranch.getBoolPref(prefName));
+          break;
+
+        case Services.prefs.PREF_INVALID:
+          // This should never happen.
+          log.error(`Error migrating pref ${prefName}; pref type is invalid (${legacyPrefType}).`);
+          break;
+
+        default:
+          // This should never happen either.
+          log.error(`Error getting startup pref ${prefName}; unknown value type ${legacyPrefType}.`);
+      }
+
+      legacyBranch.clearUserPref(prefName);
+    }
+  },
+
+  initExperimentPrefs() {
+    studyPrefsChanged = {};
+    const defaultBranch = Services.prefs.getDefaultBranch("");
+    const experimentBranch = Services.prefs.getBranch(STARTUP_EXPERIMENT_PREFS_BRANCH);
+
+    for (const prefName of experimentBranch.getChildList("")) {
+      const experimentPrefType = experimentBranch.getPrefType(prefName);
+      const realPrefType = defaultBranch.getPrefType(prefName);
+
+      if (realPrefType !== Services.prefs.PREF_INVALID && realPrefType !== experimentPrefType) {
+        log.error(`Error setting startup pref ${prefName}; pref type does not match.`);
+        continue;
+      }
+
+      // record the value of the default branch before setting it
+      try {
+        switch (realPrefType) {
+          case Services.prefs.PREF_STRING:
+            studyPrefsChanged[prefName] = defaultBranch.getCharPref(prefName);
+            break;
+
+          case Services.prefs.PREF_INT:
+            studyPrefsChanged[prefName] = defaultBranch.getIntPref(prefName);
+            break;
+
+          case Services.prefs.PREF_BOOL:
+            studyPrefsChanged[prefName] = defaultBranch.getBoolPref(prefName);
+            break;
+
+          case Services.prefs.PREF_INVALID:
+            studyPrefsChanged[prefName] = null;
+            break;
+
+          default:
+            // This should never happen
+            log.error(`Error getting startup pref ${prefName}; unknown value type ${experimentPrefType}.`);
+        }
+      } catch (e) {
+        if (e.result === Cr.NS_ERROR_UNEXPECTED) {
+          // There is a value for the pref on the user branch but not on the default branch. This is ok.
+          studyPrefsChanged[prefName] = null;
+        } else {
+          // rethrow
+          throw e;
+        }
+      }
+
+      // now set the new default value
+      switch (experimentPrefType) {
+        case Services.prefs.PREF_STRING:
+          defaultBranch.setCharPref(prefName, experimentBranch.getCharPref(prefName));
+          break;
+
+        case Services.prefs.PREF_INT:
+          defaultBranch.setIntPref(prefName, experimentBranch.getIntPref(prefName));
+          break;
+
+        case Services.prefs.PREF_BOOL:
+          defaultBranch.setBoolPref(prefName, experimentBranch.getBoolPref(prefName));
+          break;
+
+        case Services.prefs.PREF_INVALID:
+          // This should never happen.
+          log.error(`Error setting startup pref ${prefName}; pref type is invalid (${experimentPrefType}).`);
+          break;
+
+        default:
+          // This should never happen either.
+          log.error(`Error getting startup pref ${prefName}; unknown value type ${experimentPrefType}.`);
+      }
+    }
+  },
+};
rename from browser/extensions/shield-recipe-client/content/AboutPages.jsm
rename to toolkit/components/normandy/content/AboutPages.jsm
--- a/browser/extensions/shield-recipe-client/content/AboutPages.jsm
+++ b/toolkit/components/normandy/content/AboutPages.jsm
@@ -3,40 +3,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Cm = Components.manager;
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(
-  this, "CleanupManager", "resource://shield-recipe-client/lib/CleanupManager.jsm",
+  this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm",
 );
 ChromeUtils.defineModuleGetter(
-  this, "AddonStudies", "resource://shield-recipe-client/lib/AddonStudies.jsm",
+  this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm",
 );
 ChromeUtils.defineModuleGetter(
-  this, "RecipeRunner", "resource://shield-recipe-client/lib/RecipeRunner.jsm",
+  this, "RecipeRunner", "resource://normandy/lib/RecipeRunner.jsm",
 );
 
 var EXPORTED_SYMBOLS = ["AboutPages"];
 
-const SHIELD_LEARN_MORE_URL_PREF = "extensions.shield-recipe-client.shieldLearnMoreUrl";
+const SHIELD_LEARN_MORE_URL_PREF = "app.normandy.shieldLearnMoreUrl";
 
 // Due to bug 1051238 frame scripts are cached forever, so we can't update them
 // as a restartless add-on. The Math.random() is the work around for this.
 const PROCESS_SCRIPT = (
-  `resource://shield-recipe-client-content/shield-content-process.js?${Math.random()}`
+  `resource://normandy-content/shield-content-process.js?${Math.random()}`
 );
 const FRAME_SCRIPT = (
-  `resource://shield-recipe-client-content/shield-content-frame.js?${Math.random()}`
+  `resource://normandy-content/shield-content-frame.js?${Math.random()}`
 );
 
 /**
- * Class for managing an about: page that Shield provides. Adapted from
+ * Class for managing an about: page that Normandy provides. Adapted from
  * browser/extensions/pocket/content/AboutPocket.jsm.
  *
  * @implements nsIFactory
  * @implements nsIAboutModule
  */
 class AboutPage {
   constructor({chromeUrl, aboutHost, classId, description, uriFlags}) {
     this.chromeUrl = chromeUrl;
@@ -121,17 +121,17 @@ var AboutPages = {
 
 /**
  * about:studies page for displaying in-progress and past Shield studies.
  * @type {AboutPage}
  * @implements {nsIMessageListener}
  */
 XPCOMUtils.defineLazyGetter(this.AboutPages, "aboutStudies", () => {
   const aboutStudies = new AboutPage({
-    chromeUrl: "resource://shield-recipe-client-content/about-studies/about-studies.html",
+    chromeUrl: "resource://normandy-content/about-studies/about-studies.html",
     aboutHost: "studies",
     classId: "{6ab96943-a163-482c-9622-4faedc0e827f}",
     description: "Shield Study Listing",
     uriFlags: (
       Ci.nsIAboutModule.ALLOW_SCRIPT
       | Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT
       | Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD
     ),
@@ -141,25 +141,27 @@ XPCOMUtils.defineLazyGetter(this.AboutPa
   Object.assign(aboutStudies, {
     /**
      * Register listeners for messages from the content processes.
      */
     registerParentListeners() {
       Services.mm.addMessageListener("Shield:GetStudyList", this);
       Services.mm.addMessageListener("Shield:RemoveStudy", this);
       Services.mm.addMessageListener("Shield:OpenDataPreferences", this);
+      Services.mm.addMessageListener("Shield:GetStudiesEnabled", this);
     },
 
     /**
      * Unregister listeners for messages from the content process.
      */
     unregisterParentListeners() {
       Services.mm.removeMessageListener("Shield:GetStudyList", this);
       Services.mm.removeMessageListener("Shield:RemoveStudy", this);
       Services.mm.removeMessageListener("Shield:OpenDataPreferences", this);
+      Services.mm.removeMessageListener("Shield:GetStudiesEnabled", this);
     },
 
     /**
      * Dispatch messages from the content process to the appropriate handler.
      * @param {Object} message
      *   See the nsIMessageListener documentation for details about this object.
      */
     receiveMessage(message) {
@@ -168,16 +170,19 @@ XPCOMUtils.defineLazyGetter(this.AboutPa
           this.sendStudyList(message.target);
           break;
         case "Shield:RemoveStudy":
           this.removeStudy(message.data.recipeId, message.data.reason);
           break;
         case "Shield:OpenDataPreferences":
           this.openDataPreferences();
           break;
+        case "Shield:GetStudiesEnabled":
+          this.sendStudiesEnabled(message.target);
+          break;
       }
     },
 
     /**
      * Fetch a list of studies from storage and send it to the process that
      * requested them.
      * @param {<browser>} target
      *   XUL <browser> element for the tab containing the about:studies page
@@ -190,16 +195,38 @@ XPCOMUtils.defineLazyGetter(this.AboutPa
         });
       } catch (err) {
         // The child process might be gone, so no need to throw here.
         Cu.reportError(err);
       }
     },
 
     /**
+     * Get if studies are enabled and send it to the process that
+     * requested them. This has to be in the parent process, since
+     * RecipeRunner is stateful, and can't be interacted with from
+     * content processes safely.
+     *
+     * @param {<browser>} target
+     *   XUL <browser> element for the tab containing the about:studies page
+     *   that requested a study list.
+     */
+    sendStudiesEnabled(target) {
+      RecipeRunner.checkPrefs();
+      try {
+        target.messageManager.sendAsyncMessage("Shield:ReceiveStudiesEnabled", {
+          studiesEnabled: RecipeRunner.enabled,
+        });
+      } catch (err) {
+        // The child process might be gone, so no need to throw here.
+        Cu.reportError(err);
+      }
+    },
+
+    /**
      * Disable an active study and remove its add-on.
      * @param {String} studyName
      */
     async removeStudy(recipeId, reason) {
       await AddonStudies.stop(recipeId, reason);
 
       // Update any open tabs with the new study list now that it has changed.
       Services.mm.broadcastAsyncMessage("Shield:ReceiveStudyList", {
rename from browser/extensions/shield-recipe-client/content/about-studies/about-studies.css
rename to toolkit/components/normandy/content/about-studies/about-studies.css
rename from browser/extensions/shield-recipe-client/content/about-studies/about-studies.html
rename to toolkit/components/normandy/content/about-studies/about-studies.html
--- a/browser/extensions/shield-recipe-client/content/about-studies/about-studies.html
+++ b/toolkit/components/normandy/content/about-studies/about-studies.html
@@ -3,21 +3,21 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
     <title>about:studies</title>
     <link rel="stylesheet" href="chrome://global/skin/global.css">
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
-    <link rel="stylesheet" href="resource://shield-recipe-client-content/about-studies/about-studies.css">
+    <link rel="stylesheet" href="resource://normandy-content/about-studies/about-studies.css">
   </head>
   <body>
     <div id="app"></div>
-    <script src="resource://shield-recipe-client-vendor/React.js"></script>
-    <script src="resource://shield-recipe-client-vendor/ReactDOM.js"></script>
-    <script src="resource://shield-recipe-client-vendor/PropTypes.js"></script>
-    <script src="resource://shield-recipe-client-vendor/classnames.js"></script>
-    <script src="resource://shield-recipe-client-content/about-studies/common.js"></script>
-    <script src="resource://shield-recipe-client-content/about-studies/shield-studies.js"></script>
-    <script src="resource://shield-recipe-client-content/about-studies/about-studies.js"></script>
+    <script src="resource://normandy-vendor/React.js"></script>
+    <script src="resource://normandy-vendor/ReactDOM.js"></script>
+    <script src="resource://normandy-vendor/PropTypes.js"></script>
+    <script src="resource://normandy-vendor/classnames.js"></script>
+    <script src="resource://normandy-content/about-studies/common.js"></script>
+    <script src="resource://normandy-content/about-studies/shield-studies.js"></script>
+    <script src="resource://normandy-content/about-studies/about-studies.js"></script>
   </body>
 </html>
rename from browser/extensions/shield-recipe-client/content/about-studies/about-studies.js
rename to toolkit/components/normandy/content/about-studies/about-studies.js
--- a/browser/extensions/shield-recipe-client/content/about-studies/about-studies.js
+++ b/toolkit/components/normandy/content/about-studies/about-studies.js
@@ -10,17 +10,17 @@
  *
  * Pages will appear in the sidebar in the order they are defined here. If the
  * URL doesn't contain a hash, the first page will be displayed in the content area.
  */
 const PAGES = new Map([
   ["shieldStudies", {
     name: "Shield Studies",
     component: ShieldStudies,
-    icon: "resource://shield-recipe-client-content/about-studies/img/shield-logo.png",
+    icon: "resource://normandy-content/about-studies/img/shield-logo.png",
   }],
 ]);
 
 /**
  * Handle basic layout and routing within about:studies.
  */
 class AboutStudies extends React.Component {
   constructor(props) {
rename from browser/extensions/shield-recipe-client/content/about-studies/common.js
rename to toolkit/components/normandy/content/about-studies/common.js
--- a/browser/extensions/shield-recipe-client/content/about-studies/common.js
+++ b/toolkit/components/normandy/content/about-studies/common.js
@@ -45,17 +45,17 @@ window.FxButton = class FxButton extends
 window.FxButton.propTypes = {
   children: PropTypes.node,
 };
 
 /**
  * Wrapper class for a value that is provided by the frame script.
  *
  * Emits a "GetRemoteValue:{name}" page event on load to fetch the initial
- * value, and listens for "ReceiveRemoveValue:{name}" page callbacks to receive
+ * value, and listens for "ReceiveRemoteValue:{name}" page callbacks to receive
  * the value when it updates.
  *
  * @example
  * const myRemoteValue = new RemoteValue("MyValue", 5);
  * class MyComponent extends React.Component {
  *   constructor(props) {
  *     super(props);
  *     this.state = {
rename from browser/extensions/shield-recipe-client/content/about-studies/img/shield-logo.png
rename to toolkit/components/normandy/content/about-studies/img/shield-logo.png
rename from browser/extensions/shield-recipe-client/content/about-studies/shield-studies.js
rename to toolkit/components/normandy/content/about-studies/shield-studies.js
rename from browser/extensions/shield-recipe-client/content/shield-content-frame.js
rename to toolkit/components/normandy/content/shield-content-frame.js
--- a/browser/extensions/shield-recipe-client/content/shield-content-frame.js
+++ b/toolkit/components/normandy/content/shield-content-frame.js
@@ -15,17 +15,17 @@
 
 /* global content addMessageListener removeMessageListener sendAsyncMessage */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const frameGlobal = {};
 ChromeUtils.defineModuleGetter(
-  frameGlobal, "AboutPages", "resource://shield-recipe-client-content/AboutPages.jsm",
+  frameGlobal, "AboutPages", "resource://normandy-content/AboutPages.jsm",
 );
 
 /**
  * Handles incoming events from the parent process and about:studies.
  * @implements nsIMessageListener
  * @implements EventListener
  */
 class ShieldFrameListener {
@@ -34,41 +34,39 @@ class ShieldFrameListener {
     if (!this.ensureTrustedOrigin()) {
       return;
     }
 
     // We waited until after we received an event to register message listeners
     // in order to save resources for tabs that don't ever load about:studies.
     addMessageListener("Shield:ShuttingDown", this);
     addMessageListener("Shield:ReceiveStudyList", this);
+    addMessageListener("Shield:ReceiveStudiesEnabled", this);
 
     switch (event.detail.action) {
       // Actions that require the parent process
       case "GetRemoteValue:StudyList":
         sendAsyncMessage("Shield:GetStudyList");
         break;
       case "RemoveStudy":
         sendAsyncMessage("Shield:RemoveStudy", event.detail.data);
         break;
+      case "GetRemoteValue:StudiesEnabled":
+        sendAsyncMessage("Shield:GetStudiesEnabled");
+        break;
+      case "NavigateToDataPreferences":
+        sendAsyncMessage("Shield:OpenDataPreferences");
+        break;
       // Actions that can be performed in the content process
       case "GetRemoteValue:ShieldLearnMoreHref":
         this.triggerPageCallback(
           "ReceiveRemoteValue:ShieldLearnMoreHref",
           frameGlobal.AboutPages.aboutStudies.getShieldLearnMoreHref()
         );
         break;
-      case "GetRemoteValue:StudiesEnabled":
-        this.triggerPageCallback(
-          "ReceiveRemoteValue:StudiesEnabled",
-          frameGlobal.AboutPages.aboutStudies.getStudiesEnabled()
-        );
-        break;
-      case "NavigateToDataPreferences":
-        sendAsyncMessage("Shield:OpenDataPreferences");
-        break;
     }
   }
 
   /**
    * Check that the current webpage's origin is about:studies.
    * @return {Boolean}
    */
   ensureTrustedOrigin() {
@@ -80,16 +78,19 @@ class ShieldFrameListener {
    * @param {Object} message
    *   See the nsIMessageListener docs.
    */
   receiveMessage(message) {
     switch (message.name) {
       case "Shield:ReceiveStudyList":
         this.triggerPageCallback("ReceiveRemoteValue:StudyList", message.data.studies);
         break;
+      case "Shield:ReceiveStudiesEnabled":
+        this.triggerPageCallback("ReceiveRemoteValue:StudiesEnabled", message.data.studiesEnabled);
+        break;
       case "Shield:ShuttingDown":
         this.onShutdown();
         break;
     }
   }
 
   /**
    * Trigger an event to communicate with the unprivileged about: page.
rename from browser/extensions/shield-recipe-client/content/shield-content-process.js
rename to toolkit/components/normandy/content/shield-content-process.js
--- a/browser/extensions/shield-recipe-client/content/shield-content-process.js
+++ b/toolkit/components/normandy/content/shield-content-process.js
@@ -8,31 +8,31 @@
  * from the add-on before un-registering them.
  *
  * This file is loaded as a process script. It is executed once for each
  * process, including the parent one.
  */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://shield-recipe-client-content/AboutPages.jsm");
+ChromeUtils.import("resource://normandy-content/AboutPages.jsm");
 
 class ShieldChildListener {
   onStartup() {
     Services.cpmm.addMessageListener("Shield:ShuttingDown", this, true);
     AboutPages.aboutStudies.register();
   }
 
   onShutdown() {
     AboutPages.aboutStudies.unregister();
     Services.cpmm.removeMessageListener("Shield:ShuttingDown", this);
 
     // Unload AboutPages.jsm in case the add-on is reinstalled and we need to
     // load a new version of it.
-    Cu.unload("resource://shield-recipe-client-content/AboutPages.jsm");
+    Cu.unload("resource://normandy-content/AboutPages.jsm");
   }
 
   receiveMessage(message) {
     switch (message.name) {
       case "Shield:ShuttingDown":
         this.onShutdown();
         break;
     }
rename from browser/extensions/shield-recipe-client/docs/data-collection.rst
rename to toolkit/components/normandy/docs/data-collection.rst
--- a/browser/extensions/shield-recipe-client/docs/data-collection.rst
+++ b/toolkit/components/normandy/docs/data-collection.rst
@@ -1,27 +1,27 @@
 Data Collection
 ===============
-This document describes the types of data that Shield collects.
+This document describes the types of data that Normandy collects.
 
 Uptake
 ------
-Shield monitors the execution of recipes and reports to
+Normandy monitors the execution of recipes and reports to
 :ref:`telemetry` the amount of successful and failed runs. This data
 is reported using :ref:`telemetry/collection/uptake` under the
-``shield-recipe-client`` namespace.
+``normandy`` namespace.
 
 Runner Status
 ^^^^^^^^^^^^^
 Once per-fetch and execution of recipes, one of the following statuses is
-reported under the key ``shield-recipe-client/runner``:
+reported under the key ``normandy/runner``:
 
 .. data:: RUNNER_INVALID_SIGNATURE
 
-   Shield failed to verify the signature of the fetched recipes.
+   Normandy failed to verify the signature of the fetched recipes.
 
 .. data:: RUNNER_NETWORK_ERROR
 
    There was a network-related error while fetching recipes.
 
 .. data:: RUNNER_SERVER_ERROR
 
    The data returned by the server when fetching the recipe is invalid in some
@@ -29,19 +29,19 @@ reported under the key ``shield-recipe-c
 
 .. data:: RUNNER_SUCCESS
 
    The operation completed successfully. Individual failures with actions and
    recipes may have been reported separately.
 
 Action Status
 ^^^^^^^^^^^^^
-For each action available from the Shield service, one of the
+For each action available from the Normandy service, one of the
 following statuses is reported under the key
-``shield-recipe-client/action/<action name>``:
+``normandy/action/<action name>``:
 
 .. data:: ACTION_NETWORK_ERROR
 
    There was a network-related error while fetching actions
 
 .. data:: ACTION_PRE_EXECUTION_ERROR
 
    There was an error while running the pre-execution hook for the action.
@@ -58,17 +58,17 @@ following statuses is reported under the
 .. data:: ACTION_SUCCESS
 
    The operation completed successfully. Individual failures with recipes may
    be reported separately.
 
 Recipe Status
 ^^^^^^^^^^^^^
 For each recipe that is fetched and executed, one of the following statuses is
-reported under the key ``shield-recipe-client/recipe/<recipe id>``:
+reported under the key ``normandy/recipe/<recipe id>``:
 
 .. data:: RECIPE_ACTION_DISABLED
 
    The action for this recipe failed in some way and was disabled, so the recipe
    could not be executed.
 
 .. data:: RECIPE_EXECUTION_ERROR
 
@@ -80,17 +80,17 @@ reported under the key ``shield-recipe-c
 
 .. data:: RECIPE_SUCCESS
 
    The recipe was executed successfully.
 
 
 Enrollment
 -----------
-Shield records enrollment and unenrollment of users into studies, and
+Normandy records enrollment and unenrollment of users into studies, and
 records that data using `Telemetry Events`_. All data is stored in the
 ``normandy`` category.
 
 .. _Telemetry Events: https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/collection/events.html
 
 Preference Studies
 ^^^^^^^^^^^^^^^^^^
 Enrollment
@@ -129,17 +129,17 @@ Unenrollment
            applicable to this client This can be because the recipe
            was disabled, or the user no longer matches the recipe's
            filter.
          * ``"user-preference-changed"``: The study preference was
            changed on the user branch. This could mean the user
            changed the preference, or that some other mechanism set a
            non-default value for the preference.
          * ``"user-preference-changed-sideload"``: The study
-           preference was changed on the user branch while Shield was
+           preference was changed on the user branch while Normandy was
            inactive. This could mean that the value was manually
            changed in a profile while Firefox was not running.
          * ``"unknown"``: A reason was not specificied. This should be
            considered a bug.
 
 Add-on Studies
 ^^^^^^^^^^^^^^
 Enrollment
@@ -191,13 +191,13 @@ Unenrollment
          * ``"recipe-not-seen"``: The recipe was no longer applicable
            to this client. This can be because the recipe was
            disabled, or the user no longer matches the recipe's
            filter.
          * ``"uninstalled"``: The study's add-on as uninstalled by some
            mechanism. For example, this could be a user action or the
            add-on self-uninstalling.
          * ``"uninstalled-sideload"``: The study's add-on was
-           uninstalled while Shield was inactive. This could be that
+           uninstalled while Normandy was inactive. This could be that
            the add-on is no longer compatible, or was manually removed
            from a profile.
          * ``"unknown"``: A reason was not specified. This should be
            considered a bug.
rename from browser/extensions/shield-recipe-client/docs/index.rst
rename to toolkit/components/normandy/docs/index.rst
rename from browser/extensions/shield-recipe-client/jar.mn
rename to toolkit/components/normandy/jar.mn
--- a/browser/extensions/shield-recipe-client/jar.mn
+++ b/toolkit/components/normandy/jar.mn
@@ -1,13 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-[features/shield-recipe-client@mozilla.org] chrome.jar:
-% resource shield-recipe-client %
-  lib/ (./lib/*)
-  data/ (./data/*)
-  skin/  (skin/*)
-% resource shield-recipe-client-content %content/ contentaccessible=yes
-  content/ (./content/*)
-% resource shield-recipe-client-vendor %vendor/ contentaccessible=yes
-  vendor/ (./vendor/*)
+toolkit.jar:
+% resource normandy %res/normandy/
+  res/normandy/Normandy.jsm (./Normandy.jsm)
+  res/normandy/lib/ (./lib/*)
+  res/normandy/skin/  (./skin/*)
+
+% resource normandy-content %res/normandy/content/ contentaccessible=yes
+  res/normandy/content/ (./content/*)
+
+% resource normandy-vendor %res/normandy/vendor/ contentaccessible=yes
+  res/normandy/vendor/ (./vendor/*)
rename from browser/extensions/shield-recipe-client/lib/ActionSandboxManager.jsm
rename to toolkit/components/normandy/lib/ActionSandboxManager.jsm
--- a/browser/extensions/shield-recipe-client/lib/ActionSandboxManager.jsm
+++ b/toolkit/components/normandy/lib/ActionSandboxManager.jsm
@@ -1,17 +1,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/. */
 
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyDriver.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/SandboxManager.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/NormandyDriver.jsm");
+ChromeUtils.import("resource://normandy/lib/SandboxManager.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
 
 var EXPORTED_SYMBOLS = ["ActionSandboxManager"];
 
 const log = LogManager.getLogger("recipe-sandbox-manager");
 
 /**
  * An extension to SandboxManager that prepares a sandbox for executing
  * Normandy actions.
rename from browser/extensions/shield-recipe-client/lib/AddonStudies.jsm
rename to toolkit/components/normandy/lib/AddonStudies.jsm
--- a/browser/extensions/shield-recipe-client/lib/AddonStudies.jsm
+++ b/toolkit/components/normandy/lib/AddonStudies.jsm
@@ -28,22 +28,22 @@
 
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "IndexedDB", "resource://gre/modules/IndexedDB.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "Addons", "resource://shield-recipe-client/lib/Addons.jsm");
+ChromeUtils.defineModuleGetter(this, "Addons", "resource://normandy/lib/Addons.jsm");
 ChromeUtils.defineModuleGetter(
-  this, "CleanupManager", "resource://shield-recipe-client/lib/CleanupManager.jsm"
+  this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm"
 );
-ChromeUtils.defineModuleGetter(this, "LogManager", "resource://shield-recipe-client/lib/LogManager.jsm");
-ChromeUtils.defineModuleGetter(this, "TelemetryEvents", "resource://shield-recipe-client/lib/TelemetryEvents.jsm");
+ChromeUtils.defineModuleGetter(this, "LogManager", "resource://normandy/lib/LogManager.jsm");
+ChromeUtils.defineModuleGetter(this, "TelemetryEvents", "resource://normandy/lib/TelemetryEvents.jsm");
 
 Cu.importGlobalProperties(["fetch"]); /* globals fetch */
 
 var EXPORTED_SYMBOLS = ["AddonStudies"];
 
 const DB_NAME = "shield";
 const STORE_NAME = "addon-studies";
 const DB_OPTIONS = {
@@ -337,16 +337,18 @@ var AddonStudies = {
    */
   async stop(recipeId, reason = "unknown") {
     const db = await getDatabase();
     const study = await getStore(db).get(recipeId);
     if (!study) {
       throw new Error(`No study found for recipe ${recipeId}.`);
     }
     if (!study.active) {
+      dump(`@@@ Cannot stop study for recipe ${recipeId}; it is already inactive.\n`);
+      dump(`@@@\n${new Error().stack}\n@@@\n`);
       throw new Error(`Cannot stop study for recipe ${recipeId}; it is already inactive.`);
     }
 
     await markAsEnded(db, study, reason);
 
     try {
       await Addons.uninstall(study.addonId);
     } catch (err) {
rename from browser/extensions/shield-recipe-client/lib/Addons.jsm
rename to toolkit/components/normandy/lib/Addons.jsm
--- a/browser/extensions/shield-recipe-client/lib/Addons.jsm
+++ b/toolkit/components/normandy/lib/Addons.jsm
@@ -4,17 +4,17 @@
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "Extension", "resource://gre/modules/Extension.jsm");
 ChromeUtils.defineModuleGetter(
-  this, "CleanupManager", "resource://shield-recipe-client/lib/CleanupManager.jsm"
+  this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm"
 );
 
 var EXPORTED_SYMBOLS = ["Addons"];
 
 /**
  * SafeAddons store info about an add-on. They are single-depth
  * objects to simplify cloning, and have no methods so they are safe
  * to pass to sandboxes and filter expressions.
rename from browser/extensions/shield-recipe-client/lib/CleanupManager.jsm
rename to toolkit/components/normandy/lib/CleanupManager.jsm
rename from browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
rename to toolkit/components/normandy/lib/ClientEnvironment.jsm
--- a/browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
+++ b/toolkit/components/normandy/lib/ClientEnvironment.jsm
@@ -7,24 +7,24 @@
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "ShellService", "resource:///modules/ShellService.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryArchive", "resource://gre/modules/TelemetryArchive.jsm");
 ChromeUtils.defineModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "NormandyApi", "resource://shield-recipe-client/lib/NormandyApi.jsm");
+ChromeUtils.defineModuleGetter(this, "NormandyApi", "resource://normandy/lib/NormandyApi.jsm");
 ChromeUtils.defineModuleGetter(
     this,
     "PreferenceExperiments",
-    "resource://shield-recipe-client/lib/PreferenceExperiments.jsm"
+    "resource://normandy/lib/PreferenceExperiments.jsm"
 );
-ChromeUtils.defineModuleGetter(this, "Utils", "resource://shield-recipe-client/lib/Utils.jsm");
-ChromeUtils.defineModuleGetter(this, "Addons", "resource://shield-recipe-client/lib/Addons.jsm");
+ChromeUtils.defineModuleGetter(this, "Utils", "resource://normandy/lib/Utils.jsm");
+ChromeUtils.defineModuleGetter(this, "Addons", "resource://normandy/lib/Addons.jsm");
 
 const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 
 var EXPORTED_SYMBOLS = ["ClientEnvironment"];
 
 // Cached API request for client attributes that are determined by the Normandy
 // service.
 let _classifyRequest = null;
@@ -72,21 +72,21 @@ var ClientEnvironment = {
    * Also note that, because filter expressions implicitly resolve promises, you
    * can add getter functions that return promises for async data.
    * @return {Object}
    */
   getEnvironment() {
     const environment = {};
 
     XPCOMUtils.defineLazyGetter(environment, "userId", () => {
-      let id = Preferences.get("extensions.shield-recipe-client.user_id", "");
+      let id = Preferences.get("app.normandy.user_id", "");
       if (!id) {
         // generateUUID adds leading and trailing "{" and "}". strip them off.
         id = generateUUID().toString().slice(1, -1);
-        Preferences.set("extensions.shield-recipe-client.user_id", id);
+        Preferences.set("app.normandy.user_id", id);
       }
       return id;
     });
 
     XPCOMUtils.defineLazyGetter(environment, "country", () => {
       return ClientEnvironment.getClientClassification()
         .then(classification => classification.country);
     });
@@ -199,14 +199,14 @@ var ClientEnvironment = {
     });
 
     XPCOMUtils.defineLazyGetter(environment, "addons", async () => {
       const addons = await Addons.getAll();
       return Utils.keyBy(addons, "id");
     });
 
     XPCOMUtils.defineLazyGetter(environment, "isFirstRun", () => {
-      return Preferences.get("extensions.shield-recipe-client.first_run");
+      return Preferences.get("app.normandy.first_run");
     });
 
     return environment;
   },
 };
rename from browser/extensions/shield-recipe-client/lib/EventEmitter.jsm
rename to toolkit/components/normandy/lib/EventEmitter.jsm
--- a/browser/extensions/shield-recipe-client/lib/EventEmitter.jsm
+++ b/toolkit/components/normandy/lib/EventEmitter.jsm
@@ -1,14 +1,14 @@
 /* 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/. */
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
 
 var EXPORTED_SYMBOLS = ["EventEmitter"];
 
 const log = LogManager.getLogger("event-emitter");
 
 var EventEmitter = function(sandboxManager) {
   const listeners = {};
 
rename from browser/extensions/shield-recipe-client/lib/FilterExpressions.jsm
rename to toolkit/components/normandy/lib/FilterExpressions.jsm
--- a/browser/extensions/shield-recipe-client/lib/FilterExpressions.jsm
+++ b/toolkit/components/normandy/lib/FilterExpressions.jsm
@@ -1,19 +1,19 @@
 /* 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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/Sampling.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceFilters.jsm");
+ChromeUtils.import("resource://normandy/lib/Sampling.jsm");
+ChromeUtils.import("resource://normandy/lib/PreferenceFilters.jsm");
 
-ChromeUtils.defineModuleGetter(this, "mozjexl", "resource://shield-recipe-client-vendor/mozjexl.js");
+ChromeUtils.defineModuleGetter(this, "mozjexl", "resource://normandy-vendor/mozjexl.js");
 
 var EXPORTED_SYMBOLS = ["FilterExpressions"];
 
 XPCOMUtils.defineLazyGetter(this, "jexl", () => {
   const jexl = new mozjexl.Jexl();
   jexl.addTransforms({
     date: dateString => new Date(dateString),
     stableSample: Sampling.stableSample,
rename from browser/extensions/shield-recipe-client/lib/Heartbeat.jsm
rename to toolkit/components/normandy/lib/Heartbeat.jsm
--- a/browser/extensions/shield-recipe-client/lib/Heartbeat.jsm
+++ b/toolkit/components/normandy/lib/Heartbeat.jsm
@@ -4,28 +4,28 @@
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/TelemetryController.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/CleanupManager.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/EventEmitter.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/CleanupManager.jsm");
+ChromeUtils.import("resource://normandy/lib/EventEmitter.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
 
 Cu.importGlobalProperties(["URL"]); /* globals URL */
 
 var EXPORTED_SYMBOLS = ["Heartbeat"];
 
 const PREF_SURVEY_DURATION = "browser.uitour.surveyDuration";
 const NOTIFICATION_TIME = 3000;
-const HEARTBEAT_CSS_URI = Services.io.newURI("resource://shield-recipe-client/skin/shared/Heartbeat.css");
-const HEARTBEAT_CSS_URI_OSX = Services.io.newURI("resource://shield-recipe-client/skin/osx/Heartbeat.css");
+const HEARTBEAT_CSS_URI = Services.io.newURI("resource://normandy/skin/shared/Heartbeat.css");
+const HEARTBEAT_CSS_URI_OSX = Services.io.newURI("resource://normandy/skin/osx/Heartbeat.css");
 
 const log = LogManager.getLogger("heartbeat");
 const windowsWithInjectedCss = new WeakSet();
 let anyWindowsWithInjectedCss = false;
 
 // Add cleanup handler for CSS injected into windows by Heartbeat
 CleanupManager.addCleanupHandler(() => {
   if (anyWindowsWithInjectedCss) {
@@ -152,17 +152,17 @@ var Heartbeat = class {
         },
       }];
     }
 
     this.notificationBox = this.chromeWindow.document.querySelector("#high-priority-global-notificationbox");
     this.notice = this.notificationBox.appendNotification(
       this.options.message,
       "heartbeat-" + this.options.flowId,
-      "resource://shield-recipe-client/skin/shared/heartbeat-icon.svg",
+      "resource://normandy/skin/shared/heartbeat-icon.svg",
       this.notificationBox.PRIORITY_INFO_HIGH,
       this.buttons,
       eventType => {
         if (eventType !== "removed") {
           return;
         }
         this.maybeNotifyHeartbeat("NotificationClosed");
       }
rename from browser/extensions/shield-recipe-client/lib/LogManager.jsm
rename to toolkit/components/normandy/lib/LogManager.jsm
--- a/browser/extensions/shield-recipe-client/lib/LogManager.jsm
+++ b/toolkit/components/normandy/lib/LogManager.jsm
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 
 var EXPORTED_SYMBOLS = ["LogManager"];
 
-const ROOT_LOGGER_NAME = "extensions.shield-recipe-client";
+const ROOT_LOGGER_NAME = "app.normandy";
 let rootLogger = null;
 
 var LogManager = {
   /**
    * Configure the root logger for the Recipe Client. Must be called at
    * least once before using any loggers created via getLogger.
    * @param {Number} loggingLevel
    *        Logging level to use as defined in Log.jsm
rename from browser/extensions/shield-recipe-client/lib/NormandyApi.jsm
rename to toolkit/components/normandy/lib/NormandyApi.jsm
--- a/browser/extensions/shield-recipe-client/lib/NormandyApi.jsm
+++ b/toolkit/components/normandy/lib/NormandyApi.jsm
@@ -1,27 +1,27 @@
 /* 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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
 
 ChromeUtils.defineModuleGetter(
   this, "CanonicalJSON", "resource://gre/modules/CanonicalJSON.jsm");
 
 Cu.importGlobalProperties(["fetch", "URL"]); /* globals fetch, URL */
 
 var EXPORTED_SYMBOLS = ["NormandyApi"];
 
 const log = LogManager.getLogger("normandy-api");
-const prefs = Services.prefs.getBranch("extensions.shield-recipe-client.");
+const prefs = Services.prefs.getBranch("app.normandy.");
 
 let indexPromise = null;
 
 var NormandyApi = {
   InvalidSignatureError: class InvalidSignatureError extends Error {},
 
   clearIndexCache() {
     indexPromise = null;
rename from browser/extensions/shield-recipe-client/lib/NormandyDriver.jsm
rename to toolkit/components/normandy/lib/NormandyDriver.jsm
--- a/browser/extensions/shield-recipe-client/lib/NormandyDriver.jsm
+++ b/toolkit/components/normandy/lib/NormandyDriver.jsm
@@ -5,28 +5,28 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource:///modules/ShellService.jsm");
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/Addons.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/Storage.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/Heartbeat.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/FilterExpressions.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/Sampling.jsm");
+ChromeUtils.import("resource://normandy/lib/Addons.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/Storage.jsm");
+ChromeUtils.import("resource://normandy/lib/Heartbeat.jsm");
+ChromeUtils.import("resource://normandy/lib/FilterExpressions.jsm");
+ChromeUtils.import("resource://normandy/lib/ClientEnvironment.jsm");
+ChromeUtils.import("resource://normandy/lib/PreferenceExperiments.jsm");
+ChromeUtils.import("resource://normandy/lib/Sampling.jsm");
 
 ChromeUtils.defineModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
 ChromeUtils.defineModuleGetter(
-  this, "AddonStudies", "resource://shield-recipe-client/lib/AddonStudies.jsm");
+  this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm");
 
 const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 
 var EXPORTED_SYMBOLS = ["NormandyDriver"];
 
 const log = LogManager.getLogger("normandy-driver");
 const actionLog = LogManager.getLogger("normandy-driver.actions");
 
rename from browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
rename to toolkit/components/normandy/lib/PreferenceExperiments.jsm
--- a/browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
+++ b/toolkit/components/normandy/lib/PreferenceExperiments.jsm
@@ -50,27 +50,27 @@
  *   The type to report to Telemetry's experiment marker API.
  */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "CleanupManager", "resource://shield-recipe-client/lib/CleanupManager.jsm");
+ChromeUtils.defineModuleGetter(this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm");
 ChromeUtils.defineModuleGetter(this, "JSONFile", "resource://gre/modules/JSONFile.jsm");
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
-ChromeUtils.defineModuleGetter(this, "LogManager", "resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.defineModuleGetter(this, "LogManager", "resource://normandy/lib/LogManager.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryEnvironment", "resource://gre/modules/TelemetryEnvironment.jsm");
-ChromeUtils.defineModuleGetter(this, "TelemetryEvents", "resource://shield-recipe-client/lib/TelemetryEvents.jsm");
+ChromeUtils.defineModuleGetter(this, "TelemetryEvents", "resource://normandy/lib/TelemetryEvents.jsm");
 
 var EXPORTED_SYMBOLS = ["PreferenceExperiments"];
 
 const EXPERIMENT_FILE = "shield-preference-experiments.json";
-const STARTUP_EXPERIMENT_PREFS_BRANCH = "extensions.shield-recipe-client.startupExperimentPrefs.";
+const STARTUP_EXPERIMENT_PREFS_BRANCH = "app.normandy.startupExperimentPrefs.";
 
 const MAX_EXPERIMENT_TYPE_LENGTH = 20; // enforced by TelemetryEnvironment
 const EXPERIMENT_TYPE_PREFIX = "normandy-";
 const MAX_EXPERIMENT_SUBTYPE_LENGTH = MAX_EXPERIMENT_TYPE_LENGTH - EXPERIMENT_TYPE_PREFIX.length;
 
 const PREFERENCE_TYPE_MAP = {
   boolean: Services.prefs.PREF_BOOL,
   string: Services.prefs.PREF_STRING,
rename from browser/extensions/shield-recipe-client/lib/PreferenceFilters.jsm
rename to toolkit/components/normandy/lib/PreferenceFilters.jsm
rename from browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
rename to toolkit/components/normandy/lib/RecipeRunner.jsm
--- a/browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
+++ b/toolkit/components/normandy/lib/RecipeRunner.jsm
@@ -1,77 +1,77 @@
 /* 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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "timerManager",
                                    "@mozilla.org/updates/timer-manager;1",
                                    "nsIUpdateTimerManager");
 ChromeUtils.defineModuleGetter(this, "Preferences", "resource://gre/modules/Preferences.jsm");
 ChromeUtils.defineModuleGetter(this, "Storage",
-                               "resource://shield-recipe-client/lib/Storage.jsm");
+                               "resource://normandy/lib/Storage.jsm");
 ChromeUtils.defineModuleGetter(this, "NormandyDriver",
-                               "resource://shield-recipe-client/lib/NormandyDriver.jsm");
+                               "resource://normandy/lib/NormandyDriver.jsm");
 ChromeUtils.defineModuleGetter(this, "FilterExpressions",
-                               "resource://shield-recipe-client/lib/FilterExpressions.jsm");
+                               "resource://normandy/lib/FilterExpressions.jsm");
 ChromeUtils.defineModuleGetter(this, "NormandyApi",
-                               "resource://shield-recipe-client/lib/NormandyApi.jsm");
+                               "resource://normandy/lib/NormandyApi.jsm");
 ChromeUtils.defineModuleGetter(this, "SandboxManager",
-                               "resource://shield-recipe-client/lib/SandboxManager.jsm");
+                               "resource://normandy/lib/SandboxManager.jsm");
 ChromeUtils.defineModuleGetter(this, "ClientEnvironment",
-                               "resource://shield-recipe-client/lib/ClientEnvironment.jsm");
+                               "resource://normandy/lib/ClientEnvironment.jsm");
 ChromeUtils.defineModuleGetter(this, "CleanupManager",
-                               "resource://shield-recipe-client/lib/CleanupManager.jsm");
+                               "resource://normandy/lib/CleanupManager.jsm");
 ChromeUtils.defineModuleGetter(this, "ActionSandboxManager",
-                               "resource://shield-recipe-client/lib/ActionSandboxManager.jsm");
+                               "resource://normandy/lib/ActionSandboxManager.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonStudies",
-                               "resource://shield-recipe-client/lib/AddonStudies.jsm");
+                               "resource://normandy/lib/AddonStudies.jsm");
 ChromeUtils.defineModuleGetter(this, "Uptake",
-                               "resource://shield-recipe-client/lib/Uptake.jsm");
+                               "resource://normandy/lib/Uptake.jsm");
 
 Cu.importGlobalProperties(["fetch"]);
 
 var EXPORTED_SYMBOLS = ["RecipeRunner"];
 
 const log = LogManager.getLogger("recipe-runner");
 const TIMER_NAME = "recipe-client-addon-run";
 const PREF_CHANGED_TOPIC = "nsPref:changed";
 
 const TELEMETRY_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
 
-const SHIELD_PREF_PREFIX = "extensions.shield-recipe-client";
-const RUN_INTERVAL_PREF = `${SHIELD_PREF_PREFIX}.run_interval_seconds`;
-const FIRST_RUN_PREF = `${SHIELD_PREF_PREFIX}.first_run`;
-const SHIELD_ENABLED_PREF = `${SHIELD_PREF_PREFIX}.enabled`;
-const DEV_MODE_PREF = `${SHIELD_PREF_PREFIX}.dev_mode`;
-const API_URL_PREF = `${SHIELD_PREF_PREFIX}.api_url`;
-const LAZY_CLASSIFY_PREF = `${SHIELD_PREF_PREFIX}.experiments.lazy_classify`;
+const PREF_PREFIX = "app.normandy";
+const RUN_INTERVAL_PREF = `${PREF_PREFIX}.run_interval_seconds`;
+const FIRST_RUN_PREF = `${PREF_PREFIX}.first_run`;
+const SHIELD_ENABLED_PREF = `${PREF_PREFIX}.enabled`;
+const DEV_MODE_PREF = `${PREF_PREFIX}.dev_mode`;
+const API_URL_PREF = `${PREF_PREFIX}.api_url`;
+const LAZY_CLASSIFY_PREF = `${PREF_PREFIX}.experiments.lazy_classify`;
 
 const PREFS_TO_WATCH = [
   RUN_INTERVAL_PREF,
   TELEMETRY_ENABLED_PREF,
   SHIELD_ENABLED_PREF,
   API_URL_PREF,
 ];
 
 var RecipeRunner = {
   async init() {
     this.enabled = null;
     this.checkPrefs(); // sets this.enabled
     this.watchPrefs();
 
     // Run if enabled immediately on first run, or if dev mode is enabled.
-    const firstRun = Services.prefs.getBoolPref(FIRST_RUN_PREF);
-    const devMode = Services.prefs.getBoolPref(DEV_MODE_PREF);
+    const firstRun = Services.prefs.getBoolPref(FIRST_RUN_PREF, true);
+    const devMode = Services.prefs.getBoolPref(DEV_MODE_PREF, false);
 
     if (this.enabled && (devMode || firstRun)) {
       await this.run();
     }
     if (firstRun) {
       Services.prefs.setBoolPref(FIRST_RUN_PREF, false);
     }
   },
rename from browser/extensions/shield-recipe-client/lib/Sampling.jsm
rename to toolkit/components/normandy/lib/Sampling.jsm
rename from browser/extensions/shield-recipe-client/lib/SandboxManager.jsm
rename to toolkit/components/normandy/lib/SandboxManager.jsm
rename from browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
rename to toolkit/components/normandy/lib/ShieldPreferences.jsm
--- a/browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
+++ b/toolkit/components/normandy/lib/ShieldPreferences.jsm
@@ -5,20 +5,20 @@
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(
   this, "AppConstants", "resource://gre/modules/AppConstants.jsm"
 );
 ChromeUtils.defineModuleGetter(
-  this, "AddonStudies", "resource://shield-recipe-client/lib/AddonStudies.jsm"
+  this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm"
 );
 ChromeUtils.defineModuleGetter(
-  this, "CleanupManager", "resource://shield-recipe-client/lib/CleanupManager.jsm"
+  this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm"
 );
 
 var EXPORTED_SYMBOLS = ["ShieldPreferences"];
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; // from modules/libpref/nsIPrefBranch.idl
 const FHR_UPLOAD_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
 const OPT_OUT_STUDIES_ENABLED_PREF = "app.shield.optoutstudies.enabled";
rename from browser/extensions/shield-recipe-client/lib/Storage.jsm
rename to toolkit/components/normandy/lib/Storage.jsm
rename from browser/extensions/shield-recipe-client/lib/TelemetryEvents.jsm
rename to toolkit/components/normandy/lib/TelemetryEvents.jsm
rename from browser/extensions/shield-recipe-client/lib/Uptake.jsm
rename to toolkit/components/normandy/lib/Uptake.jsm
--- a/browser/extensions/shield-recipe-client/lib/Uptake.jsm
+++ b/toolkit/components/normandy/lib/Uptake.jsm
@@ -6,17 +6,17 @@
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(
   this, "UptakeTelemetry", "resource://services-common/uptake-telemetry.js");
 
 var EXPORTED_SYMBOLS = ["Uptake"];
 
-const SOURCE_PREFIX = "shield-recipe-client";
+const SOURCE_PREFIX = "normandy";
 
 var Uptake = {
   // Action uptake
   ACTION_NETWORK_ERROR: UptakeTelemetry.STATUS.NETWORK_ERROR,
   ACTION_PRE_EXECUTION_ERROR: UptakeTelemetry.STATUS.CUSTOM_1_ERROR,
   ACTION_POST_EXECUTION_ERROR: UptakeTelemetry.STATUS.CUSTOM_2_ERROR,
   ACTION_SERVER_ERROR: UptakeTelemetry.STATUS.SERVER_ERROR,
   ACTION_SUCCESS: UptakeTelemetry.STATUS.SUCCESS,
rename from browser/extensions/shield-recipe-client/lib/Utils.jsm
rename to toolkit/components/normandy/lib/Utils.jsm
--- a/browser/extensions/shield-recipe-client/lib/Utils.jsm
+++ b/toolkit/components/normandy/lib/Utils.jsm
@@ -1,15 +1,15 @@
 /* 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/. */
 
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm");
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm");
 
 var EXPORTED_SYMBOLS = ["Utils"];
 
 const log = LogManager.getLogger("utils");
 
 var Utils = {
   /**
    * Convert an array of objects to an object. Each item is keyed by the value
rename from browser/extensions/shield-recipe-client/moz.build
rename to toolkit/components/normandy/moz.build
--- a/browser/extensions/shield-recipe-client/moz.build
+++ b/toolkit/components/normandy/moz.build
@@ -1,24 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
-DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
-
-FINAL_TARGET_FILES.features['shield-recipe-client@mozilla.org'] += [
-  'bootstrap.js',
-]
-
-FINAL_TARGET_PP_FILES.features['shield-recipe-client@mozilla.org'] += [
-  'install.rdf.in'
-]
-
-BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
-
-XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
+with Files('**'):
+    BUG_COMPONENT = ('Firefox', 'Normandy Client')
 
 JAR_MANIFESTS += ['jar.mn']
 
-SPHINX_TREES['shield-recipe-client'] = 'docs'
+SPHINX_TREES['normandy'] = 'docs'
+
+BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
+XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
rename from browser/extensions/shield-recipe-client/skin/osx/Heartbeat.css
rename to toolkit/components/normandy/skin/osx/Heartbeat.css
rename from browser/extensions/shield-recipe-client/skin/shared/Heartbeat.css
rename to toolkit/components/normandy/skin/shared/Heartbeat.css
--- a/browser/extensions/shield-recipe-client/skin/shared/Heartbeat.css
+++ b/toolkit/components/normandy/skin/shared/Heartbeat.css
@@ -109,19 +109,19 @@ notification.heartbeat {
   -moz-box-ordinal-group: 3;
 }
 
 .heartbeat > #star-rating-container > #star2 {
   -moz-box-ordinal-group: 2;
 }
 
 .heartbeat > #star-rating-container > .star-x {
-  background: url("resource://shield-recipe-client/skin/shared/heartbeat-star-off.svg");
+  background: url("resource://normandy/skin/shared/heartbeat-star-off.svg");
   cursor: pointer;
   height: 16px;
   margin-inline-end: 4px !important; /* Overrides the margin-inline-end for all platforms defined in the .plain class */
   width: 16px;
 }
 
 .heartbeat > #star-rating-container > .star-x:hover,
 .heartbeat > #star-rating-container > .star-x:hover ~ .star-x {
-  background: url("resource://shield-recipe-client/skin/shared/heartbeat-star-lit.svg");
+  background: url("resource://normandy/skin/shared/heartbeat-star-lit.svg");
 }
rename from browser/extensions/shield-recipe-client/skin/shared/heartbeat-icon.svg
rename to toolkit/components/normandy/skin/shared/heartbeat-icon.svg
rename from browser/extensions/shield-recipe-client/skin/shared/heartbeat-star-lit.svg
rename to toolkit/components/normandy/skin/shared/heartbeat-star-lit.svg
rename from browser/extensions/shield-recipe-client/skin/shared/heartbeat-star-off.svg
rename to toolkit/components/normandy/skin/shared/heartbeat-star-off.svg
rename from browser/extensions/shield-recipe-client/test/.eslintrc.js
rename to toolkit/components/normandy/test/.eslintrc.js
rename from browser/extensions/shield-recipe-client/test/browser/.eslintrc.js
rename to toolkit/components/normandy/test/browser/.eslintrc.js
rename from browser/extensions/shield-recipe-client/test/browser/action_server.sjs
rename to toolkit/components/normandy/test/browser/action_server.sjs
rename from browser/extensions/shield-recipe-client/test/browser/browser.ini
rename to toolkit/components/normandy/test/browser/browser.ini
--- a/browser/extensions/shield-recipe-client/test/browser/browser.ini
+++ b/toolkit/components/normandy/test/browser/browser.ini
@@ -1,25 +1,25 @@
 [DEFAULT]
 support-files =
   action_server.sjs
   fixtures/normandy.xpi
 head = head.js
+[browser_about_preferences.js]
+# Skip this test when FHR/Telemetry aren't available.
+skip-if = !healthreport || !telemetry
+[browser_about_studies.js]
+skip-if = true # bug 1442712
 [browser_ActionSandboxManager.js]
 [browser_Addons.js]
 [browser_AddonStudies.js]
-[browser_bootstrap.js]
 [browser_CleanupManager.js]
-[browser_NormandyDriver.js]
+[browser_ClientEnvironment.js]
+[browser_EventEmitter.js]
 [browser_FilterExpressions.js]
-[browser_EventEmitter.js]
-[browser_Storage.js]
 [browser_Heartbeat.js]
-[browser_RecipeRunner.js]
 [browser_LogManager.js]
-[browser_ClientEnvironment.js]
-[browser_ShieldRecipeClient.js]
-[browser_ShieldPreferences.js]
+[browser_Normandy.js]
+[browser_NormandyDriver.js]
 [browser_PreferenceExperiments.js]
-[browser_about_studies.js]
-[browser_about_preferences.js]
-# Skip this test when FHR/Telemetry aren't available.
-skip-if = !healthreport || !telemetry
+[browser_RecipeRunner.js]
+[browser_ShieldPreferences.js]
+[browser_Storage.js]
rename from browser/extensions/shield-recipe-client/test/browser/browser_ActionSandboxManager.js
rename to toolkit/components/normandy/test/browser/browser_ActionSandboxManager.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ActionSandboxManager.js
+++ b/toolkit/components/normandy/test/browser/browser_ActionSandboxManager.js
@@ -1,12 +1,12 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/ActionSandboxManager.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this);
+ChromeUtils.import("resource://normandy/lib/ActionSandboxManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/NormandyDriver.jsm", this);
 
 async function withManager(script, testFunction) {
   const manager = new ActionSandboxManager(script);
   manager.addHold("testing");
   await testFunction(manager);
   manager.removeHold("testing");
 }
 
rename from browser/extensions/shield-recipe-client/test/browser/browser_AddonStudies.js
rename to toolkit/components/normandy/test/browser/browser_AddonStudies.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_AddonStudies.js
+++ b/toolkit/components/normandy/test/browser/browser_AddonStudies.js
@@ -1,16 +1,16 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/IndexedDB.jsm", this);
 ChromeUtils.import("resource://testing-common/TestUtils.jsm", this);
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/Addons.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/TelemetryEvents.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Addons.jsm", this);
+ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
+ChromeUtils.import("resource://normandy/lib/TelemetryEvents.jsm", this);
 
 // Initialize test utils
 AddonTestUtils.initMochitest(this);
 
 let _startArgsFactoryId = 1;
 function startArgsFactory(args) {
   return Object.assign({
     recipeId: _startArgsFactoryId++,
@@ -342,17 +342,17 @@ decorate_task(
     const newActiveInstalledStudy = await AddonStudies.get(activeInstalledStudy.recipeId);
     Assert.deepEqual(
       activeInstalledStudy,
       newActiveInstalledStudy,
       "init does not modify studies whose add-on is still installed."
     );
 
     // Only activeUninstalledStudy should have generated any events
-    ok(sendEventStub.calledOnce);
+    ok(sendEventStub.calledOnce, "no extra events should be generated");
   }
 );
 
 decorate_task(
   AddonStudies.withStudies([
     studyFactory({active: true, addonId: "installed@example.com", studyEndDate: null}),
   ]),
   withInstalledWebExtension({id: "installed@example.com"}),
rename from browser/extensions/shield-recipe-client/test/browser/browser_Addons.js
rename to toolkit/components/normandy/test/browser/browser_Addons.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_Addons.js
+++ b/toolkit/components/normandy/test/browser/browser_Addons.js
@@ -1,13 +1,13 @@
 "use strict";
 
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/Addons.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Addons.jsm", this);
 
 // Initialize test utils
 AddonTestUtils.initMochitest(this);
 
 const testInstallId = "testInstallUpdate@example.com";
 decorate_task(
   withInstalledWebExtension({version: "1.0", id: testInstallId}),
   withWebExtension({version: "2.0", id: testInstallId}),
rename from browser/extensions/shield-recipe-client/test/browser/browser_CleanupManager.js
rename to toolkit/components/normandy/test/browser/browser_CleanupManager.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_CleanupManager.js
+++ b/toolkit/components/normandy/test/browser/browser_CleanupManager.js
@@ -1,11 +1,11 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/CleanupManager.jsm", this); /* global CleanupManagerClass */
+ChromeUtils.import("resource://normandy/lib/CleanupManager.jsm", this); /* global CleanupManagerClass */
 
 add_task(async function testCleanupManager() {
   const spy1 = sinon.spy();
   const spy2 = sinon.spy();
   const spy3 = sinon.spy();
 
   const manager = new CleanupManager.constructor();
   manager.addCleanupHandler(spy1);
rename from browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
rename to toolkit/components/normandy/test/browser/browser_ClientEnvironment.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
+++ b/toolkit/components/normandy/test/browser/browser_ClientEnvironment.js
@@ -1,16 +1,16 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", this);
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm", this);
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
+ChromeUtils.import("resource://normandy/lib/ClientEnvironment.jsm", this);
+ChromeUtils.import("resource://normandy/lib/PreferenceExperiments.jsm", this);
 
 
 add_task(async function testTelemetry() {
   // setup
   await SpecialPowers.pushPrefEnv({set: [["privacy.reduceTimerPrecision", true]]});
 
   await TelemetryController.submitExternalPing("testfoo", {foo: 1});
   await TelemetryController.submitExternalPing("testbar", {bar: 2});
@@ -28,17 +28,17 @@ add_task(async function testTelemetry() 
 
 add_task(async function testUserId() {
   let environment = ClientEnvironment.getEnvironment();
 
   // Test that userId is available
   ok(UUID_REGEX.test(environment.userId), "userId available");
 
   // test that it pulls from the right preference
-  await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.user_id", "fake id"]]});
+  await SpecialPowers.pushPrefEnv({set: [["app.normandy.user_id", "fake id"]]});
   environment = ClientEnvironment.getEnvironment();
   is(environment.userId, "fake id", "userId is pulled from preferences");
 });
 
 add_task(async function testDistribution() {
   let environment = ClientEnvironment.getEnvironment();
 
   // distribution id defaults to "default"
@@ -133,12 +133,12 @@ add_task(withDriver(Assert, async functi
     isActive: true,
     type: "extension",
   }, "addons should be available in context");
 
   await driver.addons.uninstall(addonId);
 }));
 
 add_task(async function isFirstRun() {
-  await SpecialPowers.pushPrefEnv({set: [["extensions.shield-recipe-client.first_run", true]]});
+  await SpecialPowers.pushPrefEnv({set: [["app.normandy.first_run", true]]});
   const environment = ClientEnvironment.getEnvironment();
   ok(environment.isFirstRun, "isFirstRun is read from preferences");
 });
rename from browser/extensions/shield-recipe-client/test/browser/browser_EventEmitter.js
rename to toolkit/components/normandy/test/browser/browser_EventEmitter.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_EventEmitter.js
+++ b/toolkit/components/normandy/test/browser/browser_EventEmitter.js
@@ -1,12 +1,12 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/EventEmitter.jsm", this);
+ChromeUtils.import("resource://normandy/lib/EventEmitter.jsm", this);
 
 const evidence = {
   a: 0,
   b: 0,
   c: 0,
   log: "",
 };
 
rename from browser/extensions/shield-recipe-client/test/browser/browser_FilterExpressions.js
rename to toolkit/components/normandy/test/browser/browser_FilterExpressions.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_FilterExpressions.js
+++ b/toolkit/components/normandy/test/browser/browser_FilterExpressions.js
@@ -1,11 +1,11 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/FilterExpressions.jsm", this);
+ChromeUtils.import("resource://normandy/lib/FilterExpressions.jsm", this);
 
 // Basic JEXL tests
 add_task(async function() {
   let val;
   // Test that basic expressions work
   val = await FilterExpressions.eval("2+2");
   is(val, 4, "basic expression works");
 
rename from browser/extensions/shield-recipe-client/test/browser/browser_Heartbeat.js
rename to toolkit/components/normandy/test/browser/browser_Heartbeat.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_Heartbeat.js
+++ b/toolkit/components/normandy/test/browser/browser_Heartbeat.js
@@ -1,13 +1,13 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/Heartbeat.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/SandboxManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Heartbeat.jsm", this);
+ChromeUtils.import("resource://normandy/lib/SandboxManager.jsm", this);
 
 /**
  * Assert an array is in non-descending order, and that every element is a number
  */
 function assertOrdered(arr) {
   for (let i = 0; i < arr.length; i++) {
     Assert.equal(typeof arr[i], "number", `element ${i} is type "number"`);
   }
rename from browser/extensions/shield-recipe-client/test/browser/browser_LogManager.js
rename to toolkit/components/normandy/test/browser/browser_LogManager.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_LogManager.js
+++ b/toolkit/components/normandy/test/browser/browser_LogManager.js
@@ -1,12 +1,12 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Log.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/LogManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/LogManager.jsm", this);
 
 add_task(async function() {
   // Ensure that configuring the logger affects all generated loggers.
   const firstLogger = LogManager.getLogger("first");
   LogManager.configure(5);
   const secondLogger = LogManager.getLogger("second");
   is(firstLogger.level, 5, "First logger level inherited from root logger.");
   is(secondLogger.level, 5, "Second logger level inherited from root logger.");
rename from browser/extensions/shield-recipe-client/test/browser/browser_bootstrap.js
rename to toolkit/components/normandy/test/browser/browser_Normandy.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_bootstrap.js
+++ b/toolkit/components/normandy/test/browser/browser_Normandy.js
@@ -1,118 +1,52 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/ShieldRecipeClient.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
-
-// We can't import bootstrap.js directly since it isn't in the jar manifest, but
-// we can use Addon.getResourceURI to get a path to the file and import using
-// that instead.
-ChromeUtils.import("resource://gre/modules/AddonManager.jsm", this);
-const bootstrapPromise = AddonManager.getAddonByID("shield-recipe-client@mozilla.org").then(addon => {
-  const bootstrapUri = addon.getResourceURI("bootstrap.js");
-  const {Bootstrap} = ChromeUtils.import(bootstrapUri.spec, {});
-  return Bootstrap;
-});
-
-// Use a decorator to get around getAddonByID being async.
-function withBootstrap(testFunction) {
-  return async function wrappedTestFunction(...args) {
-    const Bootstrap = await bootstrapPromise;
-    return testFunction(...args, Bootstrap);
-  };
-}
-
-const initPref1 = "test.initShieldPrefs1";
-const initPref2 = "test.initShieldPrefs2";
-const initPref3 = "test.initShieldPrefs3";
+ChromeUtils.import("resource://normandy/Normandy.jsm", this);
+ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
+ChromeUtils.import("resource://normandy/lib/PreferenceExperiments.jsm", this);
+ChromeUtils.import("resource://normandy/lib/RecipeRunner.jsm", this);
+ChromeUtils.import("resource://normandy/lib/TelemetryEvents.jsm", this);
+ChromeUtils.import("resource://normandy-content/AboutPages.jsm", this);
 
 const experimentPref1 = "test.initExperimentPrefs1";
 const experimentPref2 = "test.initExperimentPrefs2";
 const experimentPref3 = "test.initExperimentPrefs3";
 const experimentPref4 = "test.initExperimentPrefs4";
 
-decorate_task(
-  withBootstrap,
-  async function testInitShieldPrefs(Bootstrap) {
-    const defaultBranch = Services.prefs.getDefaultBranch("");
-
-    const prefDefaults = {
-      [initPref1]: true,
-      [initPref2]: 2,
-      [initPref3]: "string",
-    };
-
-    for (const pref of Object.keys(prefDefaults)) {
-      is(
-        defaultBranch.getPrefType(pref),
-        defaultBranch.PREF_INVALID,
-        `Pref ${pref} don't exist before being initialized.`,
-      );
-    }
-
-    Bootstrap.initShieldPrefs(prefDefaults);
-
-    ok(
-      defaultBranch.getBoolPref(initPref1),
-      `Pref ${initPref1} has a default value after being initialized.`,
-    );
-    is(
-      defaultBranch.getIntPref(initPref2),
-      2,
-      `Pref ${initPref2} has a default value after being initialized.`,
-    );
-    is(
-      defaultBranch.getCharPref(initPref3),
-      "string",
-      `Pref ${initPref3} has a default value after being initialized.`,
-    );
-
-    for (const pref of Object.keys(prefDefaults)) {
-      ok(
-        !defaultBranch.prefHasUserValue(pref),
-        `Pref ${pref} doesn't have a user value after being initialized.`,
-      );
-    }
-
-    defaultBranch.deleteBranch("test.");
-  },
-);
-
-decorate_task(
-  withBootstrap,
-  async function testInitShieldPrefsError(Bootstrap) {
-    Assert.throws(
-      () => Bootstrap.initShieldPrefs({"test.prefTypeError": new Date()}),
-      "initShieldPrefs throws when given an invalid type for the pref value.",
-    );
-  },
-);
+function withStubInits(testFunction) {
+  return decorate(
+    withStub(AboutPages, "init"),
+    withStub(AddonStudies, "init"),
+    withStub(PreferenceExperiments, "init"),
+    withStub(RecipeRunner, "init"),
+    withStub(TelemetryEvents, "init"),
+    testFunction
+  );
+}
 
 decorate_task(
   withPrefEnv({
     set: [
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref1}`, true],
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref2}`, 2],
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref3}`, "string"],
+      [`app.normandy.startupExperimentPrefs.${experimentPref1}`, true],
+      [`app.normandy.startupExperimentPrefs.${experimentPref2}`, 2],
+      [`app.normandy.startupExperimentPrefs.${experimentPref3}`, "string"],
     ],
-    clear: [[experimentPref1], [experimentPref2], [experimentPref3]],
   }),
-  withBootstrap,
-  async function testInitExperimentPrefs(Bootstrap) {
+  async function testInitExperimentPrefs() {
     const defaultBranch = Services.prefs.getDefaultBranch("");
     for (const pref of [experimentPref1, experimentPref2, experimentPref3]) {
       is(
         defaultBranch.getPrefType(pref),
         defaultBranch.PREF_INVALID,
         `Pref ${pref} don't exist before being initialized.`,
       );
     }
 
-    Bootstrap.initExperimentPrefs();
+    Normandy.initExperimentPrefs();
 
     ok(
       defaultBranch.getBoolPref(experimentPref1),
       `Pref ${experimentPref1} has a default value after being initialized.`,
     );
     is(
       defaultBranch.getIntPref(experimentPref2),
       2,
@@ -124,147 +58,223 @@ decorate_task(
       `Pref ${experimentPref3} has a default value after being initialized.`,
     );
 
     for (const pref of [experimentPref1, experimentPref2, experimentPref3]) {
       ok(
         !defaultBranch.prefHasUserValue(pref),
         `Pref ${pref} doesn't have a user value after being initialized.`,
       );
+      Services.prefs.clearUserPref(pref);
+      defaultBranch.deleteBranch(pref);
     }
   },
 );
 
 decorate_task(
   withPrefEnv({
     set: [
-      ["extensions.shield-recipe-client.startupExperimentPrefs.test.existingPref", "experiment"],
+      ["app.normandy.startupExperimentPrefs.test.existingPref", "experiment"],
     ],
   }),
-  withBootstrap,
-  async function testInitExperimentPrefsExisting(Bootstrap) {
+  async function testInitExperimentPrefsExisting() {
     const defaultBranch = Services.prefs.getDefaultBranch("");
     defaultBranch.setCharPref("test.existingPref", "default");
-    Bootstrap.initExperimentPrefs();
+    Normandy.initExperimentPrefs();
     is(
       defaultBranch.getCharPref("test.existingPref"),
       "experiment",
       "initExperimentPrefs overwrites the default values of existing preferences.",
     );
   },
 );
 
 decorate_task(
   withPrefEnv({
     set: [
-      ["extensions.shield-recipe-client.startupExperimentPrefs.test.mismatchPref", "experiment"],
+      ["app.normandy.startupExperimentPrefs.test.mismatchPref", "experiment"],
     ],
   }),
-  withBootstrap,
-  async function testInitExperimentPrefsMismatch(Bootstrap) {
+  async function testInitExperimentPrefsMismatch() {
     const defaultBranch = Services.prefs.getDefaultBranch("");
     defaultBranch.setIntPref("test.mismatchPref", 2);
-    Bootstrap.initExperimentPrefs();
+    Normandy.initExperimentPrefs();
     is(
       defaultBranch.getPrefType("test.mismatchPref"),
       Services.prefs.PREF_INT,
       "initExperimentPrefs skips prefs that don't match the existing default value's type.",
     );
   },
 );
 
 decorate_task(
-  withBootstrap,
-  async function testStartupDelayed(Bootstrap) {
-    const finishStartupStub = sinon.stub(Bootstrap, "finishStartup");
-    try {
-      Bootstrap.startup(undefined, 1); // 1 == APP_STARTUP
-      ok(
-        !finishStartupStub.called,
-        "When started at app startup, do not call ShieldRecipeClient.startup immediately.",
-      );
+  withStub(Normandy, "finishInit"),
+  async function testStartupDelayed(finishInitStub) {
+    Normandy.init();
+    ok(
+      !finishInitStub.called,
+      "When initialized, do not call finishInit immediately.",
+    );
 
-      Bootstrap.observe(null, "sessionstore-windows-restored");
-      ok(
-        finishStartupStub.called,
-        "Once the sessionstore-windows-restored event is observed, call ShieldRecipeClient.startup.",
-      );
-    } finally {
-      finishStartupStub.restore();
-    }
-  },
-);
-
-decorate_task(
-  withBootstrap,
-  async function testStartupDelayed(Bootstrap) {
-    const finishStartupStub = sinon.stub(Bootstrap, "finishStartup");
-    try {
-      Bootstrap.startup(undefined, 3); // 3 == ADDON_ENABLED
-      ok(
-        finishStartupStub.called,
-        "When the add-on is enabled outside app startup, call ShieldRecipeClient.startup immediately.",
-      );
-    } finally {
-      finishStartupStub.restore();
-    }
+    Normandy.observe(null, "sessionstore-windows-restored");
+    ok(
+      finishInitStub.called,
+      "Once the sessionstore-windows-restored event is observed, finishInit should be called.",
+    );
   },
 );
 
 // During startup, preferences that are changed for experiments should
 // be record by calling PreferenceExperiments.recordOriginalValues.
 decorate_task(
   withPrefEnv({
     set: [
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref1}`, true],
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref2}`, 2],
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref3}`, "string"],
-      [`extensions.shield-recipe-client.startupExperimentPrefs.${experimentPref4}`, "another string"],
-    ],
-    clear: [
-      [experimentPref1],
-      [experimentPref2],
-      [experimentPref3],
-      [experimentPref4],
-      ["extensions.shield-recipe-client.startupExperimentPrefs.existingPref"],
+      [`app.normandy.startupExperimentPrefs.${experimentPref1}`, true],
+      [`app.normandy.startupExperimentPrefs.${experimentPref2}`, 2],
+      [`app.normandy.startupExperimentPrefs.${experimentPref3}`, "string"],
+      [`app.normandy.startupExperimentPrefs.${experimentPref4}`, "another string"],
     ],
   }),
-  withBootstrap,
   withStub(PreferenceExperiments, "recordOriginalValues"),
-  async function testInitExperimentPrefs(Bootstrap, recordOriginalValuesStub) {
+  async function testInitExperimentPrefs(recordOriginalValuesStub) {
     const defaultBranch = Services.prefs.getDefaultBranch("");
 
     defaultBranch.setBoolPref(experimentPref1, false);
     defaultBranch.setIntPref(experimentPref2, 1);
     defaultBranch.setCharPref(experimentPref3, "original string");
     // experimentPref4 is left unset
 
-    Bootstrap.initExperimentPrefs();
-    await Bootstrap.finishStartup();
+    Normandy.initExperimentPrefs();
+    await Normandy.finishInit();
 
     Assert.deepEqual(
       recordOriginalValuesStub.getCall(0).args,
       [{
         [experimentPref1]: false,
         [experimentPref2]: 1,
         [experimentPref3]: "original string",
         [experimentPref4]: null,  // null because it was not initially set.
       }],
-      "finishStartup should record original values of the prefs initExperimentPrefs changed",
+      "finishInit should record original values of the prefs initExperimentPrefs changed",
     );
+
+    for (const pref of [experimentPref1, experimentPref2, experimentPref3, experimentPref4]) {
+      Services.prefs.clearUserPref(pref);
+      defaultBranch.deleteBranch(pref);
+    }
   },
 );
 
 // Test that startup prefs are handled correctly when there is a value on the user branch but not the default branch.
 decorate_task(
   withPrefEnv({
     set: [
-      ["extensions.shield-recipe-client.startupExperimentPrefs.testing.does-not-exist", "foo"],
+      ["app.normandy.startupExperimentPrefs.testing.does-not-exist", "foo"],
       ["testing.does-not-exist", "foo"],
     ],
   }),
-  withBootstrap,
   withStub(PreferenceExperiments, "recordOriginalValues"),
-  async function testInitExperimentPrefsNoDefaultValue(Bootstrap) {
-    Bootstrap.initExperimentPrefs();
+  async function testInitExperimentPrefsNoDefaultValue() {
+    Normandy.initExperimentPrefs();
     ok(true, "initExperimentPrefs should not throw for non-existant prefs");
   },
 );
+
+decorate_task(
+  withStubInits,
+  async function testStartup() {
+    const initObserved = TestUtils.topicObserved("shield-init-complete");
+    await Normandy.finishInit();
+    ok(AboutPages.init.called, "startup calls AboutPages.init");
+    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
+    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
+    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
+    await initObserved;
+  }
+);
+
+decorate_task(
+  withStubInits,
+  async function testStartupPrefInitFail() {
+    PreferenceExperiments.init.returns(Promise.reject(new Error("oh no")));
+
+    await Normandy.finishInit();
+    ok(AboutPages.init.called, "startup calls AboutPages.init");
+    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
+    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
+    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
+    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
+  }
+);
+
+decorate_task(
+  withStubInits,
+  async function testStartupAboutPagesInitFail() {
+    AboutPages.init.returns(Promise.reject(new Error("oh no")));
+
+    await Normandy.finishInit();
+    ok(AboutPages.init.called, "startup calls AboutPages.init");
+    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
+    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
+    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
+    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
+  }
+);
+
+decorate_task(
+  withStubInits,
+  async function testStartupAddonStudiesInitFail() {
+    AddonStudies.init.returns(Promise.reject(new Error("oh no")));
+
+    await Normandy.finishInit();
+    ok(AboutPages.init.called, "startup calls AboutPages.init");
+    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
+    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
+    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
+    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
+  }
+);
+
+decorate_task(
+  withStubInits,
+  async function testStartupTelemetryEventsInitFail() {
+    TelemetryEvents.init.throws();
+
+    await Normandy.finishInit();
+    ok(AboutPages.init.called, "startup calls AboutPages.init");
+    ok(AddonStudies.init.called, "startup calls AddonStudies.init");
+    ok(PreferenceExperiments.init.called, "startup calls PreferenceExperiments.init");
+    ok(RecipeRunner.init.called, "startup calls RecipeRunner.init");
+    ok(TelemetryEvents.init.called, "startup calls TelemetryEvents.init");
+  }
+);
+
+decorate_task(
+  withMockPreferences,
+  async function testPrefMigration(mockPreferences) {
+    const legacyPref = "extensions.shield-recipe-client.test";
+    const migratedPref = "app.normandy.test";
+    mockPreferences.set(legacyPref, 1);
+
+    ok(
+      Services.prefs.prefHasUserValue(legacyPref),
+      "Legacy pref should have a user value before running migration",
+    );
+    ok(
+      !Services.prefs.prefHasUserValue(migratedPref),
+      "Migrated pref should not have a user value before running migration",
+    );
+
+    Normandy.migrateShieldPrefs();
+
+    ok(
+      !Services.prefs.prefHasUserValue(legacyPref),
+      "Legacy pref should not have a user value after running migration",
+    );
+    ok(
+      Services.prefs.prefHasUserValue(migratedPref),
+      "Migrated pref should have a user value after running migration",
+    );
+    is(Services.prefs.getIntPref(migratedPref), 1, "Value should have been migrated");
+
+    Services.prefs.clearUserPref(migratedPref);
+  },
+);
rename from browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js
rename to toolkit/components/normandy/test/browser/browser_NormandyDriver.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js
+++ b/toolkit/components/normandy/test/browser/browser_NormandyDriver.js
@@ -1,15 +1,15 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
+ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
+ChromeUtils.import("resource://normandy/lib/NormandyDriver.jsm", this);
+ChromeUtils.import("resource://normandy/lib/PreferenceExperiments.jsm", this);
 
 add_task(withDriver(Assert, async function uuids(driver) {
   // Test that it is a UUID
   const uuid1 = driver.uuid();
   ok(UUID_REGEX.test(uuid1), "valid uuid format");
 
   // Test that UUIDs are different each time
   const uuid2 = driver.uuid();
rename from browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js
rename to toolkit/components/normandy/test/browser/browser_PreferenceExperiments.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js
+++ b/toolkit/components/normandy/test/browser/browser_PreferenceExperiments.js
@@ -1,20 +1,20 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
 ChromeUtils.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/CleanupManager.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/TelemetryEvents.jsm", this);
+ChromeUtils.import("resource://normandy/lib/PreferenceExperiments.jsm", this);
+ChromeUtils.import("resource://normandy/lib/CleanupManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/TelemetryEvents.jsm", this);
 
 // Save ourselves some typing
 const {withMockExperiments} = PreferenceExperiments;
 const DefaultPreferences = new Preferences({defaultBranch: true});
-const startupPrefs = "extensions.shield-recipe-client.startupExperimentPrefs";
+const startupPrefs = "app.normandy.startupExperimentPrefs";
 
 function experimentFactory(attrs) {
   return Object.assign({
     name: "fakename",
     branch: "fakebranch",
     expired: false,
     lastSeen: new Date().toJSON(),
     preferenceName: "fake.preference",
@@ -1044,37 +1044,39 @@ decorate_task(
   }
 );
 
 // Should send the correct event telemetry when a study ends because
 // the user changed preferences during a browser run.
 decorate_task(
   withMockPreferences,
   withStub(TelemetryEvents, "sendEvent"),
-  async function testPrefChangeEventTelemetry(mockPreferences, sendEventStub) {
+  withMockExperiments,
+  async function testPrefChangeEventTelemetry(mockPreferences, sendEventStub, mockExperiments) {
     is(Preferences.get("fake.preference"), null, "preference should start unset");
-
-    await PreferenceExperiments.start({
+    mockPreferences.set("fake.preference", "oldvalue", "default");
+    mockExperiments.test = experimentFactory({
       name: "test",
-      branch: "branch",
+      expired: false,
       preferenceName: "fake.preference",
       preferenceValue: "experimentvalue",
+      preferenceType: "string",
+      previousPreferenceValue: "oldvalue",
       preferenceBranchType: "default",
-      preferenceType: "string",
     });
+    PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
 
     // setting the preference on the user branch should trigger the observer to stop the experiment
     mockPreferences.set("fake.preference", "uservalue", "user");
 
     // let the event loop tick to run the observer
     await Promise.resolve();
 
-    is(sendEventStub.getCall(0).args[0], "enroll", "There is an enrollment event from start()");
     Assert.deepEqual(
-      sendEventStub.getCall(1).args,
+      sendEventStub.getCall(0).args,
       ["unenroll", "preference_study", "test", {
         didResetValue: "false",
         reason: "user-preference-changed",
       }],
       "stop should send a telemetry event indicating the user unenrolled manually",
     );
   },
 );
rename from browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
rename to toolkit/components/normandy/test/browser/browser_RecipeRunner.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
+++ b/toolkit/components/normandy/test/browser/browser_RecipeRunner.js
@@ -1,18 +1,18 @@
 "use strict";
 
 ChromeUtils.import("resource://testing-common/TestUtils.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/CleanupManager.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyApi.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/ActionSandboxManager.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/Uptake.jsm", this);
+ChromeUtils.import("resource://normandy/lib/RecipeRunner.jsm", this);
+ChromeUtils.import("resource://normandy/lib/ClientEnvironment.jsm", this);
+ChromeUtils.import("resource://normandy/lib/CleanupManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/NormandyApi.jsm", this);
+ChromeUtils.import("resource://normandy/lib/ActionSandboxManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Uptake.jsm", this);
 
 add_task(async function getFilterContext() {
   const recipe = {id: 17, arguments: {foo: "bar"}, unrelated: false};
   const context = RecipeRunner.getFilterContext(recipe);
 
   // Test for expected properties in the filter expression context.
   const expectedNormandyKeys = [
     "channel",
@@ -68,31 +68,31 @@ add_task(async function checkFilter() {
 
 decorate_task(
   withMockNormandyApi,
   withStub(ClientEnvironment, "getClientClassification"),
   async function testClientClassificationCache(api, getStub) {
     getStub.returns(Promise.resolve(false));
 
     await SpecialPowers.pushPrefEnv({set: [
-      ["extensions.shield-recipe-client.api_url",
+      ["app.normandy.api_url",
        "https://example.com/selfsupport-dummy"],
     ]});
 
     // When the experiment pref is false, eagerly call getClientClassification.
     await SpecialPowers.pushPrefEnv({set: [
-      ["extensions.shield-recipe-client.experiments.lazy_classify", false],
+      ["app.normandy.experiments.lazy_classify", false],
     ]});
     ok(!getStub.called, "getClientClassification hasn't been called");
     await RecipeRunner.run();
     ok(getStub.called, "getClientClassification was called eagerly");
 
     // When the experiment pref is true, do not eagerly call getClientClassification.
     await SpecialPowers.pushPrefEnv({set: [
-      ["extensions.shield-recipe-client.experiments.lazy_classify", true],
+      ["app.normandy.experiments.lazy_classify", true],
     ]});
     getStub.reset();
     ok(!getStub.called, "getClientClassification hasn't been called");
     await RecipeRunner.run();
     ok(!getStub.called, "getClientClassification was not called eagerly");
   }
 );
 
@@ -337,112 +337,112 @@ decorate_task(
   }
 );
 
 // test init() in dev mode
 decorate_task(
   withPrefEnv({
     set: [
       ["datareporting.healthreport.uploadEnabled", true],  // telemetry enabled
-      ["extensions.shield-recipe-client.dev_mode", true],
-      ["extensions.shield-recipe-client.first_run", false],
+      ["app.normandy.dev_mode", true],
+      ["app.normandy.first_run", false],
     ],
   }),
   withStub(RecipeRunner, "run"),
   withStub(RecipeRunner, "registerTimer"),
   async function testInitDevMode(runStub, registerTimerStub, updateRunIntervalStub) {
     await RecipeRunner.init();
     ok(runStub.called, "RecipeRunner.run is called immediately when in dev mode");
     ok(registerTimerStub.called, "RecipeRunner.init registers a timer");
   }
 );
 
 // Test init() during normal operation
 decorate_task(
   withPrefEnv({
     set: [
       ["datareporting.healthreport.uploadEnabled", true],  // telemetry enabled
-      ["extensions.shield-recipe-client.dev_mode", false],
-      ["extensions.shield-recipe-client.first_run", false],
+      ["app.normandy.dev_mode", false],
+      ["app.normandy.first_run", false],
     ],
   }),
   withStub(RecipeRunner, "run"),
   withStub(RecipeRunner, "registerTimer"),
   async function testInit(runStub, registerTimerStub) {
     await RecipeRunner.init();
     ok(!runStub.called, "RecipeRunner.run is called immediately when not in dev mode or first run");
     ok(registerTimerStub.called, "RecipeRunner.init registers a timer");
   }
 );
 
 // Test init() first run
 decorate_task(
   withPrefEnv({
     set: [
       ["datareporting.healthreport.uploadEnabled", true],  // telemetry enabled
-      ["extensions.shield-recipe-client.dev_mode", false],
-      ["extensions.shield-recipe-client.first_run", true],
-      ["extensions.shield-recipe-client.api_url", "https://example.com"],
+      ["app.normandy.dev_mode", false],
+      ["app.normandy.first_run", true],
+      ["app.normandy.api_url", "https://example.com"],
     ],
   }),
   withStub(RecipeRunner, "run"),
   withStub(RecipeRunner, "registerTimer"),
   withStub(RecipeRunner, "watchPrefs"),
   async function testInitFirstRun(runStub, registerTimerStub, watchPrefsStub) {
     await RecipeRunner.init();
     ok(runStub.called, "RecipeRunner.run is called immediately on first run");
     ok(
-      !Services.prefs.getBoolPref("extensions.shield-recipe-client.first_run"),
+      !Services.prefs.getBoolPref("app.normandy.first_run"),
       "On first run, the first run pref is set to false"
     );
     ok(registerTimerStub.called, "RecipeRunner.registerTimer registers a timer");
 
     // RecipeRunner.init() sets this to false, but SpecialPowers
     // relies on the preferences it manages to actually change when it
     // tries to change them. Settings this back to true here allows
     // that to happen. Not doing this causes popPrefEnv to hang forever.
-    Services.prefs.setBoolPref("extensions.shield-recipe-client.first_run", true);
+    Services.prefs.setBoolPref("app.normandy.first_run", true);
   }
 );
 
 // Test that prefs are watched correctly
 decorate_task(
   withPrefEnv({
     set: [
       ["datareporting.healthreport.uploadEnabled", true],  // telemetry enabled
-      ["extensions.shield-recipe-client.dev_mode", false],
-      ["extensions.shield-recipe-client.first_run", false],
-      ["extensions.shield-recipe-client.enabled", true],
-      ["extensions.shield-recipe-client.api_url", "https://example.com"], // starts with "https://"
+      ["app.normandy.dev_mode", false],
+      ["app.normandy.first_run", false],
+      ["app.normandy.enabled", true],
+      ["app.normandy.api_url", "https://example.com"], // starts with "https://"
     ],
   }),
   withStub(RecipeRunner, "run"),
   withStub(RecipeRunner, "enable"),
   withStub(RecipeRunner, "disable"),
   withStub(CleanupManager, "addCleanupHandler"),
   withStub(AddonStudies, "stop"),
 
   async function testPrefWatching(runStub, enableStub, disableStub, addCleanupHandlerStub, stopStub) {
     await RecipeRunner.init();
     is(enableStub.callCount, 1, "Enable should be called initially");
     is(disableStub.callCount, 0, "Disable should not be called initially");
 
-    await SpecialPowers.pushPrefEnv({ set: [["extensions.shield-recipe-client.enabled", false]] });
+    await SpecialPowers.pushPrefEnv({ set: [["app.normandy.enabled", false]] });
     is(enableStub.callCount, 1, "Enable should not be called again");
     is(disableStub.callCount, 1, "RecipeRunner should disable when Shield is disabled");
 
-    await SpecialPowers.pushPrefEnv({ set: [["extensions.shield-recipe-client.enabled", true]] });
+    await SpecialPowers.pushPrefEnv({ set: [["app.normandy.enabled", true]] });
     is(enableStub.callCount, 2, "RecipeRunner should re-enable when Shield is enabled");
     is(disableStub.callCount, 1, "Disable should not be called again");
 
-    await SpecialPowers.pushPrefEnv({ set: [["extensions.shield-recipe-client.api_url", "http://example.com"]] }); // does not start with https://
+    await SpecialPowers.pushPrefEnv({ set: [["app.normandy.api_url", "http://example.com"]] }); // does not start with https://
     is(enableStub.callCount, 2, "Enable should not be called again");
     is(disableStub.callCount, 2, "RecipeRunner should disable when an invalid api url is given");
 
-    await SpecialPowers.pushPrefEnv({ set: [["extensions.shield-recipe-client.api_url", "https://example.com"]] }); // ends with https://
+    await SpecialPowers.pushPrefEnv({ set: [["app.normandy.api_url", "https://example.com"]] }); // ends with https://
     is(enableStub.callCount, 3, "RecipeRunner should re-enable when a valid api url is given");
     is(disableStub.callCount, 2, "Disable should not be called again");
 
     await SpecialPowers.pushPrefEnv({ set: [["datareporting.healthreport.uploadEnabled", false]] });
     is(enableStub.callCount, 3, "Enable should not be called again");
     is(disableStub.callCount, 3, "RecipeRunner should disable when telemetry is disabled");
 
     await SpecialPowers.pushPrefEnv({ set: [["datareporting.healthreport.uploadEnabled", true]] });
rename from browser/extensions/shield-recipe-client/test/browser/browser_ShieldPreferences.js
rename to toolkit/components/normandy/test/browser/browser_ShieldPreferences.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ShieldPreferences.js
+++ b/toolkit/components/normandy/test/browser/browser_ShieldPreferences.js
@@ -1,12 +1,12 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
+ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
 
 const OPT_OUT_PREF = "app.shield.optoutstudies.enabled";
 
 decorate_task(
   withMockPreferences,
   AddonStudies.withStudies([
     studyFactory({active: true}),
     studyFactory({active: true}),
rename from browser/extensions/shield-recipe-client/test/browser/browser_Storage.js
rename to toolkit/components/normandy/test/browser/browser_Storage.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_Storage.js
+++ b/toolkit/components/normandy/test/browser/browser_Storage.js
@@ -1,12 +1,12 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/Storage.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/SandboxManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Storage.jsm", this);
+ChromeUtils.import("resource://normandy/lib/SandboxManager.jsm", this);
 
 add_task(async function() {
   const store1 = new Storage("prefix1");
   const store2 = new Storage("prefix2");
 
   // Make sure values return null before being set
   Assert.equal(await store1.getItem("key"), null);
   Assert.equal(await store2.getItem("key"), null);
rename from browser/extensions/shield-recipe-client/test/browser/browser_about_preferences.js
rename to toolkit/components/normandy/test/browser/browser_about_preferences.js
rename from browser/extensions/shield-recipe-client/test/browser/browser_about_studies.js
rename to toolkit/components/normandy/test/browser/browser_about_studies.js
--- a/browser/extensions/shield-recipe-client/test/browser/browser_about_studies.js
+++ b/toolkit/components/normandy/test/browser/browser_about_studies.js
@@ -1,33 +1,33 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/RecipeRunner.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client-content/AboutPages.jsm", this);
+ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
+ChromeUtils.import("resource://normandy/lib/RecipeRunner.jsm", this);
+ChromeUtils.import("resource://normandy-content/AboutPages.jsm", this);
 
 function withAboutStudies(testFunc) {
-  return async (...args) => (
+return async (...args) => (
     BrowserTestUtils.withNewTab("about:studies", async browser => (
       testFunc(...args, browser)
     ))
   );
 }
 
 decorate_task(
   withAboutStudies,
   async function testAboutStudiesWorks(browser) {
     // eslint-disable-next-line mozilla/no-cpows-in-tests
     ok(browser.contentDocumentAsCPOW.getElementById("app"), "App element was found");
   }
 );
 
 decorate_task(
   withPrefEnv({
-    set: [["extensions.shield-recipe-client.shieldLearnMoreUrl", "http://test/%OS%/"]],
+    set: [["app.normandy.shieldLearnMoreUrl", "http://test/%OS%/"]],
   }),
   withAboutStudies,
   async function testLearnMore(browser) {
     ContentTask.spawn(browser, null, () => {
       content.document.getElementById("shield-studies-learn-more").click();
     });
     await BrowserTestUtils.waitForLocationChange(gBrowser);
 
rename from browser/extensions/shield-recipe-client/test/browser/fixtures/addon-fixture/manifest.json
rename to toolkit/components/normandy/test/browser/fixtures/addon-fixture/manifest.json
rename from browser/extensions/shield-recipe-client/test/browser/fixtures/normandy.xpi
rename to toolkit/components/normandy/test/browser/fixtures/normandy.xpi
rename from browser/extensions/shield-recipe-client/test/browser/head.js
rename to toolkit/components/normandy/test/browser/head.js
--- a/browser/extensions/shield-recipe-client/test/browser/head.js
+++ b/toolkit/components/normandy/test/browser/head.js
@@ -1,17 +1,18 @@
 ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
 ChromeUtils.import("resource://testing-common/TestUtils.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/Addons.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/SandboxManager.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyApi.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/TelemetryEvents.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/Utils.jsm", this);
+ChromeUtils.import("resource://normandy-content/AboutPages.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Addons.jsm", this);
+ChromeUtils.import("resource://normandy/lib/SandboxManager.jsm", this);
+ChromeUtils.import("resource://normandy/lib/NormandyDriver.jsm", this);
+ChromeUtils.import("resource://normandy/lib/NormandyApi.jsm", this);
+ChromeUtils.import("resource://normandy/lib/TelemetryEvents.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Utils.jsm", this);
 
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/docs/
 /* global sinon */
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
 // Make sinon assertions fail in a way that mochitest understands
 sinon.assert.fail = function(message) {
@@ -168,20 +169,26 @@ class MockPreferences {
   set(name, value, branch = "user") {
     this.preserve(name, branch);
     preferenceBranches[branch].set(name, value);
   }
 
   preserve(name, branch) {
     if (!(name in this.oldValues[branch])) {
       const preferenceBranch = preferenceBranches[branch];
-      this.oldValues[branch][name] = {
-        oldValue: preferenceBranch.get(name),
-        existed: preferenceBranch.has(name),
-      };
+      let oldValue;
+      let existed;
+      try {
+        oldValue = preferenceBranch.get(name);
+        existed = preferenceBranch.has(name);
+      } catch (e) {
+        oldValue = null;
+        existed = false;
+      }
+      this.oldValues[branch][name] = {oldValue, existed};
     }
   }
 
   cleanup() {
     for (const [branchName, values] of Object.entries(this.oldValues)) {
       const preferenceBranch = preferenceBranches[branchName];
       for (const [name, {oldValue, existed}] of Object.entries(values)) {
         const before = preferenceBranch.get(name);
rename from browser/extensions/shield-recipe-client/test/unit/.eslintrc.js
rename to toolkit/components/normandy/test/unit/.eslintrc.js
rename from browser/extensions/shield-recipe-client/test/unit/echo_server.sjs
rename to toolkit/components/normandy/test/unit/echo_server.sjs
rename from browser/extensions/shield-recipe-client/test/unit/head_xpc.js
rename to toolkit/components/normandy/test/unit/head_xpc.js
--- a/browser/extensions/shield-recipe-client/test/unit/head_xpc.js
+++ b/toolkit/components/normandy/test/unit/head_xpc.js
@@ -1,26 +1,12 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-// Load our bootstrap extension manifest so we can access our chrome/resource URIs.
-// Cargo culted from formautofill system add-on
-const EXTENSION_ID = "shield-recipe-client@mozilla.org";
-let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
-extensionDir.append("browser");
-extensionDir.append("features");
-extensionDir.append(EXTENSION_ID);
-// If the unpacked extension doesn't exist, use the packed version.
-if (!extensionDir.exists()) {
-  extensionDir = extensionDir.parent;
-  extensionDir.append(EXTENSION_ID + ".xpi");
-}
-Components.manager.addBootstrappedManifestLocation(extensionDir);
-
 // ================================================
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/releases/v2.3.2/
 /* exported sinon */
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
 // ================================================
rename from browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/index.json
rename to toolkit/components/normandy/test/unit/invalid_recipe_signature_api/api/v1/index.json
rename from browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/recipe/signed/index.json
rename to toolkit/components/normandy/test/unit/invalid_recipe_signature_api/api/v1/recipe/signed/index.json
rename from browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/normandy.content-signature.mozilla.org-20210705.dev.chain
rename to toolkit/components/normandy/test/unit/invalid_recipe_signature_api/normandy.content-signature.mozilla.org-20210705.dev.chain
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/console-log/implementation/sha384-RGx3rydrSq53UfmW9kFcK0mQYra67XIvZvr4MhmAe--ljiiMQOtgM7Cmca48um3v
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/console-log/implementation/sha384-RGx3rydrSq53UfmW9kFcK0mQYra67XIvZvr4MhmAe--ljiiMQOtgM7Cmca48um3v
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/console-log/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/console-log/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/opt-out-study/implementation/sha384-HM_avYcD00o27ufwU1V7PIBtiuMAXML6MMwlYrDEqDX-XzGVuOfL52RCM680JExN
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/opt-out-study/implementation/sha384-HM_avYcD00o27ufwU1V7PIBtiuMAXML6MMwlYrDEqDX-XzGVuOfL52RCM680JExN
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/opt-out-study/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/opt-out-study/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/preference-experiment/implementation/sha384-KQgG38GQ7KZAb2VIB48ANQO6nBcxZoLm2ORzUviRT5nAvSywyPjZ5cJIElw6iXIt
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/preference-experiment/implementation/sha384-KQgG38GQ7KZAb2VIB48ANQO6nBcxZoLm2ORzUviRT5nAvSywyPjZ5cJIElw6iXIt
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/preference-experiment/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/preference-experiment/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/show-heartbeat/implementation/sha384-dEGiyKPEln8Ns5cQHzGpMIGdirSAAX0X-Kwlu-U3sJ05yNbO-ANij_a6c5SyL7G4
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/show-heartbeat/implementation/sha384-dEGiyKPEln8Ns5cQHzGpMIGdirSAAX0X-Kwlu-U3sJ05yNbO-ANij_a6c5SyL7G4
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/show-heartbeat/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/show-heartbeat/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/signed/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/action/signed/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/classify_client/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/classify_client/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/recipe/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/recipe/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/recipe/signed/index.json
rename to toolkit/components/normandy/test/unit/mock_api/api/v1/recipe/signed/index.json
rename from browser/extensions/shield-recipe-client/test/unit/mock_api/normandy.content-signature.mozilla.org-20210705.dev.chain
rename to toolkit/components/normandy/test/unit/mock_api/normandy.content-signature.mozilla.org-20210705.dev.chain
rename from browser/extensions/shield-recipe-client/test/unit/query_server.sjs
rename to toolkit/components/normandy/test/unit/query_server.sjs
rename from browser/extensions/shield-recipe-client/test/unit/test_NormandyApi.js
rename to toolkit/components/normandy/test/unit/test_NormandyApi.js
--- a/browser/extensions/shield-recipe-client/test/unit/test_NormandyApi.js
+++ b/toolkit/components/normandy/test/unit/test_NormandyApi.js
@@ -1,16 +1,16 @@
 /* globals sinon */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://gre/modules/CanonicalJSON.jsm", this);
 ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
-ChromeUtils.import("resource://shield-recipe-client/lib/NormandyApi.jsm", this);
+ChromeUtils.import("resource://normandy/lib/NormandyApi.jsm", this);
 
 load("utils.js"); /* globals withMockPreferences */
 
 class MockResponse {
   constructor(content) {
     this.content = content;
   }
 
@@ -21,17 +21,17 @@ class MockResponse {
   async json() {
     return JSON.parse(this.content);
   }
 }
 
 function withServer(server, task) {
   return withMockPreferences(async function inner(preferences) {
     const serverUrl = `http://localhost:${server.identity.primaryPort}`;
-    preferences.set("extensions.shield-recipe-client.api_url", `${serverUrl}/api/v1`);
+    preferences.set("app.normandy.api_url", `${serverUrl}/api/v1`);
     preferences.set(
       "security.content.signature.root_hash",
       // Hash of the key that signs the normandy dev certificates
       "4C:35:B1:C3:E3:12:D9:55:E7:78:ED:D0:A7:E7:8A:38:83:04:EF:01:BF:FA:03:29:B2:46:9F:3C:C5:EC:36:04"
     );
     NormandyApi.clearIndexCache();
 
     try {
@@ -106,27 +106,27 @@ add_task(withMockApiServer(async functio
 
 add_task(withMockApiServer(async function test_getApiUrlSlashes(serverUrl, preferences) {
   const fakeResponse = new MockResponse(JSON.stringify({"test-endpoint": `${serverUrl}/test/`}));
   const mockGet = sinon.stub(NormandyApi, "get", async () => fakeResponse);
 
   // without slash
   {
     NormandyApi.clearIndexCache();
-    preferences.set("extensions.shield-recipe-client.api_url", `${serverUrl}/api/v1`);
+    preferences.set("app.normandy.api_url", `${serverUrl}/api/v1`);
     const endpoint = await NormandyApi.getApiUrl("test-endpoint");
     equal(endpoint, `${serverUrl}/test/`);
     ok(mockGet.calledWithExactly(`${serverUrl}/api/v1/`), "trailing slash was added");
     mockGet.reset();
   }
 
   // with slash
   {
     NormandyApi.clearIndexCache();
-    preferences.set("extensions.shield-recipe-client.api_url", `${serverUrl}/api/v1/`);
+    preferences.set("app.normandy.api_url", `${serverUrl}/api/v1/`);
     const endpoint = await NormandyApi.getApiUrl("test-endpoint");
     equal(endpoint, `${serverUrl}/test/`);
     ok(mockGet.calledWithExactly(`${serverUrl}/api/v1/`), "existing trailing slash was preserved");
     mockGet.reset();
   }
 
   NormandyApi.clearIndexCache();
   mockGet.restore();
rename from browser/extensions/shield-recipe-client/test/unit/test_Sampling.js
rename to toolkit/components/normandy/test/unit/test_Sampling.js
--- a/browser/extensions/shield-recipe-client/test/unit/test_Sampling.js
+++ b/toolkit/components/normandy/test/unit/test_Sampling.js
@@ -1,11 +1,11 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/Sampling.jsm", this);
+ChromeUtils.import("resource://normandy/lib/Sampling.jsm", this);
 
 add_task(async function testStableSample() {
   // Absolute samples
   equal(await Sampling.stableSample("test", 1), true, "stableSample returns true for 100% sample");
   equal(await Sampling.stableSample("test", 0), false, "stableSample returns false for 0% sample");
 
   // Known samples. The numbers are nonces to make the tests pass
   equal(await Sampling.stableSample("test-0", 0.5), true, "stableSample returns true for known matching sample");
rename from browser/extensions/shield-recipe-client/test/unit/test_SandboxManager.js
rename to toolkit/components/normandy/test/unit/test_SandboxManager.js
--- a/browser/extensions/shield-recipe-client/test/unit/test_SandboxManager.js
+++ b/toolkit/components/normandy/test/unit/test_SandboxManager.js
@@ -1,11 +1,11 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/SandboxManager.jsm");
+ChromeUtils.import("resource://normandy/lib/SandboxManager.jsm");
 
 // wrapAsync should wrap privileged Promises with Promises that are usable by
 // the sandbox.
 add_task(function* () {
   const manager = new SandboxManager();
   manager.addHold("testing");
 
   manager.cloneIntoGlobal("driver", {
rename from browser/extensions/shield-recipe-client/test/unit/test_Utils.js
rename to toolkit/components/normandy/test/unit/test_Utils.js
--- a/browser/extensions/shield-recipe-client/test/unit/test_Utils.js
+++ b/toolkit/components/normandy/test/unit/test_Utils.js
@@ -1,11 +1,11 @@
 "use strict";
 
-ChromeUtils.import("resource://shield-recipe-client/lib/Utils.jsm");
+ChromeUtils.import("resource://normandy/lib/Utils.jsm");
 
 add_task(async function testKeyBy() {
   const list = [];
   deepEqual(Utils.keyBy(list, "foo"), {});
 
   const foo = {name: "foo", value: 1};
   list.push(foo);
   deepEqual(Utils.keyBy(list, "name"), {foo});
rename from browser/extensions/shield-recipe-client/test/unit/utils.js
rename to toolkit/components/normandy/test/unit/utils.js
rename from browser/extensions/shield-recipe-client/test/unit/xpcshell.ini
rename to toolkit/components/normandy/test/unit/xpcshell.ini
rename from browser/extensions/shield-recipe-client/vendor/LICENSE_THIRDPARTY
rename to toolkit/components/normandy/vendor/LICENSE_THIRDPARTY
rename from browser/extensions/shield-recipe-client/vendor/PropTypes.js
rename to toolkit/components/normandy/vendor/PropTypes.js
rename from browser/extensions/shield-recipe-client/vendor/React.js
rename to toolkit/components/normandy/vendor/React.js
rename from browser/extensions/shield-recipe-client/vendor/ReactDOM.js
rename to toolkit/components/normandy/vendor/ReactDOM.js
rename from browser/extensions/shield-recipe-client/vendor/classnames.js
rename to toolkit/components/normandy/vendor/classnames.js
rename from browser/extensions/shield-recipe-client/vendor/mozjexl.js
rename to toolkit/components/normandy/vendor/mozjexl.js
--- a/toolkit/components/payments/content/paymentDialogWrapper.js
+++ b/toolkit/components/payments/content/paymentDialogWrapper.js
@@ -253,21 +253,18 @@ var paymentDialogWrapper = {
     });
   },
 
   updateRequest() {
     // There is no need to update this.request since the object is live
     // and will automatically get updated if event.updateWith is used.
     let requestSerialized = this._serializeRequest(this.request);
 
-    this.mm.sendAsyncMessage("paymentChromeToContent", {
-      messageType: "updateState",
-      data: {
-        request: requestSerialized,
-      },
+    this.sendMessageToContent("updateState", {
+      request: requestSerialized,
     });
   },
 
   /**
    * Recursively convert and filter input to the subset of data types supported by JSON
    *
    * @param {*} value - any type of input to serialize
    * @param {string?} name - name or key associated with this input.
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1431,26 +1431,16 @@
     "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 1024,
     "high": 16777216,
     "n_buckets": 200,
     "description": "Heap memory allocated (KB)"
   },
-  "MEMORY_HEAP_COMMITTED_UNUSED": {
-    "record_in_processes": ["main", "content"],
-    "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "low": 1024,
-    "high": 524288,
-    "n_buckets": 50,
-    "description": "Committed, unused heap memory (KB)"
-  },
   "MEMORY_HEAP_OVERHEAD_FRACTION": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
     "bug_numbers": [1252375],
     "expires_in_version": "never",
     "kind": "linear",
     "high": 100,
     "n_buckets": 25,
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -948,17 +948,16 @@
     "LOW_MEMORY_EVENTS_PHYSICAL",
     "LOW_MEMORY_EVENTS_VIRTUAL",
     "MAC_INITFONTLIST_TOTAL",
     "MASTER_PASSWORD_ENABLED",
     "MEDIA_CODEC_USED",
     "MEDIA_WMF_DECODE_ERROR",
     "MEMORY_FREE_PURGED_PAGES_MS",
     "MEMORY_HEAP_ALLOCATED",
-    "MEMORY_HEAP_COMMITTED_UNUSED",
     "MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED",
     "MEMORY_JS_COMPARTMENTS_SYSTEM",
     "MEMORY_JS_COMPARTMENTS_USER",
     "MEMORY_JS_GC_HEAP",
     "MEMORY_STORAGE_SQLITE",
     "MEMORY_VSIZE",
     "MEMORY_VSIZE_MAX_CONTIGUOUS",
     "MIXED_CONTENT_HSTS",
--- a/widget/cocoa/nsNativeThemeCocoa.h
+++ b/widget/cocoa/nsNativeThemeCocoa.h
@@ -158,18 +158,16 @@ protected:
                       nsIFrame* aFrame, uint8_t aWidgetType);
   void DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
                           NSWindow* aWindow);
   void DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect,
                      nsIFrame *aFrame);
   void DrawResizer(CGContextRef cgContext, const HIRect& aRect, nsIFrame *aFrame);
 
   // Scrollbars
-  void GetScrollbarPressStates(nsIFrame *aFrame,
-                               mozilla::EventStates aButtonStates[]);
   nsIFrame* GetParentScrollbarFrame(nsIFrame *aFrame);
   bool IsParentScrollbarRolledOver(nsIFrame* aFrame);
 
 private:
   NSButtonCell* mDisclosureButtonCell;
   NSButtonCell* mHelpButtonCell;
   NSButtonCell* mPushButtonCell;
   NSButtonCell* mRadioButtonCell;
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2052,41 +2052,16 @@ nsNativeThemeCocoa::DrawSegment(CGContex
             (isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state",
             [NSNumber numberWithBool:isFocused], @"focus",
             CUIControlSizeForCocoaSize(controlSize), @"size",
             [NSNumber numberWithBool:YES], @"is.flipped",
             @"up", @"direction",
             nil]);
 }
 
-void
-nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame* aFrame,
-                                            EventStates aButtonStates[])
-{
-  static Element::AttrValuesArray attributeValues[] = {
-    &nsGkAtoms::scrollbarUpTop,
-    &nsGkAtoms::scrollbarDownTop,
-    &nsGkAtoms::scrollbarUpBottom,
-    &nsGkAtoms::scrollbarDownBottom,
-    nullptr
-  };
-
-  // Get the state of any scrollbar buttons in our child frames
-  for (nsIFrame *childFrame : aFrame->PrincipalChildList()) {
-    nsIContent *childContent = childFrame->GetContent();
-    if (!childContent || !childContent->IsElement()) continue;
-    int32_t attrIndex =
-      childContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sbattr,
-                                                 attributeValues, eCaseMatters);
-    if (attrIndex < 0) continue;
-
-    aButtonStates[attrIndex] = GetContentState(childFrame, NS_THEME_BUTTON);
-  }
-}
-
 nsIFrame*
 nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame)
 {
   // Walk our parents to find a scrollbar frame
   nsIFrame* scrollbarFrame = aFrame;
   do {
     if (scrollbarFrame->IsScrollbarFrame()) break;
   } while ((scrollbarFrame = scrollbarFrame->GetParent()));