Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Wed, 06 Jun 2018 12:52:15 +0300
changeset 421554 072201279e41b15e1ff1606bfd7460e3656d13b3
parent 421502 11aacf3b7e93135523e0bd45f5d1b658ebd99a07 (current diff)
parent 421553 cec4a3cecc29ff97860198969b6fdff24b9e93bb (diff)
child 421555 f72ccbb154263d442b58e10a8dc69da9cd004a4d
push id104056
push usernbeleuzu@mozilla.com
push dateWed, 06 Jun 2018 09:52:47 +0000
treeherdermozilla-inbound@072201279e41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.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/components/search/searchplugins/allaannonser-sv-SE.xml
browser/components/search/searchplugins/allegro-pl.xml
browser/components/search/searchplugins/amazon-au.xml
browser/components/search/searchplugins/amazon-br.xml
browser/components/search/searchplugins/amazon-ca.xml
browser/components/search/searchplugins/amazon-en-GB.xml
browser/components/search/searchplugins/amazon-france.xml
browser/components/search/searchplugins/amazon-in.xml
browser/components/search/searchplugins/amazon-it.xml
browser/components/search/searchplugins/amazon-jp.xml
browser/components/search/searchplugins/amazon-mx.xml
browser/components/search/searchplugins/amazon-nl.xml
browser/components/search/searchplugins/amazondotcn.xml
browser/components/search/searchplugins/amazondotcom-de.xml
browser/components/search/searchplugins/amazondotcom.xml
browser/components/search/searchplugins/atlas-sk.xml
browser/components/search/searchplugins/azerdict.xml
browser/components/search/searchplugins/azet-sk.xml
browser/components/search/searchplugins/baidu.xml
browser/components/search/searchplugins/bbc-alba.xml
browser/components/search/searchplugins/bing.xml
browser/components/search/searchplugins/bok-NO.xml
browser/components/search/searchplugins/bolcom-fy-NL.xml
browser/components/search/searchplugins/bolcom-nl.xml
browser/components/search/searchplugins/bookplus-fi.xml
browser/components/search/searchplugins/buscape.xml
browser/components/search/searchplugins/ceneji.xml
browser/components/search/searchplugins/chambers-en-GB.xml
browser/components/search/searchplugins/cnrtl-tlfi-fr.xml
browser/components/search/searchplugins/coccoc.xml
browser/components/search/searchplugins/danawa-kr.xml
browser/components/search/searchplugins/daum-kr.xml
browser/components/search/searchplugins/ddg.xml
browser/components/search/searchplugins/diccionariu-alla.xml
browser/components/search/searchplugins/dict-enlv.xml
browser/components/search/searchplugins/diec2.xml
browser/components/search/searchplugins/drae.xml
browser/components/search/searchplugins/ebay-at.xml
browser/components/search/searchplugins/ebay-au.xml
browser/components/search/searchplugins/ebay-be.xml
browser/components/search/searchplugins/ebay-ca.xml
browser/components/search/searchplugins/ebay-ch.xml
browser/components/search/searchplugins/ebay-de.xml
browser/components/search/searchplugins/ebay-es.xml
browser/components/search/searchplugins/ebay-fr.xml
browser/components/search/searchplugins/ebay-ie.xml
browser/components/search/searchplugins/ebay-it.xml
browser/components/search/searchplugins/ebay-nl.xml
browser/components/search/searchplugins/ebay-uk.xml
browser/components/search/searchplugins/ebay.xml
browser/components/search/searchplugins/ecosia.xml
browser/components/search/searchplugins/eki-ee.xml
browser/components/search/searchplugins/elebila.xml
browser/components/search/searchplugins/eudict.xml
browser/components/search/searchplugins/faclair-beag.xml
browser/components/search/searchplugins/flip.xml
browser/components/search/searchplugins/freelang.xml
browser/components/search/searchplugins/google-2018.xml
browser/components/search/searchplugins/google.xml
browser/components/search/searchplugins/gujaratilexicon.xml
browser/components/search/searchplugins/gulesider-NO.xml
browser/components/search/searchplugins/heureka-cz.xml
browser/components/search/searchplugins/hoepli.xml
browser/components/search/searchplugins/hotline-ua.xml
browser/components/search/searchplugins/images/amazon.ico
browser/components/search/searchplugins/images/ebay.ico
browser/components/search/searchplugins/images/wikipedia.ico
browser/components/search/searchplugins/images/yandex-en.ico
browser/components/search/searchplugins/images/yandex-ru.ico
browser/components/search/searchplugins/kannadastore.xml
browser/components/search/searchplugins/kaz-kk.xml
browser/components/search/searchplugins/klask.xml
browser/components/search/searchplugins/leit-is.xml
browser/components/search/searchplugins/leo_ende_de-rm.xml
browser/components/search/searchplugins/leo_ende_de.xml
browser/components/search/searchplugins/list-am.xml
browser/components/search/searchplugins/list.json
browser/components/search/searchplugins/longdo.xml
browser/components/search/searchplugins/mailru.xml
browser/components/search/searchplugins/mapy-cz.xml
browser/components/search/searchplugins/marktplaats-fy-NL.xml
browser/components/search/searchplugins/marktplaats-nl.xml
browser/components/search/searchplugins/mercadolibre-ar.xml
browser/components/search/searchplugins/mercadolibre-cl.xml
browser/components/search/searchplugins/mercadolibre-mx.xml
browser/components/search/searchplugins/mercadolivre.xml
browser/components/search/searchplugins/meta-ua.xml
browser/components/search/searchplugins/morfix-dic.xml
browser/components/search/searchplugins/najdi-si.xml
browser/components/search/searchplugins/naver-kr.xml
browser/components/search/searchplugins/neti-ee.xml
browser/components/search/searchplugins/odpiralni.xml
browser/components/search/searchplugins/olx.xml
browser/components/search/searchplugins/oshiete-goo.xml
browser/components/search/searchplugins/osta-ee.xml
browser/components/search/searchplugins/ozonru.xml
browser/components/search/searchplugins/palasprint.xml
browser/components/search/searchplugins/paroledigenova-lij.xml
browser/components/search/searchplugins/pazaruvaj.xml
browser/components/search/searchplugins/pledarigrond.xml
browser/components/search/searchplugins/pogodak.xml
browser/components/search/searchplugins/portalbgdict.xml
browser/components/search/searchplugins/priberam.xml
browser/components/search/searchplugins/priceru.xml
browser/components/search/searchplugins/prisjakt-sv-SE.xml
browser/components/search/searchplugins/pwn-pl.xml
browser/components/search/searchplugins/qwant.xml
browser/components/search/searchplugins/qxl-NO.xml
browser/components/search/searchplugins/rakuten.xml
browser/components/search/searchplugins/readmoo.xml
browser/components/search/searchplugins/rediff.xml
browser/components/search/searchplugins/reta-vortaro.xml
browser/components/search/searchplugins/salidzinilv.xml
browser/components/search/searchplugins/sapo.xml
browser/components/search/searchplugins/seznam-cz.xml
browser/components/search/searchplugins/slovnik-sk.xml
browser/components/search/searchplugins/sslv.xml
browser/components/search/searchplugins/sztaki-en-hu.xml
browser/components/search/searchplugins/tearma.xml
browser/components/search/searchplugins/termau.xml
browser/components/search/searchplugins/twitter-ja.xml
browser/components/search/searchplugins/twitter.xml
browser/components/search/searchplugins/tyda-sv-SE.xml
browser/components/search/searchplugins/vatera.xml
browser/components/search/searchplugins/webdunia.xml
browser/components/search/searchplugins/wikipedia-NN.xml
browser/components/search/searchplugins/wikipedia-NO.xml
browser/components/search/searchplugins/wikipedia-af.xml
browser/components/search/searchplugins/wikipedia-an.xml
browser/components/search/searchplugins/wikipedia-ar.xml
browser/components/search/searchplugins/wikipedia-as.xml
browser/components/search/searchplugins/wikipedia-ast.xml
browser/components/search/searchplugins/wikipedia-az.xml
browser/components/search/searchplugins/wikipedia-be-tarask.xml
browser/components/search/searchplugins/wikipedia-be.xml
browser/components/search/searchplugins/wikipedia-bg.xml
browser/components/search/searchplugins/wikipedia-bn.xml
browser/components/search/searchplugins/wikipedia-br.xml
browser/components/search/searchplugins/wikipedia-bs.xml
browser/components/search/searchplugins/wikipedia-ca.xml
browser/components/search/searchplugins/wikipedia-crh.xml
browser/components/search/searchplugins/wikipedia-cy.xml
browser/components/search/searchplugins/wikipedia-cz.xml
browser/components/search/searchplugins/wikipedia-da.xml
browser/components/search/searchplugins/wikipedia-de.xml
browser/components/search/searchplugins/wikipedia-dsb.xml
browser/components/search/searchplugins/wikipedia-el.xml
browser/components/search/searchplugins/wikipedia-eo.xml
browser/components/search/searchplugins/wikipedia-es.xml
browser/components/search/searchplugins/wikipedia-et.xml
browser/components/search/searchplugins/wikipedia-eu.xml
browser/components/search/searchplugins/wikipedia-fa.xml
browser/components/search/searchplugins/wikipedia-fi.xml
browser/components/search/searchplugins/wikipedia-fr.xml
browser/components/search/searchplugins/wikipedia-fy-NL.xml
browser/components/search/searchplugins/wikipedia-ga-IE.xml
browser/components/search/searchplugins/wikipedia-gd.xml
browser/components/search/searchplugins/wikipedia-gl.xml
browser/components/search/searchplugins/wikipedia-gn.xml
browser/components/search/searchplugins/wikipedia-gu.xml
browser/components/search/searchplugins/wikipedia-he.xml
browser/components/search/searchplugins/wikipedia-hi.xml
browser/components/search/searchplugins/wikipedia-hr.xml
browser/components/search/searchplugins/wikipedia-hsb.xml
browser/components/search/searchplugins/wikipedia-hu.xml
browser/components/search/searchplugins/wikipedia-hy.xml
browser/components/search/searchplugins/wikipedia-ia.xml
browser/components/search/searchplugins/wikipedia-id.xml
browser/components/search/searchplugins/wikipedia-is.xml
browser/components/search/searchplugins/wikipedia-it.xml
browser/components/search/searchplugins/wikipedia-ja.xml
browser/components/search/searchplugins/wikipedia-ka.xml
browser/components/search/searchplugins/wikipedia-kab.xml
browser/components/search/searchplugins/wikipedia-kk.xml
browser/components/search/searchplugins/wikipedia-km.xml
browser/components/search/searchplugins/wikipedia-kn.xml
browser/components/search/searchplugins/wikipedia-kr.xml
browser/components/search/searchplugins/wikipedia-lij.xml
browser/components/search/searchplugins/wikipedia-lo.xml
browser/components/search/searchplugins/wikipedia-lt.xml
browser/components/search/searchplugins/wikipedia-ltg.xml
browser/components/search/searchplugins/wikipedia-lv.xml
browser/components/search/searchplugins/wikipedia-mk.xml
browser/components/search/searchplugins/wikipedia-ml.xml
browser/components/search/searchplugins/wikipedia-mr.xml
browser/components/search/searchplugins/wikipedia-ms.xml
browser/components/search/searchplugins/wikipedia-my.xml
browser/components/search/searchplugins/wikipedia-ne.xml
browser/components/search/searchplugins/wikipedia-nl.xml
browser/components/search/searchplugins/wikipedia-oc.xml
browser/components/search/searchplugins/wikipedia-or.xml
browser/components/search/searchplugins/wikipedia-pa.xml
browser/components/search/searchplugins/wikipedia-pl.xml
browser/components/search/searchplugins/wikipedia-pt.xml
browser/components/search/searchplugins/wikipedia-rm.xml
browser/components/search/searchplugins/wikipedia-ro.xml
browser/components/search/searchplugins/wikipedia-ru.xml
browser/components/search/searchplugins/wikipedia-si.xml
browser/components/search/searchplugins/wikipedia-sk.xml
browser/components/search/searchplugins/wikipedia-sl.xml
browser/components/search/searchplugins/wikipedia-sq.xml
browser/components/search/searchplugins/wikipedia-sr.xml
browser/components/search/searchplugins/wikipedia-sv-SE.xml
browser/components/search/searchplugins/wikipedia-ta.xml
browser/components/search/searchplugins/wikipedia-te.xml
browser/components/search/searchplugins/wikipedia-th.xml
browser/components/search/searchplugins/wikipedia-tl.xml
browser/components/search/searchplugins/wikipedia-tr.xml
browser/components/search/searchplugins/wikipedia-uk.xml
browser/components/search/searchplugins/wikipedia-ur.xml
browser/components/search/searchplugins/wikipedia-uz.xml
browser/components/search/searchplugins/wikipedia-vi.xml
browser/components/search/searchplugins/wikipedia-wo.xml
browser/components/search/searchplugins/wikipedia-zh-CN.xml
browser/components/search/searchplugins/wikipedia-zh-TW.xml
browser/components/search/searchplugins/wikipedia.xml
browser/components/search/searchplugins/wiktionary-oc.xml
browser/components/search/searchplugins/wiktionary-te.xml
browser/components/search/searchplugins/wolnelektury-pl.xml
browser/components/search/searchplugins/yahoo-jp-auctions.xml
browser/components/search/searchplugins/yahoo-jp.xml
browser/components/search/searchplugins/yandex-az.xml
browser/components/search/searchplugins/yandex-by.xml
browser/components/search/searchplugins/yandex-en.xml
browser/components/search/searchplugins/yandex-kk.xml
browser/components/search/searchplugins/yandex-ru.xml
browser/components/search/searchplugins/yandex-tr.xml
browser/components/search/searchplugins/zoznam-sk.xml
mobile/android/components/search/jar.mn
mobile/android/components/search/moz.build
mobile/android/components/search/searchplugins/amazon-au.xml
mobile/android/components/search/searchplugins/amazon-br.xml
mobile/android/components/search/searchplugins/amazon-ca.xml
mobile/android/components/search/searchplugins/amazon-co-uk.xml
mobile/android/components/search/searchplugins/amazon-de.xml
mobile/android/components/search/searchplugins/amazon-fr.xml
mobile/android/components/search/searchplugins/amazon-in.xml
mobile/android/components/search/searchplugins/amazon-it.xml
mobile/android/components/search/searchplugins/amazon-jp.xml
mobile/android/components/search/searchplugins/amazon-mx.xml
mobile/android/components/search/searchplugins/amazon-nl.xml
mobile/android/components/search/searchplugins/amazondotcom.xml
mobile/android/components/search/searchplugins/azerdict.xml
mobile/android/components/search/searchplugins/azet-sk.xml
mobile/android/components/search/searchplugins/baidu.xml
mobile/android/components/search/searchplugins/bing.xml
mobile/android/components/search/searchplugins/bolcom-fy-NL.xml
mobile/android/components/search/searchplugins/bolcom-nl.xml
mobile/android/components/search/searchplugins/ceneje.xml
mobile/android/components/search/searchplugins/coccoc.xml
mobile/android/components/search/searchplugins/danawa-kr.xml
mobile/android/components/search/searchplugins/daum-kr.xml
mobile/android/components/search/searchplugins/ddg.xml
mobile/android/components/search/searchplugins/diec2.xml
mobile/android/components/search/searchplugins/drae.xml
mobile/android/components/search/searchplugins/duckduckgo.xml
mobile/android/components/search/searchplugins/elebila.xml
mobile/android/components/search/searchplugins/faclair-beag.xml
mobile/android/components/search/searchplugins/google-2018.xml
mobile/android/components/search/searchplugins/google.xml
mobile/android/components/search/searchplugins/gulesider-mobile-NO.xml
mobile/android/components/search/searchplugins/heureka-cz.xml
mobile/android/components/search/searchplugins/hotline-ua.xml
mobile/android/components/search/searchplugins/leit-is.xml
mobile/android/components/search/searchplugins/leo_ende_de.xml
mobile/android/components/search/searchplugins/list-am.xml
mobile/android/components/search/searchplugins/list.json
mobile/android/components/search/searchplugins/mapy-cz.xml
mobile/android/components/search/searchplugins/mercadolibre-ar.xml
mobile/android/components/search/searchplugins/mercadolibre-cl.xml
mobile/android/components/search/searchplugins/mercadolibre-mx.xml
mobile/android/components/search/searchplugins/naver-kr.xml
mobile/android/components/search/searchplugins/odpiralni.xml
mobile/android/components/search/searchplugins/pazaruvaj.xml
mobile/android/components/search/searchplugins/pledarigrond.xml
mobile/android/components/search/searchplugins/prisjakt-sv-SE.xml
mobile/android/components/search/searchplugins/qwant.xml
mobile/android/components/search/searchplugins/rediff.xml
mobile/android/components/search/searchplugins/reta-vortaro.xml
mobile/android/components/search/searchplugins/salidzinilv.xml
mobile/android/components/search/searchplugins/seznam-cz.xml
mobile/android/components/search/searchplugins/skroutz.xml
mobile/android/components/search/searchplugins/slovnik-sk.xml
mobile/android/components/search/searchplugins/sslv.xml
mobile/android/components/search/searchplugins/sztaki-en-hu.xml
mobile/android/components/search/searchplugins/taobao.xml
mobile/android/components/search/searchplugins/tearma.xml
mobile/android/components/search/searchplugins/twitter-ja.xml
mobile/android/components/search/searchplugins/twitter.xml
mobile/android/components/search/searchplugins/vatera.xml
mobile/android/components/search/searchplugins/wikipedia-NN.xml
mobile/android/components/search/searchplugins/wikipedia-NO.xml
mobile/android/components/search/searchplugins/wikipedia-an.xml
mobile/android/components/search/searchplugins/wikipedia-ar.xml
mobile/android/components/search/searchplugins/wikipedia-as.xml
mobile/android/components/search/searchplugins/wikipedia-ast.xml
mobile/android/components/search/searchplugins/wikipedia-az.xml
mobile/android/components/search/searchplugins/wikipedia-be.xml
mobile/android/components/search/searchplugins/wikipedia-bg.xml
mobile/android/components/search/searchplugins/wikipedia-bn.xml
mobile/android/components/search/searchplugins/wikipedia-br.xml
mobile/android/components/search/searchplugins/wikipedia-bs.xml
mobile/android/components/search/searchplugins/wikipedia-ca.xml
mobile/android/components/search/searchplugins/wikipedia-cy.xml
mobile/android/components/search/searchplugins/wikipedia-cz.xml
mobile/android/components/search/searchplugins/wikipedia-da.xml
mobile/android/components/search/searchplugins/wikipedia-de.xml
mobile/android/components/search/searchplugins/wikipedia-dsb.xml
mobile/android/components/search/searchplugins/wikipedia-el.xml
mobile/android/components/search/searchplugins/wikipedia-eo.xml
mobile/android/components/search/searchplugins/wikipedia-es.xml
mobile/android/components/search/searchplugins/wikipedia-et.xml
mobile/android/components/search/searchplugins/wikipedia-eu.xml
mobile/android/components/search/searchplugins/wikipedia-fa.xml
mobile/android/components/search/searchplugins/wikipedia-fi.xml
mobile/android/components/search/searchplugins/wikipedia-fr.xml
mobile/android/components/search/searchplugins/wikipedia-fy-NL.xml
mobile/android/components/search/searchplugins/wikipedia-ga-IE.xml
mobile/android/components/search/searchplugins/wikipedia-gd.xml
mobile/android/components/search/searchplugins/wikipedia-gl.xml
mobile/android/components/search/searchplugins/wikipedia-gn.xml
mobile/android/components/search/searchplugins/wikipedia-gu.xml
mobile/android/components/search/searchplugins/wikipedia-he.xml
mobile/android/components/search/searchplugins/wikipedia-hi.xml
mobile/android/components/search/searchplugins/wikipedia-hr.xml
mobile/android/components/search/searchplugins/wikipedia-hsb.xml
mobile/android/components/search/searchplugins/wikipedia-hu.xml
mobile/android/components/search/searchplugins/wikipedia-hy-AM.xml
mobile/android/components/search/searchplugins/wikipedia-ia.xml
mobile/android/components/search/searchplugins/wikipedia-id.xml
mobile/android/components/search/searchplugins/wikipedia-is.xml
mobile/android/components/search/searchplugins/wikipedia-it.xml
mobile/android/components/search/searchplugins/wikipedia-ja.xml
mobile/android/components/search/searchplugins/wikipedia-ka.xml
mobile/android/components/search/searchplugins/wikipedia-kab.xml
mobile/android/components/search/searchplugins/wikipedia-kk.xml
mobile/android/components/search/searchplugins/wikipedia-km.xml
mobile/android/components/search/searchplugins/wikipedia-kn.xml
mobile/android/components/search/searchplugins/wikipedia-lij.xml
mobile/android/components/search/searchplugins/wikipedia-lo.xml
mobile/android/components/search/searchplugins/wikipedia-lt.xml
mobile/android/components/search/searchplugins/wikipedia-ltg.xml
mobile/android/components/search/searchplugins/wikipedia-lv.xml
mobile/android/components/search/searchplugins/wikipedia-ml.xml
mobile/android/components/search/searchplugins/wikipedia-mr.xml
mobile/android/components/search/searchplugins/wikipedia-ms.xml
mobile/android/components/search/searchplugins/wikipedia-my.xml
mobile/android/components/search/searchplugins/wikipedia-ne.xml
mobile/android/components/search/searchplugins/wikipedia-nl.xml
mobile/android/components/search/searchplugins/wikipedia-oc.xml
mobile/android/components/search/searchplugins/wikipedia-or.xml
mobile/android/components/search/searchplugins/wikipedia-pa.xml
mobile/android/components/search/searchplugins/wikipedia-pl.xml
mobile/android/components/search/searchplugins/wikipedia-pt.xml
mobile/android/components/search/searchplugins/wikipedia-rm.xml
mobile/android/components/search/searchplugins/wikipedia-ro.xml
mobile/android/components/search/searchplugins/wikipedia-ru.xml
mobile/android/components/search/searchplugins/wikipedia-sk.xml
mobile/android/components/search/searchplugins/wikipedia-sl.xml
mobile/android/components/search/searchplugins/wikipedia-sq.xml
mobile/android/components/search/searchplugins/wikipedia-sr.xml
mobile/android/components/search/searchplugins/wikipedia-sv-SE.xml
mobile/android/components/search/searchplugins/wikipedia-ta.xml
mobile/android/components/search/searchplugins/wikipedia-te.xml
mobile/android/components/search/searchplugins/wikipedia-th.xml
mobile/android/components/search/searchplugins/wikipedia-tr.xml
mobile/android/components/search/searchplugins/wikipedia-uk.xml
mobile/android/components/search/searchplugins/wikipedia-ur.xml
mobile/android/components/search/searchplugins/wikipedia-uz.xml
mobile/android/components/search/searchplugins/wikipedia-vi.xml
mobile/android/components/search/searchplugins/wikipedia-wo.xml
mobile/android/components/search/searchplugins/wikipedia-zh-CN.xml
mobile/android/components/search/searchplugins/wikipedia-zh-TW.xml
mobile/android/components/search/searchplugins/wikipedia.xml
mobile/android/components/search/searchplugins/wiktionary-kn.xml
mobile/android/components/search/searchplugins/wiktionary-oc.xml
mobile/android/components/search/searchplugins/wiktionary-or.xml
mobile/android/components/search/searchplugins/wiktionary-ta.xml
mobile/android/components/search/searchplugins/wiktionary-te.xml
mobile/android/components/search/searchplugins/yahoo-jp.xml
mobile/android/components/search/searchplugins/yandex-en.xml
mobile/android/components/search/searchplugins/yandex-ru.xml
mobile/android/components/search/searchplugins/yandex-tr.xml
mobile/android/components/search/searchplugins/yandex.by.xml
mobile/android/components/search/searchplugins/yandex.xml
security/manager/ssl/nsNSSErrors.cpp
toolkit/components/search/tests/xpcshell/test_list_json_locale.js
toolkit/themes/osx/global/tree/arrow-disclosure.svg
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1718,8 +1718,12 @@ pref("app.normandy.first_run", true);
 pref("app.normandy.logging.level", 50); // Warn
 pref("app.normandy.run_interval_seconds", 21600); // 6 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
+
+// Savant Shield study preferences
+pref("shield.savant.enabled", false);
+pref("shield.savant.loglevel", "warn");
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -7,16 +7,17 @@
 
 // Slow on asan builds.
 requestLongerTimeout(5);
 
 var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
 
 var gExceptionPaths = [
   "chrome://browser/content/defaultthemes/",
+  "chrome://browser/locale/searchplugins/",
   "resource://app/defaults/settings/blocklists/",
   "resource://app/defaults/settings/main/",
   "resource://app/defaults/settings/pinning/",
   "resource://app/defaults/preferences/",
   "resource://gre/modules/commonjs/",
   "resource://gre/defaults/pref/",
 
   // These resources are referenced using relative paths from html files.
@@ -30,19 +31,16 @@ var gExceptionPaths = [
   "resource://activity-stream/prerendered/",
 
   // browser/extensions/pdfjs/content/build/pdf.js#1999
   "resource://pdf.js/web/images/",
 
   // Exclude all the metadata paths under the country metadata folder because these
   // paths will be concatenated in FormAutofillUtils.jsm based on different country/region.
   "resource://formautofill/addressmetadata/",
-
-  // Exclude all search-plugins because they aren't referenced by filename
-  "resource://search-plugins/",
 ];
 
 // These are not part of the omni.ja file, so we find them only when running
 // the test on a non-packaged build.
 if (AppConstants.platform == "macosx")
   gExceptionPaths.push("resource://gre/res/cursors/");
 
 var whitelist = [
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -112,8 +112,9 @@ browser.jar:
         content/browser/blockedSite.xhtml               (content/blockedSite.xhtml)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml
 
 # L10n resources and overrides.
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
+% resource search-plugins chrome://browser/locale/searchplugins/
--- a/browser/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -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/. */
 
 pref("startup.homepage_override_url", "");
-pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
+pref("startup.homepage_welcome_url", "about:welcome");
 pref("startup.homepage_welcome_url.additional", "");
 // Interval: Time between checks for a new version (in seconds)
 pref("app.update.interval", 43200); // 12 hours
 // The time interval between the downloading of mar file chunks in the
 // background (in seconds)
 // 0 means "download everything at once"
 pref("app.update.download.backgroundInterval", 0);
 // Give the user x seconds to react before showing the big UI. default=192 hours
--- a/browser/components/extensions/parent/ext-browserAction.js
+++ b/browser/components/extensions/parent/ext-browserAction.js
@@ -73,21 +73,17 @@ this.browserAction = class extends Exten
       title: options.default_title || extension.name,
       badgeText: "",
       badgeBackgroundColor: null,
       popup: options.default_popup || "",
       area: browserAreas[options.default_area || "navbar"],
     };
     this.globals = Object.create(this.defaults);
 
-    this.browserStyle = options.browser_style || false;
-    if (options.browser_style === null) {
-      this.extension.logger.warn("Please specify whether you want browser_style " +
-                                 "or not in your browser_action options.");
-    }
+    this.browserStyle = options.browser_style;
 
     browserActionMap.set(extension, this);
 
     this.defaults.icon = await StartupCache.get(
       extension, ["browserAction", "default_icon"],
       () => IconDetails.normalize({
         path: options.default_icon,
         iconType: "browserAction",
--- a/browser/components/extensions/parent/ext-pageAction.js
+++ b/browser/components/extensions/parent/ext-pageAction.js
@@ -65,21 +65,17 @@ this.pageAction = class extends Extensio
     this.defaults = {
       show,
       showMatches,
       hideMatches,
       title: options.default_title || extension.name,
       popup: options.default_popup || "",
     };
 
-    this.browserStyle = options.browser_style || false;
-    if (options.browser_style === null) {
-      this.extension.logger.warn("Please specify whether you want browser_style " +
-                                 "or not in your page_action options.");
-    }
+    this.browserStyle = options.browser_style;
 
     this.tabContext = new TabContext(tab => this.defaults);
 
     this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
 
     pageActionMap.set(extension, this);
 
     this.defaults.icon = await StartupCache.get(
--- a/browser/components/extensions/parent/ext-sidebarAction.js
+++ b/browser/components/extensions/parent/ext-sidebarAction.js
@@ -37,19 +37,17 @@ this.sidebarAction = class extends Exten
 
     // Add the extension to the sidebar menu.  The sidebar widget will copy
     // from that when it is viewed, so we shouldn't need to update that.
     let widgetId = makeWidgetId(extension.id);
     this.id = `${widgetId}-sidebar-action`;
     this.menuId = `menu_${this.id}`;
     this.buttonId = `button_${this.id}`;
 
-    // We default browser_style to true because this is a new API and
-    // we therefore don't need to worry about breaking existing add-ons.
-    this.browserStyle = options.browser_style || options.browser_style === null;
+    this.browserStyle = options.browser_style;
 
     this.defaults = {
       enabled: true,
       title: options.default_title || extension.name,
       icon: IconDetails.normalize({path: options.default_icon}, extension),
       panel: options.default_panel || "",
     };
     this.globals = Object.create(this.defaults);
--- a/browser/components/extensions/schemas/browser_action.json
+++ b/browser/components/extensions/schemas/browser_action.json
@@ -32,17 +32,18 @@
               "default_popup": {
                 "type": "string",
                 "format": "relativeUrl",
                 "optional": true,
                 "preprocess": "localize"
               },
               "browser_style": {
                 "type": "boolean",
-                "optional": true
+                "optional": true,
+                "default": false
               },
               "default_area": {
                 "description": "Defines the location the browserAction will appear by default.  The default location is navbar.",
                 "type": "string",
                 "enum": ["navbar", "menupanel", "tabstrip", "personaltoolbar"],
                 "optional": true
               }
             },
--- a/browser/components/extensions/schemas/page_action.json
+++ b/browser/components/extensions/schemas/page_action.json
@@ -25,17 +25,18 @@
               "default_popup": {
                 "type": "string",
                 "format": "relativeUrl",
                 "optional": true,
                 "preprocess": "localize"
               },
               "browser_style": {
                 "type": "boolean",
-                "optional": true
+                "optional": true,
+                "default": false
               },
               "show_matches": {
                 "type": "array",
                 "optional": true,
                 "minItems": 1,
                 "items": { "$ref": "MatchPattern" }
               },
               "hide_matches": {
--- a/browser/components/extensions/schemas/sidebar_action.json
+++ b/browser/components/extensions/schemas/sidebar_action.json
@@ -19,17 +19,18 @@
                 "preprocess": "localize"
               },
               "default_icon": {
                 "$ref": "IconPath",
                 "optional": true
               },
               "browser_style": {
                 "type": "boolean",
-                "optional": true
+                "optional": true,
+                "default": true
               },
               "default_panel": {
                 "type": "string",
                 "format": "strictRelativeUrl",
                 "preprocess": "localize"
               },
               "open_at_install": {
                 "type": "boolean",
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_browser_style.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_browser_style.js
@@ -1,17 +1,17 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 async function testSidebarBrowserStyle(sidebarAction, assertMessage) {
   function sidebarScript() {
     browser.test.onMessage.addListener((msgName, info, assertMessage) => {
       if (msgName !== "check-style") {
-        browser.test.notifyFail("options-ui-browser_style");
+        browser.test.notifyFail("sidebar-browser-style");
       }
 
       let style = window.getComputedStyle(document.getElementById("button"));
       let buttonBackgroundColor = style.backgroundColor;
       let browserStyleBackgroundColor = "rgb(9, 150, 248)";
       if (!("browser_style" in info) || info.browser_style) {
         browser.test.assertEq(browserStyleBackgroundColor, buttonBackgroundColor, assertMessage);
       } else {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -130,16 +130,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
   RemotePrompt: "resource:///modules/RemotePrompt.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
+  SavantShieldStudy: "resource:///modules/SavantShieldStudy.jsm",
   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
   ShellService: "resource:///modules/ShellService.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   UIState: "resource://services-sync/UIState.jsm",
   UITour: "resource:///modules/UITour.jsm",
   WebChannel: "resource://gre/modules/WebChannel.jsm",
   WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
 });
@@ -1055,16 +1056,18 @@ BrowserGlue.prototype = {
 
     PageActions.init();
 
     this._firstWindowTelemetry(aWindow);
     this._firstWindowLoaded();
 
     // Set the default favicon size for UI views that use the page-icon protocol.
     PlacesUtils.favicons.setDefaultIconURIPreferredSize(16 * aWindow.devicePixelRatio);
+
+    SavantShieldStudy.init();
   },
 
   _sendMediaTelemetry() {
     let win = Services.appShell.hiddenDOMWindow;
     let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
     v.reportCanPlayTelemetry();
   },
 
@@ -1107,16 +1110,18 @@ BrowserGlue.prototype = {
     DateTimePickerHelper.uninit();
 
     // Browser errors are only collected on Nightly
     if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_DATA_REPORTING) {
       this.browserErrorReporter.uninit();
     }
 
     Normandy.uninit();
+
+    SavantShieldStudy.uninit();
   },
 
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     if (this._windowsWereRestored) {
       return;
     }
     this._windowsWereRestored = true;
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -62,17 +62,16 @@
     </content>
 
     <implementation implements="nsIObserver">
       <constructor><![CDATA[
         if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
           return;
 
         Services.obs.addObserver(this, "browser-search-engine-modified");
-        Services.obs.addObserver(this, "browser-search-service");
 
         this._initialized = true;
 
         (window.delayedStartupPromise || Promise.resolve()).then(() => {
           window.requestIdleCallback(() => {
             Services.search.init(aStatus => {
               // Bail out if the binding's been destroyed
               if (!this._initialized)
@@ -110,17 +109,16 @@
       ]]></destructor>
 
       <method name="destroy">
         <body><![CDATA[
         if (this._initialized) {
           this._initialized = false;
 
           Services.obs.removeObserver(this, "browser-search-engine-modified");
-          Services.obs.removeObserver(this, "browser-search-service");
         }
 
         // Make sure to break the cycle from _textbox to us. Otherwise we leak
         // the world. But make sure it's actually pointing to us.
         // Also make sure the textbox has ever been constructed, otherwise the
         // _textbox getter will cause the textbox constructor to run, add an
         // observer, and leak the world too.
         if (this._textboxInitialized && this._textbox.mController.input == this)
@@ -180,18 +178,17 @@
         ]]></body>
       </method>
 
       <method name="observe">
         <parameter name="aEngine"/>
         <parameter name="aTopic"/>
         <parameter name="aVerb"/>
         <body><![CDATA[
-          if (aTopic == "browser-search-engine-modified" ||
-              (aTopic == "browser-search-service" && aVerb == "init-complete")) {
+          if (aTopic == "browser-search-engine-modified") {
             // Make sure the engine list is refetched next time it's needed
             this._engines = null;
 
             // Update the popup header and update the display after any modification.
             this._textbox.popup.updateHeader();
             this.updateDisplay();
           }
         ]]></body>
@@ -1304,17 +1301,16 @@
         menu.addEventListener("popuphidden", aEvent => {
           this._ignoreMouseEvents = false;
           aEvent.stopPropagation();
         });
 
         // Add weak referenced observers to invalidate our cached list of engines.
         Services.prefs.addObserver("browser.search.hiddenOneOffs", this, true);
         Services.obs.addObserver(this, "browser-search-engine-modified", true);
-        Services.obs.addObserver(this, "browser-search-service", true);
 
         // Rebuild the buttons when the theme changes.  See bug 1357800 for
         // details.  Summary: On Linux, switching between themes can cause a row
         // of buttons to disappear.
         Services.obs.addObserver(this, "lightweight-theme-changed", true);
       ]]></constructor>
 
       <!-- This handles events outside the one-off buttons, like on the popup
--- a/browser/components/search/jar.mn
+++ b/browser/components/search/jar.mn
@@ -1,12 +1,8 @@
 # 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/.
 
 browser.jar:
         content/browser/search/search.xml                           (content/search.xml)
         content/browser/search/searchReset.xhtml                    (content/searchReset.xhtml)
         content/browser/search/searchReset.js                       (content/searchReset.js)
-
-        searchplugins/                                              (searchplugins/**)
-
-% resource search-plugins %searchplugins/
--- a/browser/components/search/test/browser.ini
+++ b/browser/components/search/test/browser.ini
@@ -19,18 +19,21 @@ support-files =
 [browser_addEngine.js]
 [browser_amazon.js]
 [browser_bing.js]
 [browser_contextmenu.js]
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" # bug 967013
 [browser_ddg.js]
 [browser_eBay.js]
+skip-if = artifact # bug 1315953
 [browser_google.js]
+skip-if = artifact # bug 1315953
 [browser_google_behavior.js]
+skip-if = artifact # bug 1315953
 [browser_healthreport.js]
 [browser_hiddenOneOffs_cleanup.js]
 [browser_hiddenOneOffs_diacritics.js]
 [browser_oneOffContextMenu.js]
 [browser_oneOffContextMenu_setDefault.js]
 [browser_oneOffHeader.js]
 skip-if = os == "mac" #1421238
 [browser_private_search_perwindowpb.js]
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/debug-lto
@@ -0,0 +1,17 @@
+ac_add_options --enable-debug
+ac_add_options --enable-optimize="-O1"
+
+. $topsrcdir/build/mozconfig.stylo
+
+. $topsrcdir/build/unix/mozconfig.lto
+
+# Enable Telemetry
+export MOZ_TELEMETRY_REPORTING=1
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
+
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=lto
+
+. "$topsrcdir/build/mozconfig.common.override"
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/nightly-lto
@@ -0,0 +1,14 @@
+ac_add_options --disable-debug
+ac_add_options --enable-optimize="-O2"
+
+. $topsrcdir/build/mozconfig.stylo
+
+. $topsrcdir/build/unix/mozconfig.lto
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
+
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=lto
+
+. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -34,39 +34,58 @@ ifneq (,$(filter cocoa,$(MOZ_WIDGET_TOOL
 MOZ_PKG_MAC_DSSTORE=$(ABS_DIST)/branding/dsstore
 MOZ_PKG_MAC_BACKGROUND=$(ABS_DIST)/branding/background.png
 MOZ_PKG_MAC_ICON=$(ABS_DIST)/branding/disk.icns
 MOZ_PKG_MAC_EXTRA=--symlink '/Applications:/ '
 endif
 
 MOZ_SFX_PACKAGE=$(topsrcdir)/other-licenses/7zstub/firefox/7zSD.sfx
 
+SEARCHPLUGINS_FILENAMES := $(or $(shell $(call py_action,output_searchplugins_list,$(srcdir)/search/list.json $(AB_CD))), $(error Missing search plugins))
+SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
+SEARCHPLUGINS_TARGET := libs searchplugins
+SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(srcdir)/searchplugins/$(plugin)),$(error Missing searchplugin: $(plugin))))
+# Some locale-specific search plugins may have preprocessor directives, but the
+# default en-US ones do not.
+SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings
+PP_TARGETS += SEARCHPLUGINS
+
+list-json = $(SEARCHPLUGINS_PATH)/list.json
+GARBAGE += $(list-json)
+
+libs:: searchplugins
+
 # Required for l10n.mk - defines a list of app sub dirs that should
 # be included in langpack xpis.
 DIST_SUBDIRS = $(DIST_SUBDIR)
 
 include $(topsrcdir)/config/rules.mk
 
 include $(topsrcdir)/toolkit/locales/l10n.mk
 
+$(list-json): $(call mkdir_deps,$(SEARCHPLUGINS_PATH)) $(if $(IS_LANGUAGE_REPACK),FORCE)
+	$(call py_action,generate_searchjson,$(srcdir)/search/list.json $(AB_CD) $(list-json))
+searchplugins:: $(list-json)
+
 libs-%: AB_CD=$*
 libs-%:
 	$(if $(filter en-US,$(AB_CD)),, @$(MAKE) merge-$*)
 	$(NSINSTALL) -D $(DIST)/install
 	@$(MAKE) -C ../../toolkit/locales libs-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
 ifneq (,$(wildcard ../extensions/formautofill/locales))
 	@$(MAKE) -C ../extensions/formautofill/locales AB_CD=$* XPI_NAME=locale-$*
 endif
 	@$(MAKE) -C ../extensions/onboarding/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../extensions/pocket/locale AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../extensions/webcompat-reporter/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../devtools/client/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -C ../../devtools/startup/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
+	@$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=$(PREF_DIR)
 	@$(MAKE) multilocale.txt-$* AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$*
 
 chrome-%: AB_CD=$*
 chrome-%: IS_LANGUAGE_REPACK=1
 chrome-%:
 	$(if $(filter en-US,$(AB_CD)),, @$(MAKE) merge-$*)
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -55,16 +55,28 @@
     locale/browser/feeds/subscribe.dtd              (%chrome/browser/feeds/subscribe.dtd)
     locale/browser/feeds/subscribe.properties       (%chrome/browser/feeds/subscribe.properties)
     locale/browser/migration/migration.dtd         (%chrome/browser/migration/migration.dtd)
     locale/browser/migration/migration.properties  (%chrome/browser/migration/migration.properties)
     locale/browser/preferences/preferences.properties     (%chrome/browser/preferences/preferences.properties)
     locale/browser/preferences/security.dtd           (%chrome/browser/preferences/security.dtd)
     locale/browser/syncBrand.dtd                (%chrome/browser/syncBrand.dtd)
     locale/browser/syncSetup.properties         (%chrome/browser/syncSetup.properties)
+#if BUILD_FASTER
+    locale/browser/searchplugins/               (searchplugins/*.xml)
+    locale/browser/searchplugins/list.json      (search/list.json)
+#else
+    locale/browser/searchplugins/               (.deps/generated_@AB_CD@/*.xml)
+    locale/browser/searchplugins/list.json      (.deps/generated_@AB_CD@/list.json)
+#endif
+    locale/browser/searchplugins/images/amazon.ico     (searchplugins/images/amazon.ico)
+    locale/browser/searchplugins/images/ebay.ico       (searchplugins/images/ebay.ico)
+    locale/browser/searchplugins/images/wikipedia.ico  (searchplugins/images/wikipedia.ico)
+    locale/browser/searchplugins/images/yandex-en.ico  (searchplugins/images/yandex-en.ico)
+    locale/browser/searchplugins/images/yandex-ru.ico  (searchplugins/images/yandex-ru.ico)
 % locale browser-region @AB_CD@ %locale/browser-region/
     locale/browser-region/region.properties        (%chrome/browser-region/region.properties)
 # the following files are browser-specific overrides
     locale/browser/netError.dtd                (%chrome/overrides/netError.dtd)
     locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
     locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
 % locale pdf.js @AB_CD@ %locale/pdfviewer/
     locale/pdfviewer/viewer.properties             (%pdfviewer/viewer.properties)
--- a/browser/locales/moz.build
+++ b/browser/locales/moz.build
@@ -39,10 +39,16 @@ with Files("**"):
     BUG_COMPONENT = ("Firefox Build System", "General")
 
 with Files("all-locales"):
     BUG_COMPONENT = ("Core", "Localization")
 
 with Files("en-US/**"):
     BUG_COMPONENT = ("Core", "Localization")
 
+with Files("search/**"):
+    BUG_COMPONENT = ("Firefox", "Search")
+
+with Files("searchplugins/**"):
+    BUG_COMPONENT = ("Firefox", "Search")
+
 with Files("shipped-locales"):
     BUG_COMPONENT = ("Core", "Localization")
rename from browser/components/search/searchplugins/list.json
rename to browser/locales/search/list.json
rename from browser/components/search/searchplugins/allaannonser-sv-SE.xml
rename to browser/locales/searchplugins/allaannonser-sv-SE.xml
rename from browser/components/search/searchplugins/allegro-pl.xml
rename to browser/locales/searchplugins/allegro-pl.xml
rename from browser/components/search/searchplugins/amazon-au.xml
rename to browser/locales/searchplugins/amazon-au.xml
rename from browser/components/search/searchplugins/amazon-br.xml
rename to browser/locales/searchplugins/amazon-br.xml
rename from browser/components/search/searchplugins/amazon-ca.xml
rename to browser/locales/searchplugins/amazon-ca.xml
rename from browser/components/search/searchplugins/amazon-en-GB.xml
rename to browser/locales/searchplugins/amazon-en-GB.xml
rename from browser/components/search/searchplugins/amazon-france.xml
rename to browser/locales/searchplugins/amazon-france.xml
rename from browser/components/search/searchplugins/amazon-in.xml
rename to browser/locales/searchplugins/amazon-in.xml
rename from browser/components/search/searchplugins/amazon-it.xml
rename to browser/locales/searchplugins/amazon-it.xml
rename from browser/components/search/searchplugins/amazon-jp.xml
rename to browser/locales/searchplugins/amazon-jp.xml
rename from browser/components/search/searchplugins/amazon-mx.xml
rename to browser/locales/searchplugins/amazon-mx.xml
rename from browser/components/search/searchplugins/amazon-nl.xml
rename to browser/locales/searchplugins/amazon-nl.xml
rename from browser/components/search/searchplugins/amazondotcn.xml
rename to browser/locales/searchplugins/amazondotcn.xml
rename from browser/components/search/searchplugins/amazondotcom-de.xml
rename to browser/locales/searchplugins/amazondotcom-de.xml
rename from browser/components/search/searchplugins/amazondotcom.xml
rename to browser/locales/searchplugins/amazondotcom.xml
rename from browser/components/search/searchplugins/atlas-sk.xml
rename to browser/locales/searchplugins/atlas-sk.xml
rename from browser/components/search/searchplugins/azerdict.xml
rename to browser/locales/searchplugins/azerdict.xml
rename from browser/components/search/searchplugins/azet-sk.xml
rename to browser/locales/searchplugins/azet-sk.xml
rename from browser/components/search/searchplugins/baidu.xml
rename to browser/locales/searchplugins/baidu.xml
rename from browser/components/search/searchplugins/bbc-alba.xml
rename to browser/locales/searchplugins/bbc-alba.xml
rename from browser/components/search/searchplugins/bing.xml
rename to browser/locales/searchplugins/bing.xml
rename from browser/components/search/searchplugins/bok-NO.xml
rename to browser/locales/searchplugins/bok-NO.xml
rename from browser/components/search/searchplugins/bolcom-fy-NL.xml
rename to browser/locales/searchplugins/bolcom-fy-NL.xml
rename from browser/components/search/searchplugins/bolcom-nl.xml
rename to browser/locales/searchplugins/bolcom-nl.xml
rename from browser/components/search/searchplugins/bookplus-fi.xml
rename to browser/locales/searchplugins/bookplus-fi.xml
rename from browser/components/search/searchplugins/buscape.xml
rename to browser/locales/searchplugins/buscape.xml
rename from browser/components/search/searchplugins/ceneji.xml
rename to browser/locales/searchplugins/ceneji.xml
rename from browser/components/search/searchplugins/chambers-en-GB.xml
rename to browser/locales/searchplugins/chambers-en-GB.xml
rename from browser/components/search/searchplugins/cnrtl-tlfi-fr.xml
rename to browser/locales/searchplugins/cnrtl-tlfi-fr.xml
rename from browser/components/search/searchplugins/coccoc.xml
rename to browser/locales/searchplugins/coccoc.xml
rename from browser/components/search/searchplugins/danawa-kr.xml
rename to browser/locales/searchplugins/danawa-kr.xml
rename from browser/components/search/searchplugins/daum-kr.xml
rename to browser/locales/searchplugins/daum-kr.xml
rename from browser/components/search/searchplugins/ddg.xml
rename to browser/locales/searchplugins/ddg.xml
rename from browser/components/search/searchplugins/diccionariu-alla.xml
rename to browser/locales/searchplugins/diccionariu-alla.xml
rename from browser/components/search/searchplugins/dict-enlv.xml
rename to browser/locales/searchplugins/dict-enlv.xml
rename from browser/components/search/searchplugins/diec2.xml
rename to browser/locales/searchplugins/diec2.xml
rename from browser/components/search/searchplugins/drae.xml
rename to browser/locales/searchplugins/drae.xml
rename from browser/components/search/searchplugins/ebay-at.xml
rename to browser/locales/searchplugins/ebay-at.xml
rename from browser/components/search/searchplugins/ebay-au.xml
rename to browser/locales/searchplugins/ebay-au.xml
rename from browser/components/search/searchplugins/ebay-be.xml
rename to browser/locales/searchplugins/ebay-be.xml
rename from browser/components/search/searchplugins/ebay-ca.xml
rename to browser/locales/searchplugins/ebay-ca.xml
rename from browser/components/search/searchplugins/ebay-ch.xml
rename to browser/locales/searchplugins/ebay-ch.xml
rename from browser/components/search/searchplugins/ebay-de.xml
rename to browser/locales/searchplugins/ebay-de.xml
rename from browser/components/search/searchplugins/ebay-es.xml
rename to browser/locales/searchplugins/ebay-es.xml
rename from browser/components/search/searchplugins/ebay-fr.xml
rename to browser/locales/searchplugins/ebay-fr.xml
rename from browser/components/search/searchplugins/ebay-ie.xml
rename to browser/locales/searchplugins/ebay-ie.xml
rename from browser/components/search/searchplugins/ebay-it.xml
rename to browser/locales/searchplugins/ebay-it.xml
rename from browser/components/search/searchplugins/ebay-nl.xml
rename to browser/locales/searchplugins/ebay-nl.xml
rename from browser/components/search/searchplugins/ebay-uk.xml
rename to browser/locales/searchplugins/ebay-uk.xml
rename from browser/components/search/searchplugins/ebay.xml
rename to browser/locales/searchplugins/ebay.xml
rename from browser/components/search/searchplugins/ecosia.xml
rename to browser/locales/searchplugins/ecosia.xml
rename from browser/components/search/searchplugins/eki-ee.xml
rename to browser/locales/searchplugins/eki-ee.xml
rename from browser/components/search/searchplugins/elebila.xml
rename to browser/locales/searchplugins/elebila.xml
rename from browser/components/search/searchplugins/eudict.xml
rename to browser/locales/searchplugins/eudict.xml
rename from browser/components/search/searchplugins/faclair-beag.xml
rename to browser/locales/searchplugins/faclair-beag.xml
rename from browser/components/search/searchplugins/flip.xml
rename to browser/locales/searchplugins/flip.xml
rename from browser/components/search/searchplugins/freelang.xml
rename to browser/locales/searchplugins/freelang.xml
rename from browser/components/search/searchplugins/google-2018.xml
rename to browser/locales/searchplugins/google-2018.xml
rename from browser/components/search/searchplugins/google.xml
rename to browser/locales/searchplugins/google.xml
rename from browser/components/search/searchplugins/gujaratilexicon.xml
rename to browser/locales/searchplugins/gujaratilexicon.xml
rename from browser/components/search/searchplugins/gulesider-NO.xml
rename to browser/locales/searchplugins/gulesider-NO.xml
rename from browser/components/search/searchplugins/heureka-cz.xml
rename to browser/locales/searchplugins/heureka-cz.xml
rename from browser/components/search/searchplugins/hoepli.xml
rename to browser/locales/searchplugins/hoepli.xml
rename from browser/components/search/searchplugins/hotline-ua.xml
rename to browser/locales/searchplugins/hotline-ua.xml
rename from browser/components/search/searchplugins/images/amazon.ico
rename to browser/locales/searchplugins/images/amazon.ico
rename from browser/components/search/searchplugins/images/ebay.ico
rename to browser/locales/searchplugins/images/ebay.ico
rename from browser/components/search/searchplugins/images/wikipedia.ico
rename to browser/locales/searchplugins/images/wikipedia.ico
rename from browser/components/search/searchplugins/images/yandex-en.ico
rename to browser/locales/searchplugins/images/yandex-en.ico
rename from browser/components/search/searchplugins/images/yandex-ru.ico
rename to browser/locales/searchplugins/images/yandex-ru.ico
rename from browser/components/search/searchplugins/kannadastore.xml
rename to browser/locales/searchplugins/kannadastore.xml
rename from browser/components/search/searchplugins/kaz-kk.xml
rename to browser/locales/searchplugins/kaz-kk.xml
rename from browser/components/search/searchplugins/klask.xml
rename to browser/locales/searchplugins/klask.xml
rename from browser/components/search/searchplugins/leit-is.xml
rename to browser/locales/searchplugins/leit-is.xml
rename from browser/components/search/searchplugins/leo_ende_de-rm.xml
rename to browser/locales/searchplugins/leo_ende_de-rm.xml
rename from browser/components/search/searchplugins/leo_ende_de.xml
rename to browser/locales/searchplugins/leo_ende_de.xml
rename from browser/components/search/searchplugins/list-am.xml
rename to browser/locales/searchplugins/list-am.xml
rename from browser/components/search/searchplugins/longdo.xml
rename to browser/locales/searchplugins/longdo.xml
rename from browser/components/search/searchplugins/mailru.xml
rename to browser/locales/searchplugins/mailru.xml
rename from browser/components/search/searchplugins/mapy-cz.xml
rename to browser/locales/searchplugins/mapy-cz.xml
rename from browser/components/search/searchplugins/marktplaats-fy-NL.xml
rename to browser/locales/searchplugins/marktplaats-fy-NL.xml
rename from browser/components/search/searchplugins/marktplaats-nl.xml
rename to browser/locales/searchplugins/marktplaats-nl.xml
rename from browser/components/search/searchplugins/mercadolibre-ar.xml
rename to browser/locales/searchplugins/mercadolibre-ar.xml
rename from browser/components/search/searchplugins/mercadolibre-cl.xml
rename to browser/locales/searchplugins/mercadolibre-cl.xml
rename from browser/components/search/searchplugins/mercadolibre-mx.xml
rename to browser/locales/searchplugins/mercadolibre-mx.xml
rename from browser/components/search/searchplugins/mercadolivre.xml
rename to browser/locales/searchplugins/mercadolivre.xml
rename from browser/components/search/searchplugins/meta-ua.xml
rename to browser/locales/searchplugins/meta-ua.xml
rename from browser/components/search/searchplugins/morfix-dic.xml
rename to browser/locales/searchplugins/morfix-dic.xml
rename from browser/components/search/searchplugins/najdi-si.xml
rename to browser/locales/searchplugins/najdi-si.xml
rename from browser/components/search/searchplugins/naver-kr.xml
rename to browser/locales/searchplugins/naver-kr.xml
rename from browser/components/search/searchplugins/neti-ee.xml
rename to browser/locales/searchplugins/neti-ee.xml
rename from browser/components/search/searchplugins/odpiralni.xml
rename to browser/locales/searchplugins/odpiralni.xml
rename from browser/components/search/searchplugins/olx.xml
rename to browser/locales/searchplugins/olx.xml
rename from browser/components/search/searchplugins/oshiete-goo.xml
rename to browser/locales/searchplugins/oshiete-goo.xml
rename from browser/components/search/searchplugins/osta-ee.xml
rename to browser/locales/searchplugins/osta-ee.xml
rename from browser/components/search/searchplugins/ozonru.xml
rename to browser/locales/searchplugins/ozonru.xml
rename from browser/components/search/searchplugins/palasprint.xml
rename to browser/locales/searchplugins/palasprint.xml
rename from browser/components/search/searchplugins/paroledigenova-lij.xml
rename to browser/locales/searchplugins/paroledigenova-lij.xml
rename from browser/components/search/searchplugins/pazaruvaj.xml
rename to browser/locales/searchplugins/pazaruvaj.xml
rename from browser/components/search/searchplugins/pledarigrond.xml
rename to browser/locales/searchplugins/pledarigrond.xml
rename from browser/components/search/searchplugins/pogodak.xml
rename to browser/locales/searchplugins/pogodak.xml
rename from browser/components/search/searchplugins/portalbgdict.xml
rename to browser/locales/searchplugins/portalbgdict.xml
rename from browser/components/search/searchplugins/priberam.xml
rename to browser/locales/searchplugins/priberam.xml
rename from browser/components/search/searchplugins/priceru.xml
rename to browser/locales/searchplugins/priceru.xml
rename from browser/components/search/searchplugins/prisjakt-sv-SE.xml
rename to browser/locales/searchplugins/prisjakt-sv-SE.xml
rename from browser/components/search/searchplugins/pwn-pl.xml
rename to browser/locales/searchplugins/pwn-pl.xml
rename from browser/components/search/searchplugins/qwant.xml
rename to browser/locales/searchplugins/qwant.xml
rename from browser/components/search/searchplugins/qxl-NO.xml
rename to browser/locales/searchplugins/qxl-NO.xml
rename from browser/components/search/searchplugins/rakuten.xml
rename to browser/locales/searchplugins/rakuten.xml
rename from browser/components/search/searchplugins/readmoo.xml
rename to browser/locales/searchplugins/readmoo.xml
rename from browser/components/search/searchplugins/rediff.xml
rename to browser/locales/searchplugins/rediff.xml
rename from browser/components/search/searchplugins/reta-vortaro.xml
rename to browser/locales/searchplugins/reta-vortaro.xml
rename from browser/components/search/searchplugins/salidzinilv.xml
rename to browser/locales/searchplugins/salidzinilv.xml
rename from browser/components/search/searchplugins/sapo.xml
rename to browser/locales/searchplugins/sapo.xml
rename from browser/components/search/searchplugins/seznam-cz.xml
rename to browser/locales/searchplugins/seznam-cz.xml
rename from browser/components/search/searchplugins/slovnik-sk.xml
rename to browser/locales/searchplugins/slovnik-sk.xml
rename from browser/components/search/searchplugins/sslv.xml
rename to browser/locales/searchplugins/sslv.xml
rename from browser/components/search/searchplugins/sztaki-en-hu.xml
rename to browser/locales/searchplugins/sztaki-en-hu.xml
rename from browser/components/search/searchplugins/tearma.xml
rename to browser/locales/searchplugins/tearma.xml
rename from browser/components/search/searchplugins/termau.xml
rename to browser/locales/searchplugins/termau.xml
rename from browser/components/search/searchplugins/twitter-ja.xml
rename to browser/locales/searchplugins/twitter-ja.xml
rename from browser/components/search/searchplugins/twitter.xml
rename to browser/locales/searchplugins/twitter.xml
rename from browser/components/search/searchplugins/tyda-sv-SE.xml
rename to browser/locales/searchplugins/tyda-sv-SE.xml
rename from browser/components/search/searchplugins/vatera.xml
rename to browser/locales/searchplugins/vatera.xml
rename from browser/components/search/searchplugins/webdunia.xml
rename to browser/locales/searchplugins/webdunia.xml
rename from browser/components/search/searchplugins/wikipedia-NN.xml
rename to browser/locales/searchplugins/wikipedia-NN.xml
rename from browser/components/search/searchplugins/wikipedia-NO.xml
rename to browser/locales/searchplugins/wikipedia-NO.xml
rename from browser/components/search/searchplugins/wikipedia-af.xml
rename to browser/locales/searchplugins/wikipedia-af.xml
rename from browser/components/search/searchplugins/wikipedia-an.xml
rename to browser/locales/searchplugins/wikipedia-an.xml
rename from browser/components/search/searchplugins/wikipedia-ar.xml
rename to browser/locales/searchplugins/wikipedia-ar.xml
rename from browser/components/search/searchplugins/wikipedia-as.xml
rename to browser/locales/searchplugins/wikipedia-as.xml
rename from browser/components/search/searchplugins/wikipedia-ast.xml
rename to browser/locales/searchplugins/wikipedia-ast.xml
rename from browser/components/search/searchplugins/wikipedia-az.xml
rename to browser/locales/searchplugins/wikipedia-az.xml
rename from browser/components/search/searchplugins/wikipedia-be-tarask.xml
rename to browser/locales/searchplugins/wikipedia-be-tarask.xml
rename from browser/components/search/searchplugins/wikipedia-be.xml
rename to browser/locales/searchplugins/wikipedia-be.xml
rename from browser/components/search/searchplugins/wikipedia-bg.xml
rename to browser/locales/searchplugins/wikipedia-bg.xml
rename from browser/components/search/searchplugins/wikipedia-bn.xml
rename to browser/locales/searchplugins/wikipedia-bn.xml
rename from browser/components/search/searchplugins/wikipedia-br.xml
rename to browser/locales/searchplugins/wikipedia-br.xml
rename from browser/components/search/searchplugins/wikipedia-bs.xml
rename to browser/locales/searchplugins/wikipedia-bs.xml
rename from browser/components/search/searchplugins/wikipedia-ca.xml
rename to browser/locales/searchplugins/wikipedia-ca.xml
rename from browser/components/search/searchplugins/wikipedia-crh.xml
rename to browser/locales/searchplugins/wikipedia-crh.xml
rename from browser/components/search/searchplugins/wikipedia-cy.xml
rename to browser/locales/searchplugins/wikipedia-cy.xml
rename from browser/components/search/searchplugins/wikipedia-cz.xml
rename to browser/locales/searchplugins/wikipedia-cz.xml
rename from browser/components/search/searchplugins/wikipedia-da.xml
rename to browser/locales/searchplugins/wikipedia-da.xml
rename from browser/components/search/searchplugins/wikipedia-de.xml
rename to browser/locales/searchplugins/wikipedia-de.xml
rename from browser/components/search/searchplugins/wikipedia-dsb.xml
rename to browser/locales/searchplugins/wikipedia-dsb.xml
rename from browser/components/search/searchplugins/wikipedia-el.xml
rename to browser/locales/searchplugins/wikipedia-el.xml
rename from browser/components/search/searchplugins/wikipedia-eo.xml
rename to browser/locales/searchplugins/wikipedia-eo.xml
rename from browser/components/search/searchplugins/wikipedia-es.xml
rename to browser/locales/searchplugins/wikipedia-es.xml
rename from browser/components/search/searchplugins/wikipedia-et.xml
rename to browser/locales/searchplugins/wikipedia-et.xml
rename from browser/components/search/searchplugins/wikipedia-eu.xml
rename to browser/locales/searchplugins/wikipedia-eu.xml
rename from browser/components/search/searchplugins/wikipedia-fa.xml
rename to browser/locales/searchplugins/wikipedia-fa.xml
rename from browser/components/search/searchplugins/wikipedia-fi.xml
rename to browser/locales/searchplugins/wikipedia-fi.xml
rename from browser/components/search/searchplugins/wikipedia-fr.xml
rename to browser/locales/searchplugins/wikipedia-fr.xml
rename from browser/components/search/searchplugins/wikipedia-fy-NL.xml
rename to browser/locales/searchplugins/wikipedia-fy-NL.xml
rename from browser/components/search/searchplugins/wikipedia-ga-IE.xml
rename to browser/locales/searchplugins/wikipedia-ga-IE.xml
rename from browser/components/search/searchplugins/wikipedia-gd.xml
rename to browser/locales/searchplugins/wikipedia-gd.xml
rename from browser/components/search/searchplugins/wikipedia-gl.xml
rename to browser/locales/searchplugins/wikipedia-gl.xml
rename from browser/components/search/searchplugins/wikipedia-gn.xml
rename to browser/locales/searchplugins/wikipedia-gn.xml
rename from browser/components/search/searchplugins/wikipedia-gu.xml
rename to browser/locales/searchplugins/wikipedia-gu.xml
rename from browser/components/search/searchplugins/wikipedia-he.xml
rename to browser/locales/searchplugins/wikipedia-he.xml
rename from browser/components/search/searchplugins/wikipedia-hi.xml
rename to browser/locales/searchplugins/wikipedia-hi.xml
rename from browser/components/search/searchplugins/wikipedia-hr.xml
rename to browser/locales/searchplugins/wikipedia-hr.xml
rename from browser/components/search/searchplugins/wikipedia-hsb.xml
rename to browser/locales/searchplugins/wikipedia-hsb.xml
rename from browser/components/search/searchplugins/wikipedia-hu.xml
rename to browser/locales/searchplugins/wikipedia-hu.xml
rename from browser/components/search/searchplugins/wikipedia-hy.xml
rename to browser/locales/searchplugins/wikipedia-hy.xml
rename from browser/components/search/searchplugins/wikipedia-ia.xml
rename to browser/locales/searchplugins/wikipedia-ia.xml
rename from browser/components/search/searchplugins/wikipedia-id.xml
rename to browser/locales/searchplugins/wikipedia-id.xml
rename from browser/components/search/searchplugins/wikipedia-is.xml
rename to browser/locales/searchplugins/wikipedia-is.xml
rename from browser/components/search/searchplugins/wikipedia-it.xml
rename to browser/locales/searchplugins/wikipedia-it.xml
rename from browser/components/search/searchplugins/wikipedia-ja.xml
rename to browser/locales/searchplugins/wikipedia-ja.xml
rename from browser/components/search/searchplugins/wikipedia-ka.xml
rename to browser/locales/searchplugins/wikipedia-ka.xml
rename from browser/components/search/searchplugins/wikipedia-kab.xml
rename to browser/locales/searchplugins/wikipedia-kab.xml
rename from browser/components/search/searchplugins/wikipedia-kk.xml
rename to browser/locales/searchplugins/wikipedia-kk.xml
rename from browser/components/search/searchplugins/wikipedia-km.xml
rename to browser/locales/searchplugins/wikipedia-km.xml
rename from browser/components/search/searchplugins/wikipedia-kn.xml
rename to browser/locales/searchplugins/wikipedia-kn.xml
rename from browser/components/search/searchplugins/wikipedia-kr.xml
rename to browser/locales/searchplugins/wikipedia-kr.xml
rename from browser/components/search/searchplugins/wikipedia-lij.xml
rename to browser/locales/searchplugins/wikipedia-lij.xml
rename from browser/components/search/searchplugins/wikipedia-lo.xml
rename to browser/locales/searchplugins/wikipedia-lo.xml
rename from browser/components/search/searchplugins/wikipedia-lt.xml
rename to browser/locales/searchplugins/wikipedia-lt.xml
rename from browser/components/search/searchplugins/wikipedia-ltg.xml
rename to browser/locales/searchplugins/wikipedia-ltg.xml
rename from browser/components/search/searchplugins/wikipedia-lv.xml
rename to browser/locales/searchplugins/wikipedia-lv.xml
rename from browser/components/search/searchplugins/wikipedia-mk.xml
rename to browser/locales/searchplugins/wikipedia-mk.xml
rename from browser/components/search/searchplugins/wikipedia-ml.xml
rename to browser/locales/searchplugins/wikipedia-ml.xml
rename from browser/components/search/searchplugins/wikipedia-mr.xml
rename to browser/locales/searchplugins/wikipedia-mr.xml
rename from browser/components/search/searchplugins/wikipedia-ms.xml
rename to browser/locales/searchplugins/wikipedia-ms.xml
rename from browser/components/search/searchplugins/wikipedia-my.xml
rename to browser/locales/searchplugins/wikipedia-my.xml
rename from browser/components/search/searchplugins/wikipedia-ne.xml
rename to browser/locales/searchplugins/wikipedia-ne.xml
rename from browser/components/search/searchplugins/wikipedia-nl.xml
rename to browser/locales/searchplugins/wikipedia-nl.xml
rename from browser/components/search/searchplugins/wikipedia-oc.xml
rename to browser/locales/searchplugins/wikipedia-oc.xml
rename from browser/components/search/searchplugins/wikipedia-or.xml
rename to browser/locales/searchplugins/wikipedia-or.xml
rename from browser/components/search/searchplugins/wikipedia-pa.xml
rename to browser/locales/searchplugins/wikipedia-pa.xml
rename from browser/components/search/searchplugins/wikipedia-pl.xml
rename to browser/locales/searchplugins/wikipedia-pl.xml
rename from browser/components/search/searchplugins/wikipedia-pt.xml
rename to browser/locales/searchplugins/wikipedia-pt.xml
rename from browser/components/search/searchplugins/wikipedia-rm.xml
rename to browser/locales/searchplugins/wikipedia-rm.xml
rename from browser/components/search/searchplugins/wikipedia-ro.xml
rename to browser/locales/searchplugins/wikipedia-ro.xml
rename from browser/components/search/searchplugins/wikipedia-ru.xml
rename to browser/locales/searchplugins/wikipedia-ru.xml
rename from browser/components/search/searchplugins/wikipedia-si.xml
rename to browser/locales/searchplugins/wikipedia-si.xml
rename from browser/components/search/searchplugins/wikipedia-sk.xml
rename to browser/locales/searchplugins/wikipedia-sk.xml
rename from browser/components/search/searchplugins/wikipedia-sl.xml
rename to browser/locales/searchplugins/wikipedia-sl.xml
rename from browser/components/search/searchplugins/wikipedia-sq.xml
rename to browser/locales/searchplugins/wikipedia-sq.xml
rename from browser/components/search/searchplugins/wikipedia-sr.xml
rename to browser/locales/searchplugins/wikipedia-sr.xml
rename from browser/components/search/searchplugins/wikipedia-sv-SE.xml
rename to browser/locales/searchplugins/wikipedia-sv-SE.xml
rename from browser/components/search/searchplugins/wikipedia-ta.xml
rename to browser/locales/searchplugins/wikipedia-ta.xml
rename from browser/components/search/searchplugins/wikipedia-te.xml
rename to browser/locales/searchplugins/wikipedia-te.xml
rename from browser/components/search/searchplugins/wikipedia-th.xml
rename to browser/locales/searchplugins/wikipedia-th.xml
rename from browser/components/search/searchplugins/wikipedia-tl.xml
rename to browser/locales/searchplugins/wikipedia-tl.xml
rename from browser/components/search/searchplugins/wikipedia-tr.xml
rename to browser/locales/searchplugins/wikipedia-tr.xml
rename from browser/components/search/searchplugins/wikipedia-uk.xml
rename to browser/locales/searchplugins/wikipedia-uk.xml
rename from browser/components/search/searchplugins/wikipedia-ur.xml
rename to browser/locales/searchplugins/wikipedia-ur.xml
rename from browser/components/search/searchplugins/wikipedia-uz.xml
rename to browser/locales/searchplugins/wikipedia-uz.xml
rename from browser/components/search/searchplugins/wikipedia-vi.xml
rename to browser/locales/searchplugins/wikipedia-vi.xml
rename from browser/components/search/searchplugins/wikipedia-wo.xml
rename to browser/locales/searchplugins/wikipedia-wo.xml
rename from browser/components/search/searchplugins/wikipedia-zh-CN.xml
rename to browser/locales/searchplugins/wikipedia-zh-CN.xml
rename from browser/components/search/searchplugins/wikipedia-zh-TW.xml
rename to browser/locales/searchplugins/wikipedia-zh-TW.xml
rename from browser/components/search/searchplugins/wikipedia.xml
rename to browser/locales/searchplugins/wikipedia.xml
rename from browser/components/search/searchplugins/wiktionary-oc.xml
rename to browser/locales/searchplugins/wiktionary-oc.xml
rename from browser/components/search/searchplugins/wiktionary-te.xml
rename to browser/locales/searchplugins/wiktionary-te.xml
rename from browser/components/search/searchplugins/wolnelektury-pl.xml
rename to browser/locales/searchplugins/wolnelektury-pl.xml
rename from browser/components/search/searchplugins/yahoo-jp-auctions.xml
rename to browser/locales/searchplugins/yahoo-jp-auctions.xml
rename from browser/components/search/searchplugins/yahoo-jp.xml
rename to browser/locales/searchplugins/yahoo-jp.xml
rename from browser/components/search/searchplugins/yandex-az.xml
rename to browser/locales/searchplugins/yandex-az.xml
rename from browser/components/search/searchplugins/yandex-by.xml
rename to browser/locales/searchplugins/yandex-by.xml
rename from browser/components/search/searchplugins/yandex-en.xml
rename to browser/locales/searchplugins/yandex-en.xml
rename from browser/components/search/searchplugins/yandex-kk.xml
rename to browser/locales/searchplugins/yandex-kk.xml
rename from browser/components/search/searchplugins/yandex-ru.xml
rename to browser/locales/searchplugins/yandex-ru.xml
rename from browser/components/search/searchplugins/yandex-tr.xml
rename to browser/locales/searchplugins/yandex-tr.xml
rename from browser/components/search/searchplugins/zoznam-sk.xml
rename to browser/locales/searchplugins/zoznam-sk.xml
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -434,16 +434,19 @@ let BrowserUsageTelemetry = {
   },
 
   _recordSearch(engine, source, action = null) {
     let scalarKey = action ? "search_" + action : "search";
     Services.telemetry.keyedScalarAdd("browser.engagement.navigation." + source,
                                       scalarKey, 1);
     Services.telemetry.recordEvent("navigation", "search", source, action,
                                    { engine: getSearchEngineId(engine) });
+    Services.telemetry.recordEvent("savant", "search", source, action,
+                                   { subcategory: "navigation",
+                                   engine: getSearchEngineId(engine) });
   },
 
   _handleSearchAction(engine, source, details) {
     switch (source) {
       case "urlbar":
       case "oneoff-urlbar":
       case "searchbar":
       case "oneoff-searchbar":
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -102,17 +102,16 @@ var ContentSearch = {
   _destroyedPromise: null,
 
   // The current controller and browser in _onMessageGetSuggestions.  Allows
   // fetch cancellation from _cancelSuggestions.
   _currentSuggestion: null,
 
   init() {
     Services.obs.addObserver(this, "browser-search-engine-modified");
-    Services.obs.addObserver(this, "browser-search-service");
     Services.obs.addObserver(this, "shutdown-leaks-before-check");
     Services.prefs.addObserver("browser.search.hiddenOneOffs", this);
     this._stringBundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
   },
 
   get searchSuggestionUIStrings() {
     if (this._searchSuggestionUIStrings) {
       return this._searchSuggestionUIStrings;
@@ -129,17 +128,16 @@ var ContentSearch = {
   },
 
   destroy() {
     if (this._destroyedPromise) {
       return this._destroyedPromise;
     }
 
     Services.obs.removeObserver(this, "browser-search-engine-modified");
-    Services.obs.removeObserver(this, "browser-search-service");
     Services.obs.removeObserver(this, "shutdown-leaks-before-check");
 
     this._eventQueue.length = 0;
     this._destroyedPromise = Promise.resolve(this._currentEventPromise);
     return this._destroyedPromise;
   },
 
   /**
@@ -181,20 +179,16 @@ var ContentSearch = {
       type: "Message",
       data: msg,
     });
     this._processEventQueue();
   },
 
   observe(subj, topic, data) {
     switch (topic) {
-    case "browser-search-service":
-      if (data != "init-complete") {
-        break;
-      }
     case "nsPref:changed":
     case "browser-search-engine-modified":
       this._eventQueue.push({
         type: "Observe",
         data,
       });
       this._processEventQueue();
       break;
new file mode 100644
--- /dev/null
+++ b/browser/modules/SavantShieldStudy.jsm
@@ -0,0 +1,179 @@
+/* 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 = ["SavantShieldStudy"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// See LOG_LEVELS in Console.jsm. Examples: "all", "info", "warn", & "error".
+const PREF_LOG_LEVEL = "shield.savant.loglevel";
+
+// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
+XPCOMUtils.defineLazyGetter(this, "log", () => {
+  let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
+  let consoleOptions = {
+    maxLogLevelPref: PREF_LOG_LEVEL,
+    prefix: "SavantShieldStudy",
+  };
+  return new ConsoleAPI(consoleOptions);
+});
+
+class SavantShieldStudyClass {
+  constructor() {
+    this.STUDY_PREF = "shield.savant.enabled";
+    this.STUDY_TELEMETRY_CATEGORY = "savant";
+    this.ALWAYS_PRIVATE_BROWSING_PREF = "browser.privatebrowsing.autostart";
+    this.STUDY_DURATION_OVERRIDE_PREF = "shield.savant.duration_override";
+    this.STUDY_EXPIRATION_DATE_PREF = "shield.savant.expiration_date";
+    // ms = 'x' weeks * 7 days/week * 24 hours/day * 60 minutes/hour
+    // * 60 seconds/minute * 1000 milliseconds/second
+    this.DEFAULT_STUDY_DURATION_MS = 4 * 7 * 24 * 60 * 60 * 1000;
+  }
+
+  init() {
+    this.TelemetryEvents = new TelemetryEvents(this.STUDY_TELEMETRY_CATEGORY);
+
+    // check the pref in case Normandy flipped it on before we could add the pref listener
+    this.shouldCollect = Services.prefs.getBoolPref(this.STUDY_PREF);
+    if (this.shouldCollect) {
+      this.startupStudy();
+    }
+    Services.prefs.addObserver(this.STUDY_PREF, this);
+  }
+
+  observe(subject, topic, data) {
+    if (topic === "nsPref:changed" && data === this.STUDY_PREF) {
+      // toggle state of the pref
+      this.shouldCollect = !this.shouldCollect;
+      if (this.shouldCollect) {
+        this.startupStudy();
+      } else {
+        // The pref has been turned off
+        this.endStudy("study_disable");
+      }
+    }
+  }
+
+  startupStudy() {
+    // enable before any possible calls to endStudy, since it sends an 'end_study' event
+    this.TelemetryEvents.enableCollection();
+
+    if (!this.isEligible()) {
+      this.endStudy("ineligible");
+      return;
+    }
+
+    this.initStudyDuration();
+
+    if (this.isStudyExpired()) {
+      log.debug("Study expired in between this and the previous session.");
+      this.endStudy("expired");
+    }
+  }
+
+  isEligible() {
+    const isAlwaysPrivateBrowsing = Services.prefs.getBoolPref(this.ALWAYS_PRIVATE_BROWSING_PREF);
+    if (isAlwaysPrivateBrowsing) {
+      return false;
+    }
+
+    return true;
+  }
+
+  initStudyDuration() {
+    if (Services.prefs.getStringPref(this.STUDY_EXPIRATION_DATE_PREF, "")) {
+      return;
+    }
+    Services.prefs.setStringPref(
+      this.STUDY_EXPIRATION_DATE_PREF,
+      this.getExpirationDateString()
+    );
+  }
+
+  getDurationFromPref() {
+    return Services.prefs.getIntPref(this.STUDY_DURATION_OVERRIDE_PREF, 0);
+  }
+
+  getExpirationDateString() {
+    const now = Date.now();
+    const studyDurationInMs =
+    this.getDurationFromPref()
+      || this.DEFAULT_STUDY_DURATION_MS;
+    const expirationDateInt = now + studyDurationInMs;
+    return new Date(expirationDateInt).toISOString();
+  }
+
+  isStudyExpired() {
+    const expirationDateInt =
+      Date.parse(Services.prefs.getStringPref(
+        this.STUDY_EXPIRATION_DATE_PREF,
+        this.getExpirationDateString()
+      ));
+
+    if (isNaN(expirationDateInt)) {
+      log.error(
+        `The value for the preference ${this.STUDY_EXPIRATION_DATE_PREF} is invalid.`
+      );
+      return false;
+    }
+
+    if (Date.now() > expirationDateInt) {
+      return true;
+    }
+    return false;
+  }
+
+  endStudy(reason) {
+    log.debug(`Ending the study due to reason: ${ reason }`);
+    const isStudyEnding = true;
+    Services.telemetry.recordEvent(this.STUDY_TELEMETRY_CATEGORY, "end_study", reason, null,
+                                  { subcategory: "shield" });
+    this.TelemetryEvents.disableCollection();
+    this.uninit(isStudyEnding);
+    // These prefs needs to persist between restarts, so only reset on endStudy
+    Services.prefs.clearUserPref(this.STUDY_PREF);
+    Services.prefs.clearUserPref(this.STUDY_EXPIRATION_DATE_PREF);
+  }
+
+  // Called on every Firefox shutdown and endStudy
+  uninit(isStudyEnding = false) {
+    // if just shutting down, check for expiration, so the endStudy event can
+    // be sent along with this session's main ping.
+    if (!isStudyEnding && this.isStudyExpired()) {
+      log.debug("Study expired during this session.");
+      this.endStudy("expired");
+      return;
+    }
+
+    Services.prefs.removeObserver(this.ALWAYS_PRIVATE_BROWSING_PREF, this);
+    Services.prefs.removeObserver(this.STUDY_PREF, this);
+    Services.prefs.removeObserver(this.STUDY_DURATION_OVERRIDE_PREF, this);
+    Services.prefs.clearUserPref(PREF_LOG_LEVEL);
+    Services.prefs.clearUserPref(this.STUDY_DURATION_OVERRIDE_PREF);
+  }
+}
+
+// References:
+// - https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/normandy/lib/TelemetryEvents.jsm
+// - https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/normandy/lib/PreferenceExperiments.jsm#l357
+class TelemetryEvents {
+  constructor(studyCategory) {
+    this.STUDY_TELEMETRY_CATEGORY = studyCategory;
+  }
+
+  enableCollection() {
+    log.debug("Study has been enabled; turning ON data collection.");
+    Services.telemetry.setEventRecordingEnabled(this.STUDY_TELEMETRY_CATEGORY, true);
+  }
+
+  disableCollection() {
+    log.debug("Study has been disabled; turning OFF data collection.");
+    Services.telemetry.setEventRecordingEnabled(this.STUDY_TELEMETRY_CATEGORY, false);
+  }
+}
+
+const SavantShieldStudy = new SavantShieldStudyClass();
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -162,16 +162,17 @@ EXTRA_JS_MODULES += [
     'PageStyleHandler.jsm',
     'PermissionUI.jsm',
     'PingCentre.jsm',
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
+    'SavantShieldStudy.jsm',
     'SchedulePressure.jsm',
     'SiteDataManager.jsm',
     'SitePermissions.jsm',
     'TabsPopup.jsm',
     'ThemeVariableMap.jsm',
     'TransientPrefs.jsm',
     'webrtcUI.jsm',
     'ZoomUI.jsm',
--- a/browser/themes/osx/places/organizer.css
+++ b/browser/themes/osx/places/organizer.css
@@ -11,22 +11,22 @@
   height: 24px;
 }
 
 #placesList > treechildren::-moz-tree-cell-text {
   font-size: 12px;
   margin-inline-end: 6px;
 }
 
-#placesList > treechildren::-moz-tree-row(selected) {  
+#placesList > treechildren::-moz-tree-row(selected) {
   -moz-appearance: -moz-mac-source-list-selection;
   -moz-font-smoothing-background-color: -moz-mac-source-list-selection;
 }
 
-#placesList > treechildren::-moz-tree-row(selected,focus) {  
+#placesList > treechildren::-moz-tree-row(selected,focus) {
   -moz-appearance: -moz-mac-active-source-list-selection;
   -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
 }
 
 #placesList > treechildren::-moz-tree-row(History),
 #placesList > treechildren::-moz-tree-row(history)  {
   background-color: blue;
 }
@@ -35,79 +35,56 @@
   cursor: default;
 }
 
 #placesList > treechildren::-moz-tree-separator {
   border-top: 1px solid #505d6d;
   margin: 0 10px;
 }
 
-#placesList > treechildren::-moz-tree-cell-text(selected) {  
+#placesList > treechildren::-moz-tree-twisty(selected),
+#placesList > treechildren::-moz-tree-cell-text(selected) {
   color: #fff;
+  fill-opacity: 1;
   font-weight: bold;
 }
 
 #placesList > treechildren::-moz-tree-twisty {
   -moz-appearance: none;
   padding: 0 2px;
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed");
+  list-style-image: url("chrome://global/skin/tree/arrow-collapsed.svg");
+  -moz-context-properties: fill, fill-opacity;
+  fill: currentColor;
+  fill-opacity: 0.6;
 }
 
-#placesList > treechildren::-moz-tree-twisty(closed, selected) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
+#placesList > treechildren::-moz-tree-twisty(selected) {
+  fill: currentColor;
 }
 
 #placesList > treechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
-}
-
-#placesList > treechildren::-moz-tree-twisty(open, selected) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+  list-style-image: url("chrome://global/skin/tree/arrow-expanded.svg");
 }
 
 #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
-}
-
-#placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed, selected) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+  list-style-image: url("chrome://global/skin/tree/arrow-collapsed-rtl.svg");
 }
 
 @media (-moz-mac-yosemite-theme) {
+  #placesList > treechildren::-moz-tree-twisty(selected),
   #placesList > treechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
+    fill-opacity: 0.6;
     font-weight: 500;
   }
 
+  #placesList > treechildren::-moz-tree-twisty(selected, focus),
   #placesList > treechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
-  }
-
-  #placesList > treechildren::-moz-tree-twisty(closed, selected) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed");
-  }
-
-  #placesList > treechildren::-moz-tree-twisty(closed, selected, focus) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
-  }
-
-  #placesList > treechildren::-moz-tree-twisty(open, selected) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
-  }
-
-  #placesList > treechildren::-moz-tree-twisty(open, selected, focus) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
-  }
-
-  #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed, selected) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
-  }
-
-  #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed, selected, focus) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+    fill-opacity: 1;
   }
 }
 
 #placesToolbar {
   padding: 0 4px 3px;
 }
 
 #placesView {
--- a/browser/themes/osx/places/places.css
+++ b/browser/themes/osx/places/places.css
@@ -46,89 +46,66 @@
   -moz-appearance: -moz-mac-active-source-list-selection;
   -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
 }
 
 .sidebar-placesTreechildren::-moz-tree-cell-text {
   margin-inline-end: 6px;
 }
 
+.sidebar-placesTreechildren::-moz-tree-twisty(selected),
 .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
   color: #fff;
+  fill-opacity: 1;
   font-weight: bold;
 }
 
 #sidebar-search-label {
   display: none;
 }
 
 #sidebar-search-container {
   /* Native searchbar styling already adds 4px margin on Mac, so
    * adding 4px padding results in 8px of total whitespace. */
   padding: 4px;
 }
 
 .sidebar-placesTreechildren::-moz-tree-twisty {
   -moz-appearance: none;
   padding: 0 2px;
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed");
+  list-style-image: url("chrome://global/skin/tree/arrow-collapsed.svg");
+  -moz-context-properties: fill, fill-opacity;
+  fill: currentColor;
+  fill-opacity: 0.6;
 }
 
-.sidebar-placesTreechildren::-moz-tree-twisty(closed, selected) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
+.sidebar-placesTreechildren::-moz-tree-twisty(selected) {
+  fill: currentColor;
 }
 
 .sidebar-placesTreechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
-}
-
-.sidebar-placesTreechildren::-moz-tree-twisty(open, selected) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+  list-style-image: url("chrome://global/skin/tree/arrow-expanded.svg");
 }
 
 .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
-}
-
-.sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed, selected) {
-  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+  list-style-image: url("chrome://global/skin/tree/arrow-collapsed-rtl.svg");
 }
 
 @media (-moz-mac-yosemite-theme) {
+  .sidebar-placesTreechildren::-moz-tree-twisty(selected),
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
+    fill-opacity: 0.6;
     font-weight: 500;
   }
 
+  .sidebar-placesTreechildren::-moz-tree-twisty(selected, focus),
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
-  }
-
-  .sidebar-placesTreechildren::-moz-tree-twisty(closed, selected) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed");
-  }
-
-  .sidebar-placesTreechildren::-moz-tree-twisty(closed, selected, focus) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
-  }
-
-  .sidebar-placesTreechildren::-moz-tree-twisty(open, selected) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
-  }
-
-  .sidebar-placesTreechildren::-moz-tree-twisty(open, selected, focus) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
-  }
-
-  .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed, selected) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
-  }
-
-  .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed, selected, focus) {
-    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl")
+    fill-opacity: 1;
   }
 }
 
 #viewButton {
   -moz-appearance: none;
   border-radius: 4px;
   padding: 2px 4px;
   margin: 4px 0;
--- a/browser/themes/osx/syncedtabs/sidebar.css
+++ b/browser/themes/osx/syncedtabs/sidebar.css
@@ -35,64 +35,40 @@
 .item.selected:focus > .item-title-container {
   -moz-appearance: -moz-mac-active-source-list-selection;
   -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
 }
 
 .item.client .item-twisty-container {
   min-width: 16px;
   height: 16px;
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
+  background-image: url("chrome://global/skin/tree/arrow-expanded.svg");
+  -moz-context-properties: fill, fill-opacity;
+  fill: currentColor;
+  fill-opacity: 1;
+}
+
+.item.client.closed .item-twisty-container {
+  background-image: url("chrome://global/skin/tree/arrow-collapsed.svg");
+}
+
+.item.client.closed .item-twisty-container:dir(rtl) {
+  background-image: url("chrome://global/skin/tree/arrow-collapsed-rtl.svg");
+}
+
+.item.client.selected:focus .item-twisty-container {
+  fill-opacity: 1;
 }
 
 @media (-moz-mac-yosemite-theme: 0) {
   .item.client.selected .item-twisty-container {
-    background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
-  }
-
-  .item.client.selected.closed .item-twisty-container {
-    background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
-  }
-
-  .item.client.selected .item-twisty-container:dir(rtl) {
-    background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
-  }
-
-  .item.client.selected.closed .item-twisty-container:dir(rtl) {
-    background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+    fill-opacity: 1;
   }
 }
 
-.item.client.closed .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed");
-}
-
-.item.client.selected:focus .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
-}
-
-.item.client.selected.closed:focus .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
-}
-
-.item.client .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
-}
-
-.item.client.closed .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
-}
-
-.item.client.selected:focus .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
-}
-
-.item.client.selected.closed:focus .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
-}
 
 @media (-moz-mac-yosemite-theme) {
   .item.selected > .item-title-container {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   .item.selected:focus > .item-title-container {
--- a/build/autoconf/sanitize.m4
+++ b/build/autoconf/sanitize.m4
@@ -1,15 +1,30 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 AC_DEFUN([MOZ_CONFIG_SANITIZE], [
 
 dnl ========================================================
+dnl = Link Time Optimization (LTO)
+dnl ========================================================
+if test -n "$MOZ_LTO"; then
+    MOZ_LLVM_HACKS=1
+    AC_DEFINE(MOZ_LTO)
+    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
+
+    CFLAGS="$CFLAGS $MOZ_LTO_CFLAGS"
+    CPPFLAGS="$CPPFLAGS $MOZ_LTO_CFLAGS"
+    CXXFLAGS="$CXXFLAGS $MOZ_LTO_CFLAGS"
+    LDFLAGS="$LDFLAGS $MOZ_LTO_LDFLAGS"
+fi
+AC_SUBST(MOZ_LTO)
+
+dnl ========================================================
 dnl = Use Address Sanitizer
 dnl ========================================================
 if test -n "$MOZ_ASAN"; then
     MOZ_LLVM_HACKS=1
     if test -n "$CLANG_CL"; then
         # Look for the ASan runtime binary
         if test "$CPU_ARCH" = "x86_64"; then
           MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-x86_64.dll
--- a/build/build-clang/clang-6-pre-linux64.json
+++ b/build/build-clang/clang-6-pre-linux64.json
@@ -1,16 +1,17 @@
 {
     "llvm_revision": "317840",
     "stages": "3",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
     "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
new file mode 100644
--- /dev/null
+++ b/build/debian-packages/python-zstandard-wheezy.diff
@@ -0,0 +1,27 @@
+diff --git a/debian/changelog b/debian/changelog
+index 84028db..d6c86c4 100644
+--- a/debian/changelog
++++ b/debian/changelog
+@@ -1,3 +1,9 @@
++python-zstandard (0.9.1-1.deb7moz1) unstable; urgency=low
++
++  * Remove build dependencies so package builds on Wheezy.
++
++ -- Gregory Szorc <gps@mozilla.com>  Mon, 04 Jun 2018 22:35:00 -0700
++
+ python-zstandard (0.9.1-1) unstable; urgency=low
+
+   * Initial Debian packaging definition.
+diff --git a/debian/control b/debian/control
+index 43bbd46..720082f 100644
+--- a/debian/control
++++ b/debian/control
+@@ -7,8 +7,6 @@ Build-Depends:
+   dh-python,
+   python-all-dev,
+   python3-all-dev,
+-  python-hypothesis,
+-  python3-hypothesis,
+   python-nose,
+   python3-nose,
+   python-setuptools,
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1303,30 +1303,83 @@ include('windows.configure', when=is_win
 # Shader Compiler for Windows (and MinGW Cross Compile)
 # ==============================================================
 
 fxc = check_prog('FXC', ('fxc.exe', 'fxc2.exe'), when=depends(target)
                  (lambda t: t.kernel == 'WINNT'))
 wine = check_prog('WINE', ['wine'], when=depends(target, host)
                   (lambda t, h: t.kernel == 'WINNT' and h.kernel == 'Linux'))
 
-# Security Hardening
+# LTO
+# ==============================================================
+
+js_option('--enable-lto',
+          nargs='?',
+          choices=('full', 'thin'),
+          help='Enable LTO')
+
+
+@depends('--enable-lto', c_compiler)
+@imports('multiprocessing')
+def lto(value, c_compiler):
+    flags = []
+    ldflags = []
+
+    # clang and clang-cl
+    if c_compiler.type in ('clang', 'clang-cl'):
+        # Until Bug 1457168 is fixed, we have to hardcode -fuse-ld=lld here
+        if c_compiler.type == 'clang':
+            flags.append("-fuse-ld=lld")
+
+        if len(value) and value[0].lower() == 'full':
+            flags.append("-flto")
+            ldflags.append("-flto")
+        elif value:
+            flags.append("-flto=thin")
+            ldflags.append("-flto=thin")
+
+    # gcc and other compilers
+    elif value:
+        num_cores = multiprocessing.cpu_count()
+        flags.append("-flto")
+        flags.append("-flifetime-dse=1")
+
+        ldflags.append("-flto=%s" % num_cores)
+        ldflags.append("-flifetime-dse=1")
+
+    return namespace(
+        enabled="1" if value else None,
+        flags=flags,
+        ldflags=ldflags
+    )
+
+
+add_old_configure_assignment('MOZ_LTO', lto.enabled)
+add_old_configure_assignment('MOZ_LTO_CFLAGS', lto.flags)
+add_old_configure_assignment('MOZ_LTO_LDFLAGS', lto.ldflags)
+
+imply_option('--enable-linker', 'lld', when='--enable-lto')
+
+# ASAN
 # ==============================================================
 
 js_option('--enable-address-sanitizer', help='Enable Address Sanitizer')
 
 
 @depends_if('--enable-address-sanitizer')
 def asan(value):
     return True
 
 
 add_old_configure_assignment('MOZ_ASAN', asan)
 
 
+# Security Hardening
+# ==============================================================
+
 option('--enable-hardening', env='MOZ_SECURITY_HARDENING',
        help='Enables security hardening compiler options')
 
 
 @depends('--enable-hardening', '--enable-address-sanitizer',
          '--enable-optimize', c_compiler, target)
 def security_hardening_cflags(hardening_flag, asan, optimize, c_compiler, target):
     compiler_is_gccish = c_compiler.type in ('gcc', 'clang')
--- a/build/unix/elfhack/moz.build
+++ b/build/unix/elfhack/moz.build
@@ -9,19 +9,20 @@ DIRS += ['inject']
 
 if not CONFIG['CROSS_COMPILE']:
     SOURCES += [
         'dummy.c',
         'test-array.c',
         'test-ctors.c',
     ]
 
-    if '-flto' in CONFIG['OS_CFLAGS']:
-        SOURCES['test-array.c'].flags += ['-fno-lto']
-        SOURCES['test-ctors.c'].flags += ['-fno-lto']
+    for f in CONFIG['OS_CFLAGS']:
+        if f.startswith('-flto'):
+            SOURCES['test-array.c'].flags += ['-fno-lto']
+            SOURCES['test-ctors.c'].flags += ['-fno-lto']
 
 HOST_SOURCES += [
     'elf.cpp',
     'elfhack.cpp',
 ]
 
 HostProgram('elfhack')
 
new file mode 100644
--- /dev/null
+++ b/build/unix/mozconfig.lto
@@ -0,0 +1,25 @@
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. "$topsrcdir/build/mozconfig.common"
+
+# Use Clang as specified in manifest
+export AR="$topsrcdir/clang/bin/llvm-ar"
+export NM="$topsrcdir/clang/bin/llvm-nm"
+export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
+
+export CC="$topsrcdir/clang/bin/clang"
+export CXX="$topsrcdir/clang/bin/clang++"
+export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer"
+
+# Use a newer binutils, from the tooltool gcc package, if it's there
+if [ -e "$topsrcdir/gcc/bin/ld" ]; then
+    export CC="$CC -B $topsrcdir/gcc/bin"
+    export CXX="$CXX -B $topsrcdir/gcc/bin"
+fi
+
+# Until Bug 1423822 is resolved
+ac_add_options --disable-elf-hack
+
+ac_add_options --enable-lto
+
+. "$topsrcdir/build/unix/mozconfig.stdcxx"
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -24,16 +24,17 @@ endif
 
 ifeq (.,$(DEPTH))
 
 include root.mk
 
 # Main rules (export, compile, libs and tools) call recurse_* rules.
 # This wrapping is only really useful for build status.
 $(TIERS)::
+	$(if $(filter $@,$(MAKECMDGOALS)),$(call BUILDSTATUS,TIERS $@),)
 	$(call BUILDSTATUS,TIER_START $@)
 	+$(MAKE) recurse_$@
 	$(call BUILDSTATUS,TIER_FINISH $@)
 
 # Special rule that does install-manifests (cf. Makefile.in) + compile
 binaries::
 	+$(MAKE) recurse_compile
 
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -167,17 +167,17 @@ public:
    * Set the time to use for starting or pausing a pending animation.
    *
    * Typically, when an animation is played, it does not start immediately but
    * is added to a table of pending animations on the document of its effect.
    * In the meantime it sets its hold time to the time from which playback
    * should begin.
    *
    * When the document finishes painting, any pending animations in its table
-   * are marked as being ready to start by calling StartOnNextTick.
+   * are marked as being ready to start by calling TriggerOnNextTick.
    * The moment when the paint completed is also recorded, converted to a
    * timeline time, and passed to StartOnTick. This is so that when these
    * animations do start, they can be timed from the point when painting
    * completed.
    *
    * After calling TriggerOnNextTick, animations remain in the pending state
    * until the next refresh driver tick. At that time they transition out of
    * the pending state using the time passed to TriggerOnNextTick as the
@@ -222,18 +222,19 @@ public:
    * and get rid of this method, but there are a lot of them.
    *
    * As with TriggerOnNextTick, the caller of this method is responsible for
    * removing the animation from any PendingAnimationTracker it may have been
    * added to.
    */
   void TriggerNow();
   /**
-   * When StartOnNextTick is called, we store the ready time but we don't apply
-   * it until the next tick. In the meantime, GetStartTime() will return null.
+   * When TriggerOnNextTick is called, we store the ready time but we don't
+   * apply it until the next tick. In the meantime, GetStartTime() will return
+   * null.
    *
    * However, if we build layer animations again before the next tick, we
    * should initialize them with the start time that GetStartTime() will return
    * on the next tick.
    *
    * If we were to simply set the start time of layer animations to null, their
    * start time would be updated to the current wallclock time when rendering
    * finishes, thus making them out of sync with the start time stored here.
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -149,21 +149,26 @@ FlattenedChildIterator::Init(bool aIgnor
 }
 
 bool
 FlattenedChildIterator::ComputeWhetherXBLIsInvolved() const
 {
   MOZ_ASSERT(mXBLInvolved.isNothing());
   // We set mXBLInvolved to true if either the node we're iterating has a
   // binding with content attached to it (in which case it is handled in Init),
-  // or the node is generated XBL content and has an <xbl:children> child.
+  // the node is generated XBL content and has an <xbl:children> child, or the
+  // node is a <slot> element.
   if (!mParent->GetBindingParent()) {
     return false;
   }
 
+  if (mParentAsSlot) {
+    return true;
+  }
+
   for (nsIContent* child = mParent->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
       MOZ_ASSERT(child->GetBindingParent());
       return true;
     }
   }
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -230,18 +230,20 @@ ShadowRoot::RemoveSlot(HTMLSlotElement* 
   nsAutoString name;
   aSlot->GetName(name);
 
   SlotArray* currentSlots = mSlotMap.Get(name);
   MOZ_DIAGNOSTIC_ASSERT(currentSlots && currentSlots->Contains(aSlot),
                         "Slot to deregister wasn't found?");
   if (currentSlots->Length() == 1) {
     MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
+
+    InvalidateStyleAndLayoutOnSubtree(aSlot);
+
     mSlotMap.Remove(name);
-
     if (!aSlot->AssignedNodes().IsEmpty()) {
       aSlot->ClearAssignedNodes();
       aSlot->EnqueueSlotChangeEvent();
     }
 
     return;
   }
 
@@ -249,16 +251,17 @@ ShadowRoot::RemoveSlot(HTMLSlotElement* 
   currentSlots->RemoveElement(aSlot);
 
   // Move assigned nodes from removed slot to the next slot in
   // tree order with the same name.
   if (!wasFirstSlot) {
     return;
   }
 
+  InvalidateStyleAndLayoutOnSubtree(aSlot);
   HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
   const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
   bool slottedNodesChanged = !assignedNodes.IsEmpty();
   while (!assignedNodes.IsEmpty()) {
     nsINode* assignedNode = assignedNodes[0];
 
     aSlot->RemoveAssignedNode(assignedNode);
     replacementSlot->AppendAssignedNode(assignedNode);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1807,17 +1807,16 @@ HTMLMediaElement::AbortExistingLoads()
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
   mMediaInfo = MediaInfo();
   mIsEncrypted = false;
   mPendingEncryptedInitData.Reset();
   mWaitingForKey = NOT_WAITING_FOR_KEY;
   mSourcePointer = nullptr;
-  mAttemptPlayUponLoadedMetadata = false;
 
   mTags = nullptr;
 
   if (mNetworkState != NETWORK_EMPTY) {
     NS_ASSERTION(!mDecoder && !mSrcStream,
                  "How did someone setup a new stream/decoder already?");
     // ChangeNetworkState() will call UpdateAudioChannelPlayingState()
     // indirectly which depends on mPaused. So we need to update mPaused first.
@@ -4039,22 +4038,17 @@ HTMLMediaElement::PlayInternal(ErrorResu
 
   // 4.8.12.8
   // When the play() method on a media element is invoked, the user agent must
   // run the following steps.
 
   // 4.8.12.8 - Step 1:
   // If the media element is not allowed to play, return a promise rejected
   // with a "NotAllowedError" DOMException and abort these steps.
-  // Note: IsAllowedToPlay() needs to know whether there is an audio track
-  // in the resource, and for that we need to be at readyState HAVE_METADATA
-  // or above. So only reject here if we're at readyState HAVE_METADATA. If
-  // we're below that, we'll we delay fulfilling the play promise until we've
-  // reached readyState >= HAVE_METADATA below.
-  if (mReadyState >= HAVE_METADATA && !IsAllowedToPlay()) {
+  if (!IsAllowedToPlay()) {
     // NOTE: for promise-based-play, will return a rejected promise here.
     LOG(LogLevel::Debug,
         ("%p Play() promise rejected because not allowed to play.", this));
     promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
     return promise.forget();
   }
 
   // 4.8.12.8 - Step 2:
@@ -4098,27 +4092,18 @@ HTMLMediaElement::PlayInternal(ErrorResu
 
   // Even if we just did Load() or ResumeLoad(), we could already have a decoder
   // here if we managed to clone an existing decoder.
   if (mDecoder) {
     if (mDecoder->IsEnded()) {
       SetCurrentTime(0);
     }
     if (!mPausedForInactiveDocumentOrChannel) {
-      if (mReadyState < HAVE_METADATA) {
-        // We don't know whether the autoplay policy will allow us to play yet,
-        // as we don't yet know whether the media has audio tracks. So delay
-        // starting playback until we've loaded metadata.
-        mAttemptPlayUponLoadedMetadata = true;
-      } else {
-        mDecoder->Play();
-      }
-    }
-  } else if (mReadyState < HAVE_METADATA) {
-    mAttemptPlayUponLoadedMetadata = true;
+      mDecoder->Play();
+    }
   }
 
   if (mCurrentPlayRangeStart == -1.0) {
     mCurrentPlayRangeStart = CurrentTime();
   }
 
   const bool oldPaused = mPaused;
   mPaused = false;
@@ -4169,18 +4154,17 @@ HTMLMediaElement::PlayInternal(ErrorResu
         DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
         break;
       case HAVE_FUTURE_DATA:
       case HAVE_ENOUGH_DATA:
         FireTimeUpdate(false);
         NotifyAboutPlaying();
         break;
     }
-  } else if (mReadyState >= HAVE_FUTURE_DATA &&
-             !mAttemptPlayUponLoadedMetadata) {
+  } else if (mReadyState >= HAVE_FUTURE_DATA) {
     // 7. Otherwise, if the media element's readyState attribute has the value
     //    HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, take pending play promises and
     //    queue a task to resolve pending play promises with the result.
     AsyncResolvePendingPlayPromises();
   }
 
   // 8. Set the media element's autoplaying flag to false. (Already done.)
 
@@ -5002,17 +4986,17 @@ HTMLMediaElement::FinishDecoderSetup(Med
   // We may want to suspend the new stream now.
   // This will also do an AddRemoveSelfReference.
   NotifyOwnerDocumentActivityChanged();
 
   if (mPausedForInactiveDocumentOrChannel) {
     mDecoder->Suspend();
   }
 
-  if (!mPaused && !mAttemptPlayUponLoadedMetadata) {
+  if (!mPaused) {
     SetPlayedOrSeeked(true);
     if (!mPausedForInactiveDocumentOrChannel) {
       mDecoder->Play();
     }
   }
 
   return NS_OK;
 }
@@ -6049,26 +6033,16 @@ HTMLMediaElement::ChangeReadyState(nsMed
   DDLOG(DDLogCategory::Property, "ready_state", gReadyStateToString[aState]);
 
   if (mNetworkState == NETWORK_EMPTY) {
     return;
   }
 
   UpdateAudioChannelPlayingState();
 
-  if (oldState < HAVE_METADATA && mReadyState >= HAVE_METADATA &&
-      mAttemptPlayUponLoadedMetadata) {
-    mAttemptPlayUponLoadedMetadata = false;
-    if (!mPaused && !IsAllowedToPlay()) {
-      mPaused = true;
-      DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
-      AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
-    }
-  }
-
   // Handle raising of "waiting" event during seek (see 4.8.10.9)
   // or
   // 4.8.12.7 Ready states:
   // "If the previous ready state was HAVE_FUTURE_DATA or more, and the new
   // ready state is HAVE_CURRENT_DATA or less
   // If the media element was potentially playing before its readyState
   // attribute changed to a value lower than HAVE_FUTURE_DATA, and the element
   // has not ended playback, and playback has not stopped due to errors,
@@ -6213,17 +6187,16 @@ HTMLMediaElement::CheckAutoplayDataReady
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
   if (mDecoder) {
     SetPlayedOrSeeked(true);
     if (mCurrentPlayRangeStart == -1.0) {
       mCurrentPlayRangeStart = CurrentTime();
     }
-    MOZ_ASSERT(!mAttemptPlayUponLoadedMetadata);
     mDecoder->Play();
   } else if (mSrcStream) {
     SetPlayedOrSeeked(true);
   }
 
   // For blocked media, the event would be pending until it is resumed.
   DispatchAsyncEvent(NS_LITERAL_STRING("play"));
 
@@ -6577,17 +6550,16 @@ HTMLMediaElement::SuspendOrResumeElement
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
-          MOZ_ASSERT(!mAttemptPlayUponLoadedMetadata);
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
         DispatchPendingMediaEvents();
       }
     }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1821,23 +1821,16 @@ private:
   // True if media element has been marked as 'tainted' and can't
   // participate in video decoder suspending.
   bool mHasSuspendTaint = false;
 
   // True if media element has been forced into being considered 'hidden'.
   // For use by mochitests. Enabling pref "media.test.video-suspend"
   bool mForcedHidden = false;
 
-  // True if we attempted to play before the media element had loaded
-  // metadata, and we need to attempt the play once we reach loaded metadata.
-  // If autoplay is disabled, we can't decide whether to allow a play()
-  // until we've loaded metadata, as we need to know whether the resource
-  // has an audio track.
-  bool mAttemptPlayUponLoadedMetadata = false;
-
   // True if audio tracks and video tracks are constructed and added into the
   // track list, false if all tracks are removed from the track list.
   bool mMediaTracksConstructed = false;
 
   Visibility mVisibilityState = Visibility::UNTRACKED;
 
   UniquePtr<ErrorSink> mErrorSink;
 
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -20,46 +20,39 @@ namespace dom {
 
 /* static */ bool
 AutoplayPolicy::IsMediaElementAllowedToPlay(NotNull<HTMLMediaElement*> aElement)
 {
   if (Preferences::GetBool("media.autoplay.enabled")) {
     return true;
   }
 
+  // TODO : this old way would be removed when user-gestures-needed becomes
+  // as a default option to block autoplay.
+  if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
+    // If elelement is blessed, it would always be allowed to play().
+    return aElement->IsBlessed() ||
+           EventStateManager::IsHandlingUserInput();
+  }
+
   // Pages which have been granted permission to capture WebRTC camera or
   // microphone are assumed to be trusted, and are allowed to autoplay.
   MediaManager* manager = MediaManager::GetIfExists();
   if (manager) {
     nsCOMPtr<nsPIDOMWindowInner> window = aElement->OwnerDoc()->GetInnerWindow();
     if (window && manager->IsActivelyCapturingOrHasAPermission(window->WindowID())) {
       return true;
     }
   }
 
-  // TODO : this old way would be removed when user-gestures-needed becomes
-  // as a default option to block autoplay.
-  if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
-    // If elelement is blessed, it would always be allowed to play().
-    return aElement->IsBlessed() ||
-           EventStateManager::IsHandlingUserInput();
-  }
-
   // Muted content
   if (aElement->Volume() == 0.0 || aElement->Muted()) {
     return true;
   }
 
-  // Media has already loaded metadata and doesn't contain audio track
-  if (aElement->IsVideo() &&
-      aElement->ReadyState() >= HTMLMediaElementBinding::HAVE_METADATA &&
-      !aElement->HasAudio()) {
-    return true;
-  }
-
   // Whitelisted.
   if (nsContentUtils::IsExactSitePermAllow(
         aElement->NodePrincipal(), "autoplay-media")) {
     return true;
   }
 
   // Activated by user gesture.
   if (aElement->OwnerDoc()->HasBeenUserActivated()) {
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -459,17 +459,18 @@ MediaSourceTrackDemuxer::DoSeek(const Ti
     seekTime = buffered[index].mStart;
   }
   seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ);
   MediaResult result = NS_OK;
   RefPtr<MediaRawData> sample =
     mManager->GetSample(mType,
                         TimeUnit::Zero(),
                         result);
-  MOZ_ASSERT(NS_SUCCEEDED(result) && sample);
+  mManager->AssertHasNextSampleIndex(mType);
+  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(result) && sample);
   mNextSample = Some(sample);
   mReset = false;
   {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint =
       mManager->GetNextRandomAccessPoint(mType, MediaSourceDemuxer::EOS_FUZZ);
   }
   return SeekPromise::CreateAndResolve(seekTime, __func__);
@@ -500,29 +501,31 @@ MediaSourceTrackDemuxer::DoGetSamples(in
         NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
     }
     mReset = false;
   }
   RefPtr<MediaRawData> sample;
   if (mNextSample) {
     sample = mNextSample.ref();
     mNextSample.reset();
+    mManager->AssertHasNextSampleIndex(mType);
   } else {
     MediaResult result = NS_OK;
     sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, result);
     if (!sample) {
       if (result == NS_ERROR_DOM_MEDIA_END_OF_STREAM ||
           result == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
         return SamplesPromise::CreateAndReject(
           (result == NS_ERROR_DOM_MEDIA_END_OF_STREAM && mManager->IsEnded())
           ? NS_ERROR_DOM_MEDIA_END_OF_STREAM
           : NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
       }
       return SamplesPromise::CreateAndReject(result, __func__);
     }
+    mManager->AssertHasNextSampleIndex(mType);
   }
   RefPtr<SamplesHolder> samples = new SamplesHolder;
   samples->mSamples.AppendElement(sample);
   if (mNextRandomAccessPoint <= sample->mTime) {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint =
       mManager->GetNextRandomAccessPoint(mType, MediaSourceDemuxer::EOS_FUZZ);
   }
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -2635,17 +2635,17 @@ TrackBuffersManager::Evictable(TrackInfo
 }
 
 TimeUnit
 TrackBuffersManager::GetNextRandomAccessPoint(TrackInfo::TrackType aTrack,
                                               const TimeUnit& aFuzz)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& trackData = GetTracksData(aTrack);
-  MOZ_ASSERT(trackData.mNextGetSampleIndex.isSome());
+  MOZ_DIAGNOSTIC_ASSERT(trackData.mNextGetSampleIndex.isSome());
   const TrackBuffersManager::TrackBuffer& track = GetTrackBuffer(aTrack);
 
   uint32_t i = trackData.mNextGetSampleIndex.ref();
   TimeUnit nextSampleTimecode = trackData.mNextSampleTimecode;
   TimeUnit nextSampleTime = trackData.mNextSampleTime;
 
   for (; i < track.Length(); i++) {
     const MediaRawData* sample =
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -159,16 +159,22 @@ public:
                                            const media::TimeUnit& aFuzz,
                                            MediaResult& aResult);
   int32_t FindCurrentPosition(TrackInfo::TrackType aTrack,
                               const media::TimeUnit& aFuzz) const;
   media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack,
                                            const media::TimeUnit& aFuzz);
 
   void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes) const;
+  void AssertHasNextSampleIndex(TrackInfo::TrackType aTrack) const
+  {
+    const auto& trackData = GetTracksData(aTrack);
+    MOZ_DIAGNOSTIC_ASSERT(trackData.mNextGetSampleIndex.isSome(),
+                          "next sample index must be set");
+  }
 
 private:
   typedef MozPromise<bool, MediaResult, /* IsExclusive = */ true> CodedFrameProcessingPromise;
 
   // for MediaSourceDemuxer::GetMozDebugReaderData
   friend class MediaSourceDemuxer;
   ~TrackBuffersManager();
   // All following functions run on the taskqueue.
--- a/dom/media/test/file_autoplay_policy_play_before_loadedmetadata.html
+++ b/dom/media/test/file_autoplay_policy_play_before_loadedmetadata.html
@@ -22,43 +22,36 @@
         window.is = window.opener.is;
         window.info = window.opener.info;
 
         async function testPlayBeforeLoadedMetata(testCase, parent_window) {
           info("testPlayBeforeLoadedMetata: " + testCase.resource);
 
           let element = document.createElement("video");
           element.preload = "auto";
+          element.muted = testCase.muted;
           element.src = testCase.resource;
           document.body.appendChild(element);
 
           is(element.paused, true, testCase.resource + " - should start out paused.");
 
           let playEventFired = false;
           once(element, "play").then(() => { playEventFired = true; });
           let playingEventFired = false;
           once(element, "playing").then(() => { playingEventFired = true;});
           let pauseEventFired = false;
           once(element, "pause").then(() => { pauseEventFired = true; });
 
           let played = await element.play().then(() => true, () => false);
 
-          // Wait for one round through the event loop. This gives any tasks
-          // inside Gecko enqueued to dispatch events a chance to run.
-          // Specifically the "playing" event, if it's erronously fired.
-          await new Promise((resolve, reject) => {
-            setTimeout(resolve, 0);
-          });
-
-          let playingEventMsg = testCase.resource + " should " + (!testCase.shouldPlay ? "not " : "") + " fire playing";
-          is(playingEventFired, testCase.shouldPlay, playingEventMsg);
           let playMsg = testCase.resource + " should " + (!testCase.shouldPlay ? "not " : "") + "play";
           is(played, testCase.shouldPlay, playMsg);
-          is(playEventFired, true, testCase.resource + " - we should always get a play event");
-          is(pauseEventFired, !testCase.shouldPlay, testCase.resource + " - if we shouldn't play, we should get a pause event");
+          is(playEventFired, testCase.shouldPlay, testCase.resource + " - should get play event if we played");
+          is(playingEventFired, testCase.shouldPlay, testCase.resource + "- should get playing event if we played");
+          is(pauseEventFired, false, testCase.resource + " - should not get pause event if we played");
           removeNodeAndSource(element);
         }
 
         nextWindowMessage().then(
           async (event) => {
             await testPlayBeforeLoadedMetata(event.data, event.source);
             event.source.postMessage("done", "*");
           });
--- a/dom/media/test/test_autoplay_policy.html
+++ b/dom/media/test/test_autoplay_policy.html
@@ -29,72 +29,63 @@ window.ok = function(val, msg, token) {
   SimpleTest.ok(val, msg + ", token=" + token);
 }
 
 /**
  * test files and paremeters
  */
 var autoplayTests = [
   /* video */
-  { name:"gizmo.mp4", type:"video/mp4", hasAudio:true },
-  { name:"gizmo-noaudio.mp4", type:"video/mp4", hasAudio:false },
-  { name:"gizmo.webm", type:'video/webm', hasAudio:true },
-  { name:"gizmo-noaudio.webm", type:'video/webm', hasAudio:false },
+  { name: "gizmo.mp4", type: "video/mp4" },
+  { name: "gizmo-noaudio.mp4", type: "video/mp4" },
+  { name: "gizmo.webm", type: "video/webm" },
+  { name: "gizmo-noaudio.webm", type: "video/webm" },
   /* audio */
-  { name:"small-shot.ogg", type:"audio/ogg", hasAudio:true },
-  { name:"small-shot.m4a", type:"audio/mp4", hasAudio:true },
-  { name:"small-shot.mp3", type:"audio/mpeg", hasAudio:true },
-  { name:"small-shot.flac", type:"audio/flac", hasAudio:true }
+  { name: "small-shot.ogg", type: "audio/ogg" },
+  { name: "small-shot.m4a", type: "audio/mp4" },
+  { name: "small-shot.mp3", type: "audio/mpeg" },
+  { name: "small-shot.flac", type: "audio/flac" }
 ];
 
 var autoplayParams = [
-  { volume: 1.0, muted: false, preload: "none" },
-  { volume: 1.0, muted: false, preload: "metadata" },
-  { volume: 0.0, muted: false, preload: "none" },
-  { volume: 0.0, muted: false, preload: "metadata" },
-  { volume: 1.0, muted: true,  preload: "none"},
-  { volume: 1.0, muted: true,  preload: "metadata" },
-  { volume: 0.0, muted: true,  preload: "none"},
-  { volume: 0.0, muted: true,  preload: "metadata" }
+  { volume: 1.0, muted: false },
+  { volume: 0.0, muted: false },
+  { volume: 1.0, muted: true },
+  { volume: 0.0, muted: true },
 ];
 
 function createTestArray()
 {
   var tests = [];
-  for (let testIdx = 0; testIdx < autoplayTests.length; testIdx++) {
-    for (let paramIdx = 0; paramIdx < autoplayParams.length; paramIdx++) {
-      let test = autoplayTests[testIdx];
-      let param = autoplayParams[paramIdx];
-      let obj = new Object;
-      obj.name = test.name;
-      obj.type = test.type;
-      obj.hasAudio = test.hasAudio;
-      obj.volume = param.volume;
-      obj.muted = param.muted;
-      obj.preload = param.preload;
-      tests.push(obj);
+  for (let test of autoplayTests) {
+    for (let param of autoplayParams) {
+      tests.push({
+        name: test.name,
+        type: test.type,
+        volume: param.volume,
+        muted: param.muted,
+      });
     }
   }
   return tests;
 }
 
 /**
  * Main test function for different autoplay cases without user interaction.
  *
  * When the pref "media.autoplay.enabled" is false and the pref
- * "media.autoplay.enabled.user-gestures-needed" is true, we would only allow
+ * "media.autoplay.enabled.user-gestures-needed" is true, we only allow
  * audible media to autoplay after the website has been activated by specific
- * user gestures. However, non-audible media won't be restricted.
+ * user gestures. However, inaudible media won't be restricted.
  *
- * Audible means the volume is not zero, or muted is not true for the video with
- * audio track. For media without loading metadata, we can't know whether it has
- * audio track or not, so we would also regard it as audible media.
+ * Audible means the volume is non zero and muted is false.
  *
- * Non-audible means the volume is zero, the muted is true, or the video without
- * audio track.
+ * Inaudible means the volume is zero, or the muted is true.
+ *
+ * We do not take into account whether a media has an audio track.
  */
 async function runTest(test, token) {
   manager.started(token);
 
   await testPlay(test, token);
   await testAutoplayKeyword(test, token);
 
   manager.finished(token);
@@ -102,34 +93,25 @@ async function runTest(test, token) {
 
 manager.runTests(createTestArray(), runTest);
 
 /**
  * Different test scenarios
  */
 async function testPlay(test, token) {
   info("### start testPlay", token);
-  info(`volume=${test.volume}, muted=${test.muted}, ` +
-       `preload=${test.preload}, hasAudio=${test.hasAudio}`, token);
+  info(`volume=${test.volume}, muted=${test.muted}`, token);
 
   let element = document.createElement(getMajorMimeType(test.type));
-  element.preload = test.preload;
   element.volume = test.volume;
   element.muted = test.muted;
   element.src = test.name;
   document.body.appendChild(element);
 
-  let preLoadNone = test.preload == "none";
-  if (!preLoadNone) {
-    info("### wait for loading metadata", token);
-    await once(element, "loadedmetadata");
-  }
-
-  let isAudible = (preLoadNone || test.hasAudio) &&
-                  test.volume != 0.0 &&
+  let isAudible = test.volume != 0.0 &&
                   !test.muted;
   let state = isAudible? "audible" : "non-audible";
   info(`### calling play() for ${state} media`, token);
   let promise = element.play();
   if (isAudible) {
     await promise.catch(function(error) {
       ok(element.paused, `${state} media fail to start via play()`, token);
       is(error.name, "NotAllowedError", "rejected play promise", token);
@@ -141,40 +123,31 @@ async function testPlay(test, token) {
     ok(!element.paused, `${state} media start via play()`, token);
   }
 
   removeNodeAndSource(element);
 }
 
 async function testAutoplayKeyword(test, token) {
   info("### start testAutoplayKeyword", token);
-  info(`volume=${test.volume}, muted=${test.muted}, ` +
-       `preload=${test.preload}, hasAudio=${test.hasAudio}`, token);
+  info(`volume=${test.volume}, muted=${test.muted}`, token);
 
   let element = document.createElement(getMajorMimeType(test.type));
   element.autoplay = true;
-  element.preload = test.preload;
   element.volume = test.volume;
   element.muted = test.muted;
   element.src = test.name;
   document.body.appendChild(element);
 
-  let preLoadNone = test.preload == "none";
-  let isAudible = (preLoadNone || test.hasAudio) &&
-                  test.volume != 0.0 &&
+  let isAudible = test.volume != 0.0 &&
                   !test.muted;
   let state = isAudible? "audible" : "non-audible";
   info(`### wait to autoplay for ${state} media`, token);
   if (isAudible) {
-    if (preLoadNone) {
-      await once(element, "suspend");
-      is(element.readyState, 0 /* HAVE_NOTHING */, "media won't start loading", token);
-    } else {
-      await once(element, "canplay");
-    }
+    await once(element, "canplay");
     ok(element.paused, `can not start with 'autoplay' keyword for ${state} media`, token);
   } else {
     await once(element, "play");
     ok(!element.paused, `start with 'autoplay' keyword for ${state} media`, token);
   }
 
   removeNodeAndSource(element);
 }
--- a/dom/media/test/test_autoplay_policy_permission.html
+++ b/dom/media/test/test_autoplay_policy_permission.html
@@ -17,54 +17,53 @@
 
         gTestPrefs.push(["media.autoplay.enabled", false],
           ["media.autoplay.enabled.user-gestures-needed", true]);
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
 
-        async function canPlayIn(testCase) {
+        async function testPlayInOrigin(testCase) {
           // Run test in a new window, to ensure its user gesture
           // activation state isn't tainted by preceeding tests.
           let child = window.open("file_autoplay_policy_permission.html", "", "width=500,height=500");
           await once(child, "load");
           child.postMessage(testCase, "*");
-          let result = await nextWindowMessage();
+          await nextWindowMessage();
           child.close();
-          return result.played == true;
         }
 
         async function runTest() {
           // First verify that we can't play in a document unwhitelisted.
           is(window.origin, "http://mochi.test:8888", "Origin should be as we assume, otherwise the rest of the test is bogus!");
 
-          await canPlayIn({
+          await testPlayInOrigin({
             origin: "http://mochi.test:8888",
             shouldPlay: false,
             message: "Should not be able to play unwhitelisted."
           });
 
           // Add our origin to the whitelist.
           await pushAutoplayAllowedPermission();
 
           // Now we should be able to play...
-          await canPlayIn({
+          await testPlayInOrigin({
             origin: "http://mochi.test:8888",
             shouldPlay: true,
             message: "Should be able to play since whitelisted."
           });
 
           // But sub-origins should not.
-          await canPlayIn({
+          await testPlayInOrigin({
             origin: "http://test1.mochi.test:8888",
             shouldPlay: false,
             message: "Sub origin should not count as whitelisted."
           });
-          await canPlayIn({
+          await testPlayInOrigin({
             origin: "http://sub1.test1.mochi.test:8888",
             shouldPlay: false,
             message: "Sub-sub-origins should not count as whitelisted."
           });
 
           SimpleTest.finish();
         }
 
--- a/dom/media/test/test_autoplay_policy_play_before_loadedmetadata.html
+++ b/dom/media/test/test_autoplay_policy_play_before_loadedmetadata.html
@@ -24,21 +24,33 @@
 
         SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
           runTest();
         });
 
         let testCases = [
           {
             resource: "320x240.ogv", // Only video track.
+            shouldPlay: false,
+            muted: false,
+          },
+          {
+            resource: "320x240.ogv", // Only video track.
             shouldPlay: true,
+            muted: true,
           },
           {
             resource: "short.mp4", // Audio and video track.
             shouldPlay: false,
+            muted: false,
+          },
+          {
+            resource: "short.mp4", // Audio and video track.
+            shouldPlay: true,
+            muted: true,
           },
         ];
 
         let child_url = "file_autoplay_policy_play_before_loadedmetadata.html";
 
         async function runTest() {
           for (testCase of testCases) {
             // Run each test in a new window, to ensure its user gesture
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -76,16 +76,17 @@ public:
    */
   LayerToParentLayerMatrix4x4 ComputeTransformForScrollThumb(
       const LayerToParentLayerMatrix4x4& aCurrentTransform,
       const LayerMetricsWrapper& aContent,
       const ScrollbarData& aThumbData,
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
+  CSSRect GetCurrentAsyncLayoutViewport(const LayerMetricsWrapper& aLayer);
   ParentLayerPoint GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer);
   AsyncTransform GetCurrentAsyncTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetOverscrollTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(const LayerMetricsWrapper& aLayer);
 
   void MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer);
   bool HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer);
 
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -145,16 +145,26 @@ APZSampler::ComputeTransformForScrollThu
                                               aContent.GetTransform(),
                                               aContent.GetApzc(),
                                               aContent.Metrics(),
                                               aThumbData,
                                               aScrollbarIsDescendant,
                                               aOutClipTransform);
 }
 
+CSSRect
+APZSampler::GetCurrentAsyncLayoutViewport(const LayerMetricsWrapper& aLayer)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  AssertOnSamplerThread();
+
+  MOZ_ASSERT(aLayer.GetApzc());
+  return aLayer.GetApzc()->GetCurrentAsyncLayoutViewport(AsyncPanZoomController::eForCompositing);
+}
+
 ParentLayerPoint
 APZSampler::GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
 
   MOZ_ASSERT(aLayer.GetApzc());
   return aLayer.GetApzc()->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing);
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3726,16 +3726,27 @@ bool AsyncPanZoomController::AdvanceAnim
     APZThreadUtils::RunOnControllerThread(deferredTasks[i].forget());
   }
 
   // If any of the deferred tasks starts a new animation, it will request a
   // new composite directly, so we can just return requestAnimationFrame here.
   return requestAnimationFrame;
 }
 
+CSSRect
+AsyncPanZoomController::GetCurrentAsyncLayoutViewport(AsyncTransformConsumer aMode) const {
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
+  MOZ_ASSERT(mFrameMetrics.IsRootContent(),
+      "Only the root content APZC has a layout viewport");
+  if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
+    return mLastContentPaintMetrics.GetViewport();
+  }
+  return GetEffectiveLayoutViewport(aMode);
+}
+
 ParentLayerPoint
 AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncTransformConsumer aMode) const
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
     return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom();
   }
@@ -3797,16 +3808,25 @@ AsyncPanZoomController::GetCurrentAsyncT
 
   LayerToParentLayerScale compositedAsyncZoom =
       (effectiveZoom / mFrameMetrics.LayersPixelsPerCSSPixel()).ToScaleFactor();
   return AsyncTransform(
     LayerToParentLayerScale(compositedAsyncZoom.scale * mTestAsyncZoom.scale),
     -translation);
 }
 
+CSSRect
+AsyncPanZoomController::GetEffectiveLayoutViewport(AsyncTransformConsumer aMode) const
+{
+  if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) {
+    return mCompositedLayoutViewport;
+  }
+  return mFrameMetrics.GetViewport();
+}
+
 CSSPoint
 AsyncPanZoomController::GetEffectiveScrollOffset(AsyncTransformConsumer aMode) const
 {
   if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) {
     return mCompositedScrollOffset;
   }
   return mFrameMetrics.GetScrollOffset();
 }
@@ -3821,16 +3841,17 @@ AsyncPanZoomController::GetEffectiveZoom
 }
 
 bool
 AsyncPanZoomController::SampleCompositedAsyncTransform()
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
   if (mCompositedScrollOffset != mFrameMetrics.GetScrollOffset() ||
       mCompositedZoom != mFrameMetrics.GetZoom()) {
+    mCompositedLayoutViewport = mFrameMetrics.GetViewport();
     mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
     mCompositedZoom = mFrameMetrics.GetZoom();
     return true;
   }
   return false;
 }
 
 AsyncTransformComponentMatrix
@@ -4079,16 +4100,17 @@ void AsyncPanZoomController::NotifyLayer
     // Initialize our internal state to something sane when the content
     // that was just painted is something we knew nothing about previously
     CancelAnimation();
 
     mScrollMetadata = aScrollMetadata;
     mExpectedGeckoMetrics = aLayerMetrics;
     ShareCompositorFrameMetrics();
 
+    mCompositedLayoutViewport = mFrameMetrics.GetViewport();
     mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
     mCompositedZoom = mFrameMetrics.GetZoom();
 
     if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) {
       // A non-zero display port margin here indicates a displayport has
       // been set by a previous APZC for the content at this guid. The
       // scrollable rect may have changed since then, making the margins
       // wrong, so we need to calculate a new display port.
@@ -4163,16 +4185,17 @@ void AsyncPanZoomController::NotifyLayer
       // Send an acknowledgement with the new scroll generation so that any
       // repaint requests later in this function go through.
       // Because of the scroll generation update, any inflight paint requests are
       // going to be ignored by layout, and so mExpectedGeckoMetrics
       // becomes incorrect for the purposes of calculating the LD transform. To
       // correct this we need to update mExpectedGeckoMetrics to be the
       // last thing we know was painted by Gecko.
       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
+      mCompositedLayoutViewport = mFrameMetrics.GetViewport();
       mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
       mExpectedGeckoMetrics = aLayerMetrics;
 
       // Cancel the animation (which might also trigger a repaint request)
       // after we update the scroll offset above. Otherwise we can be left
       // in a state where things are out of sync.
       CancelAnimation();
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -903,18 +903,20 @@ private:
   FrameMetrics& mLastContentPaintMetrics;  // for convenience, refers to mLastContentPaintMetadata.mMetrics
   // The last metrics used for a content repaint request.
   FrameMetrics mLastPaintRequestMetrics;
   // The metrics that we expect content to have. This is updated when we
   // request a content repaint, and when we receive a shadow layers update.
   // This allows us to transform events into Gecko's coordinate space.
   FrameMetrics mExpectedGeckoMetrics;
 
-  // These variables cache the scroll offset and zoom stored in |mFrameMetrics|
-  // the last time SampleCompositedAsyncTransform() was called.
+  // These variables cache the layout viewport, scroll offset, and zoom stored
+  // in |mFrameMetrics| the last time SampleCompositedAsyncTransform() was
+  // called.
+  CSSRect mCompositedLayoutViewport;
   CSSPoint mCompositedScrollOffset;
   CSSToParentLayerScale2D mCompositedZoom;
 
   AxisX mX;
   AxisY mY;
 
   // This flag is set to true when we are in a axis-locked pan as a result of
   // the touch-action CSS property.
@@ -987,16 +989,22 @@ public:
    * regardless of mForceDisableApz.
    */
   enum AsyncTransformConsumer {
     eForHitTesting,
     eForCompositing,
   };
 
   /**
+   * Get the current layout viewport of the scrollable frame corresponding to
+   * this APZC.
+   */
+  CSSRect GetCurrentAsyncLayoutViewport(AsyncTransformConsumer aMode) const;
+
+  /**
    * Get the current scroll offset of the scrollable frame corresponding
    * to this APZC, including the effects of any asynchronous panning and
    * zooming, in ParentLayer pixels.
    */
   ParentLayerPoint GetCurrentAsyncScrollOffset(AsyncTransformConsumer aMode) const;
 
   /**
    * Get the current scroll offset of the scrollable frame corresponding
@@ -1036,21 +1044,22 @@ private:
    *
    * (This is only relevant when |gfxPrefs::APZFrameDelayEnabled() == true|.
    * Otherwise, GetCurrentAsyncTransform() always reflects what's stored in
    * |mFrameMetrics| immediately, without any delay.)
    */
   bool SampleCompositedAsyncTransform();
 
   /*
-   * Helper functions to query the async scroll offset and zoom either
-   * directly from |mFrameMetrics|, or from cached variables that store
-   * the scroll offset and zoom from the last time it was sampled by
-   * calling SampleCompositedAsyncTransform(), depending on who is asking.
+   * Helper functions to query the async layout viewport, scroll offset, and
+   * zoom either directly from |mFrameMetrics|, or from cached variables that
+   * store the required value from the last time it was sampled by calling
+   * SampleCompositedAsyncTransform(), depending on who is asking.
    */
+  CSSRect GetEffectiveLayoutViewport(AsyncTransformConsumer aMode) const;
   CSSPoint GetEffectiveScrollOffset(AsyncTransformConsumer aMode) const;
   CSSToParentLayerScale2D GetEffectiveZoom(AsyncTransformConsumer aMode) const;
 
   /* ===================================================================
    * The functions and members in this section are used to manage
    * the state that tracks what this APZC is doing with the input events.
    */
 protected:
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -311,30 +311,36 @@ RenderMinimap(ContainerT* aContainer,
   const int verticalPadding = 10;
   const int horizontalPadding = 5;
   gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
   gfx::Color tileActiveColor(1, 1, 1, 0.4f);
   gfx::Color tileBorderColor(0, 0, 0, 0.1f);
   gfx::Color pageBorderColor(0, 0, 0);
   gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
   gfx::Color displayPortColor(0, 1.f, 0);
-  gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
+  gfx::Color layoutPortColor(1.f, 0, 0);
+  gfx::Color visualPortColor(0, 0, 1.f, 0.3f);
 
   // Rects
   ParentLayerRect compositionBounds = fm.GetCompositionBounds();
   LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
-  LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
+  LayerRect visualRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
   LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
+  Maybe<LayerRect> layoutRect;
   Maybe<LayerRect> cdp;
+  if (fm.IsRootContent()) {
+    CSSRect viewport = aSampler->GetCurrentAsyncLayoutViewport(wrapper);
+    layoutRect = Some(viewport * fm.LayersPixelsPerCSSPixel());
+  }
   if (!fm.GetCriticalDisplayPort().IsEmpty()) {
     cdp = Some((fm.GetCriticalDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel());
   }
 
   // Don't render trivial minimap. They can show up from textboxes and other tiny frames.
-  if (viewRect.Width() < 64 && viewRect.Height() < 64) {
+  if (visualRect.Width() < 64 && visualRect.Height() < 64) {
     return;
   }
 
   // Compute a scale with an appropriate aspect ratio
   // We allocate up to 100px of width and the height of this layer.
   float scaleFactor;
   float scaleFactorX;
   float scaleFactorY;
@@ -369,19 +375,26 @@ RenderMinimap(ContainerT* aContainer,
   compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
 
   // Render the critical displayport if there is one
   if (cdp) {
     r = transform.TransformBounds(cdp->ToUnknownRect());
     compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect, aContainer->GetEffectiveTransform());
   }
 
-  // Render the viewport.
-  r = transform.TransformBounds(viewRect.ToUnknownRect());
-  compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
+  // Render the layout viewport if it exists (which is only in the root
+  // content APZC).
+  if (layoutRect) {
+    r = transform.TransformBounds(layoutRect->ToUnknownRect());
+    compositor->SlowDrawRect(r, layoutPortColor, clipRect, aContainer->GetEffectiveTransform());
+  }
+
+  // Render the visual viewport.
+  r = transform.TransformBounds(visualRect.ToUnknownRect());
+  compositor->SlowDrawRect(r, visualPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
 }
 
 template<class ContainerT> void
 RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager,
              const RenderTargetIntRect& aClipRect,
              const Maybe<gfx::Polygon>& aGeometry)
 {
   Compositor* compositor = aManager->GetCompositor();
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7679,17 +7679,17 @@ nsCSSFrameConstructor::ContentRangeInser
 
       frameType = insertion.mParentFrame->Type();
     }
   }
 
   AutoFrameConstructionItemList items(this);
   ParentType parentType = GetParentType(frameType);
   FlattenedChildIterator iter(insertion.mContainer);
-  bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
+  bool haveNoXBLChildren = !iter.XBLInvolved() || !iter.GetNextChild();
   if (aStartChild->GetPreviousSibling() &&
       parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the
     // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
     // parent type is not block, though, since WipeContainingBlock
     // already handles that sitation.
     AddTextItemIfNeeded(state, insertion, aStartChild->GetPreviousSibling(),
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -82,8 +82,10 @@ chrome.jar:
 % override chrome://global/locale/global.dtd chrome://browser/locale/overrides/global.dtd
 % override chrome://global/locale/AccessFu.properties chrome://browser/locale/overrides/AccessFu.properties
 % override chrome://global/locale/dom/dom.properties chrome://browser/locale/overrides/dom/dom.properties
 % override chrome://global/locale/plugins.properties chrome://browser/locale/overrides/plugins.properties
 
 # mobile/locales/jar.mn resources and overrides
 % override chrome://global/locale/netError.dtd    chrome://browser/locale/netError.dtd
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
+% resource search-plugins chrome://browser/locale/searchplugins/
+
--- a/mobile/android/components/extensions/schemas/browser_action.json
+++ b/mobile/android/components/extensions/schemas/browser_action.json
@@ -27,17 +27,18 @@
                 "type": "string",
                 "format": "relativeUrl",
                 "optional": true,
                 "preprocess": "localize"
               },
               "browser_style": {
                 "type": "boolean",
                 "deprecated": "Unsupported on Android.",
-                "optional": true
+                "optional": true,
+                "default": false
               },
               "default_area": {
                 "description": "Defines the location the browserAction will appear by default.  The default location is navbar.",
                 "type": "string",
                 "enum": ["navbar", "menupanel", "tabstrip", "personaltoolbar"],
                 "deprecated": "Unsupported on Android.",
                 "optional": true
               }
--- a/mobile/android/components/extensions/schemas/page_action.json
+++ b/mobile/android/components/extensions/schemas/page_action.json
@@ -25,17 +25,18 @@
               "default_popup": {
                 "type": "string",
                 "format": "relativeUrl",
                 "optional": true,
                 "preprocess": "localize"
               },
               "browser_style": {
                 "type": "boolean",
-                "optional": true
+                "optional": true,
+                "default": false
               }
             },
             "optional": true
           }
         }
       }
     ]
   },
--- a/mobile/android/components/moz.build
+++ b/mobile/android/components/moz.build
@@ -46,10 +46,9 @@ EXTRA_COMPONENTS += [
 EXTRA_PP_COMPONENTS += [
     'MobileComponents.manifest',
 ]
 
 DIRS += [
     'extensions',
     'build',
     'geckoview',
-    'search',
 ]
deleted file mode 100644
--- a/mobile/android/components/search/jar.mn
+++ /dev/null
@@ -1,8 +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/.
-
-chrome.jar:
-        searchplugins/                                              (searchplugins/**)
-
-% resource search-plugins %searchplugins/
deleted file mode 100644
--- a/mobile/android/components/search/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- 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/.
-
-JAR_MANIFESTS += ['jar.mn']
-
-with Files('**'):
-    BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -0,0 +1,15 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package android.view.inputmethod;
+
+/**
+ * This dummy class is used when running tests on Android versions prior to 21,
+ * when the CursorAnchorInfo class was first introduced. Without this class,
+ * tests will crash with ClassNotFoundException when the test rule uses reflection
+ * to access the TextInputDelegate interface.
+ */
+public class CursorAnchorInfo {
+}
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -7,16 +7,17 @@ package org.mozilla.geckoview.test.rule;
 
 import org.mozilla.gecko.gfx.GeckoDisplay;
 import org.mozilla.geckoview.BuildConfig;
 import org.mozilla.geckoview.GeckoResponse;
 import org.mozilla.geckoview.GeckoRuntime;
 import org.mozilla.geckoview.GeckoRuntimeSettings;
 import org.mozilla.geckoview.GeckoSession;
 import org.mozilla.geckoview.GeckoSessionSettings;
+import org.mozilla.geckoview.SessionTextInput;
 import org.mozilla.geckoview.test.rdp.Actor;
 import org.mozilla.geckoview.test.rdp.Promise;
 import org.mozilla.geckoview.test.rdp.RDPConnection;
 import org.mozilla.geckoview.test.rdp.Tab;
 import org.mozilla.geckoview.test.util.Callbacks;
 
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
@@ -988,24 +989,36 @@ public class GeckoSessionTestRule extend
      * Get the runtime set up for the current test.
      *
      * @return GeckoRuntime object.
      */
     public @NonNull GeckoRuntime getRuntime() {
         return sRuntime;
     }
 
-    protected static Method getCallbackSetter(final @NonNull Class<?> cls)
-            throws NoSuchMethodException {
-        return GeckoSession.class.getMethod("set" + cls.getSimpleName(), cls);
+    protected static Object setDelegate(final @NonNull Class<?> cls,
+                                        final @NonNull GeckoSession session,
+                                        final @Nullable Object delegate)
+            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+        if (cls == GeckoSession.TextInputDelegate.class) {
+            return SessionTextInput.class.getMethod("setDelegate",
+                                                    cls).invoke(session.getTextInput(), delegate);
+        }
+        return GeckoSession.class.getMethod("set" + cls.getSimpleName(),
+                                            cls).invoke(session, delegate);
     }
 
-    protected static Method getCallbackGetter(final @NonNull Class<?> cls)
-            throws NoSuchMethodException {
-        return GeckoSession.class.getMethod("get" + cls.getSimpleName());
+    protected static Object getDelegate(final @NonNull Class<?> cls,
+                                        final @NonNull GeckoSession session)
+            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+        if (cls == GeckoSession.TextInputDelegate.class) {
+            return SessionTextInput.class.getMethod("getDelegate")
+                                         .invoke(session.getTextInput());
+        }
+        return GeckoSession.class.getMethod("get" + cls.getSimpleName()).invoke(session);
     }
 
     @NonNull
     private Set<Class<?>> getCurrentDelegates() {
         final List<ExternalDelegate<?>> waitDelegates = mWaitScopeDelegates.getExternalDelegates();
         final List<ExternalDelegate<?>> testDelegates = mTestScopeDelegates.getExternalDelegates();
 
         if (waitDelegates.isEmpty() && testDelegates.isEmpty()) {
@@ -1242,18 +1255,17 @@ public class GeckoSessionTestRule extend
             }
         } else if (!mClosedSession) {
             openSession(mMainSession);
         }
     }
 
     protected void prepareSession(final GeckoSession session) throws Throwable {
         for (final Class<?> cls : DEFAULT_DELEGATES) {
-            getCallbackSetter(cls).invoke(
-                    session, mNullDelegates.contains(cls) ? null : mCallbackProxy);
+            setDelegate(cls, session, mNullDelegates.contains(cls) ? null : mCallbackProxy);
         }
     }
 
     /**
      * Call open() on a session, and ensure it's ready for use by the test. In particular,
      * remove any extra calls recorded as part of opening the session.
      *
      * @param session Session to open.
@@ -1644,17 +1656,17 @@ public class GeckoSessionTestRule extend
         }
 
         // Make sure all handlers are set though #delegateUntilTestEnd or #delegateDuringNextWait,
         // instead of through GeckoSession directly, so that we can still record calls even with
         // custom handlers set.
         for (final Class<?> ifce : DEFAULT_DELEGATES) {
             final Object callback;
             try {
-                callback = getCallbackGetter(ifce).invoke(session == null ? mMainSession : session);
+                callback = getDelegate(ifce, session == null ? mMainSession : session);
             } catch (final NoSuchMethodException | IllegalAccessException |
                     InvocationTargetException e) {
                 throw unwrapRuntimeException(e);
             }
             if (mNullDelegates.contains(ifce)) {
                 // Null-delegates are initially null but are allowed to be any value.
                 continue;
             }
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
@@ -3,24 +3,26 @@
  * 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/. */
 
 package org.mozilla.geckoview.test.util
 
 import org.mozilla.geckoview.GeckoResponse
 import org.mozilla.geckoview.GeckoSession
 
+import android.view.inputmethod.CursorAnchorInfo
+import android.view.inputmethod.ExtractedText
+import android.view.inputmethod.ExtractedTextRequest
+
 class Callbacks private constructor() {
-    object Default : All {
-    }
+    object Default : All
 
     interface All : ContentDelegate, NavigationDelegate, PermissionDelegate, ProgressDelegate,
-                    PromptDelegate, ScrollDelegate, SelectionActionDelegate,
-                    TrackingProtectionDelegate {
-    }
+                    PromptDelegate, ScrollDelegate, SelectionActionDelegate, TextInputDelegate,
+                    TrackingProtectionDelegate
 
     interface ContentDelegate : GeckoSession.ContentDelegate {
         override fun onTitleChange(session: GeckoSession, title: String) {
         }
 
         override fun onFocusRequest(session: GeckoSession) {
         }
 
@@ -129,9 +131,29 @@ class Callbacks private constructor() {
 
     interface SelectionActionDelegate : GeckoSession.SelectionActionDelegate {
         override fun onShowActionRequest(session: GeckoSession, selection: GeckoSession.SelectionActionDelegate.Selection, actions: Array<out String>, response: GeckoResponse<String>) {
         }
 
         override fun onHideAction(session: GeckoSession, reason: Int) {
         }
     }
+
+    interface TextInputDelegate : GeckoSession.TextInputDelegate {
+        override fun restartInput(session: GeckoSession, reason: Int) {
+        }
+
+        override fun showSoftInput(session: GeckoSession) {
+        }
+
+        override fun hideSoftInput(session: GeckoSession) {
+        }
+
+        override fun updateSelection(session: GeckoSession, selStart: Int, selEnd: Int, compositionStart: Int, compositionEnd: Int) {
+        }
+
+        override fun updateExtractedText(session: GeckoSession, request: ExtractedTextRequest, text: ExtractedText) {
+        }
+
+        override fun updateCursorAnchorInfo(session: GeckoSession, info: CursorAnchorInfo) {
+        }
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
@@ -6,48 +6,52 @@
 package org.mozilla.geckoview;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.mozilla.gecko.GeckoEditableChild;
 import org.mozilla.gecko.IGeckoEditableChild;
 import org.mozilla.gecko.IGeckoEditableParent;
+import org.mozilla.gecko.InputMethods;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
 
 import android.graphics.RectF;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.Editable;
 import android.text.InputFilter;
+import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.method.KeyListener;
 import android.text.method.TextKeyListener;
 import android.text.style.CharacterStyle;
 import android.util.Log;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.inputmethod.EditorInfo;
 
 /**
  * GeckoEditable implements only some functions of Editable
  * The field mText contains the actual underlying
  * SpannableStringBuilder/Editable that contains our text.
  */
 /* package */ final class GeckoEditable
     extends IGeckoEditableParent.Stub
@@ -56,16 +60,17 @@ import android.view.View;
                SessionTextInput.EditableClient {
 
     private static final boolean DEBUG = false;
     private static final String LOGTAG = "GeckoEditable";
 
     // Filters to implement Editable's filtering functionality
     private InputFilter[] mFilters;
 
+    /* package */ final GeckoSession mSession;
     private final AsyncText mText;
     private final Editable mProxy;
     private final ConcurrentLinkedQueue<Action> mActions;
     private KeyCharacterMap mKeyMap;
 
     // mIcRunHandler is the Handler that currently runs Gecko-to-IC Runnables
     // mIcPostHandler is the Handler to post Gecko-to-IC Runnables to
     // The two can be different when switching from one handler to another
@@ -80,18 +85,29 @@ import android.view.View;
     /* package */ SessionTextInput.EditableListener mListener;
 
     /* package */ boolean mInBatchMode; // Used by IC thread
     /* package */ boolean mNeedSync; // Used by IC thread
     // Gecko side needs an updated composition from Java;
     private boolean mNeedUpdateComposition; // Used by IC thread
     private boolean mSuppressKeyUp; // Used by IC thread
 
+    private int mIMEState = // Used by IC thread.
+            SessionTextInput.EditableListener.IME_STATE_DISABLED;
+    private String mIMETypeHint = ""; // Used by IC/UI thread.
+    private String mIMEModeHint = ""; // Used by IC thread.
+    private String mIMEActionHint = ""; // Used by IC thread.
+    private int mIMEFlags; // Used by IC thread.
+
     private boolean mIgnoreSelectionChange; // Used by Gecko thread
 
+    // Prevent showSoftInput and hideSoftInput from being called multiple times in a row,
+    // including reentrant calls on some devices. Used by UI/IC thread.
+    /* package */ final AtomicInteger mSoftInputReentrancyGuard = new AtomicInteger();
+
     private static final int IME_RANGE_CARETPOSITION = 1;
     private static final int IME_RANGE_RAWINPUT = 2;
     private static final int IME_RANGE_SELECTEDRAWTEXT = 3;
     private static final int IME_RANGE_CONVERTEDTEXT = 4;
     private static final int IME_RANGE_SELECTEDCONVERTEDTEXT = 5;
 
     private static final int IME_RANGE_LINE_NONE = 0;
     private static final int IME_RANGE_LINE_DOTTED = 1;
@@ -602,22 +618,23 @@ import android.view.View;
             if (DEBUG) {
                 Log.d(LOGTAG, "sending: " + event);
             }
             onKeyEvent(mFocusedChild, event, event.getAction(),
                        /* metaState */ 0, /* isSynthesizedImeKey */ true);
         }
     }
 
-    public GeckoEditable() {
+    public GeckoEditable(@NonNull final GeckoSession session) {
         if (DEBUG) {
             // Called by SessionTextInput.
             ThreadUtils.assertOnUiThread();
         }
 
+        mSession = session;
         mText = new AsyncText();
         mActions = new ConcurrentLinkedQueue<Action>();
 
         final Class<?>[] PROXY_INTERFACES = { Editable.class };
         mProxy = (Editable) Proxy.newProxyInstance(Editable.class.getClassLoader(),
                                                    PROXY_INTERFACES, this);
 
         mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler();
@@ -848,32 +865,27 @@ import android.view.View;
                               " : " + Integer.toHexString(rangeStyles) +
                               " : " + Integer.toHexString(rangeForeColor) +
                               " : " + Integer.toHexString(rangeBackColor));
             }
         } while (rangeStart < composingEnd);
     }
 
     @Override // SessionTextInput.EditableClient
-    public void sendKeyEvent(final @Nullable View view, final boolean inputActive, final int action,
-                             @NonNull KeyEvent event) {
-        final Editable editable = getEditable();
-        if (editable == null) {
-            return;
-        }
-
+    public void sendKeyEvent(final @Nullable View view, final int action, @NonNull KeyEvent event) {
+        final Editable editable = mProxy;
         final KeyListener keyListener = TextKeyListener.getInstance();
         event = translateKey(event.getKeyCode(), event);
 
         // We only let TextKeyListener do UI things on the UI thread.
         final View v = ThreadUtils.isOnUiThread() ? view : null;
         final int keyCode = event.getKeyCode();
         final boolean handled;
 
-        if (!inputActive || shouldSkipKeyListener(keyCode, event)) {
+        if (shouldSkipKeyListener(keyCode, event)) {
             handled = false;
         } else if (action == KeyEvent.ACTION_DOWN) {
             setSuppressKeyUp(true);
             handled = keyListener.onKeyDown(v, editable, keyCode, event);
         } else if (action == KeyEvent.ACTION_UP) {
             handled = keyListener.onKeyUp(v, editable, keyCode, event);
         } else {
             handled = keyListener.onKeyOther(v, editable, event);
@@ -924,16 +936,20 @@ import android.view.View;
                        /* isSynthesizedImeKey */ false);
             icOfferAction(new Action(Action.TYPE_EVENT));
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Remote call failed", e);
         }
     }
 
     private boolean shouldSkipKeyListener(final int keyCode, final @NonNull KeyEvent event) {
+        if (mIMEState == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+            return true;
+        }
+
         // Preserve enter and tab keys for the browser
         if (keyCode == KeyEvent.KEYCODE_ENTER ||
             keyCode == KeyEvent.KEYCODE_TAB) {
             return true;
         }
         // BaseKeyListener returns false even if it handled these keys for us,
         // so we skip the key listener entirely and handle these ourselves
         if (keyCode == KeyEvent.KEYCODE_DEL ||
@@ -1169,39 +1185,83 @@ import android.view.View;
                 // Only post to IC thread below when the queue is empty.
                 return;
             }
         }
 
         mIcPostHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (type == SessionTextInput.EditableListener.NOTIFY_IME_REPLY_EVENT) {
-                    if (mNeedSync) {
-                        icSyncShadowText();
-                    }
-                    return;
-                }
-
-                if (type == SessionTextInput.EditableListener.NOTIFY_IME_OF_FOCUS &&
-                        mListener != null) {
-                    mFocusedChild = child;
-                    mNeedSync = false;
-                    mText.syncShadowText(/* listener */ null);
-                } else if (type == SessionTextInput.EditableListener.NOTIFY_IME_OF_BLUR) {
-                    mFocusedChild = null;
-                }
-
-                if (mListener != null) {
-                    mListener.notifyIME(type);
-                }
+                icNotifyIME(child, type);
             }
         });
     }
 
+    /* package */ void icNotifyIME(final IGeckoEditableChild child, final int type) {
+        if (DEBUG) {
+            assertOnIcThread();
+        }
+
+        if (type == SessionTextInput.EditableListener.NOTIFY_IME_REPLY_EVENT) {
+            if (mNeedSync) {
+                icSyncShadowText();
+            }
+            return;
+        }
+
+        switch (type) {
+            case SessionTextInput.EditableListener.NOTIFY_IME_OF_FOCUS:
+                mFocusedChild = child;
+                mNeedSync = false;
+                mText.syncShadowText(/* listener */ null);
+
+                // Do not reset mIMEState here; see comments in notifyIMEContext
+                if (mIMEState != SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+                    icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS);
+                }
+                break;
+
+            case SessionTextInput.EditableListener.NOTIFY_IME_OF_BLUR:
+                mFocusedChild = null;
+                break;
+
+            case SessionTextInput.EditableListener.NOTIFY_IME_OPEN_VKB:
+                toggleSoftInput(/* force */ true);
+                return; // Don't notify listener.
+
+            case SessionTextInput.EditableListener.NOTIFY_IME_TO_COMMIT_COMPOSITION: {
+                // Gecko already committed its composition. However, Android keyboards
+                // have trouble dealing with us removing the composition manually on the
+                // Java side. Therefore, we keep the composition intact on the Java side.
+                // The text content should still be in-sync on both sides.
+                //
+                // Nevertheless, if we somehow lost the composition, we must force the
+                // keyboard to reset.
+                final Spanned text = mText.getShadowText();
+                final Object[] spans = text.getSpans(0, text.length(), Object.class);
+                for (final Object span : spans) {
+                    if ((text.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
+                        // Still have composition; no need to reset.
+                        return; // Don't notify listener.
+                    }
+                }
+                // No longer have composition; perform reset.
+                icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE);
+                return; // Don't notify listener.
+            }
+
+            default:
+                throw new IllegalArgumentException("Invalid notifyIME type: " + type);
+        }
+
+        if (mListener != null) {
+            mListener.notifyIME(type);
+        }
+    }
+
     @Override // IGeckoEditableParent
     public void notifyIMEContext(final int state, final String typeHint,
                                  final String modeHint, final String actionHint,
                                  final int flags) {
         // On Gecko or binder thread.
         if (DEBUG) {
             Log.d(LOGTAG, "notifyIMEContext(" +
                           getConstantName(SessionTextInput.EditableListener.class,
@@ -1212,20 +1272,219 @@ import android.view.View;
 
         // Don't check token for notifyIMEContext, because the calls all come
         // from the parent process.
         ThreadUtils.assertOnGeckoThread();
 
         mIcPostHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mListener == null) {
+                icNotifyIMEContext(state, typeHint, modeHint, actionHint, flags);
+            }
+        });
+    }
+
+    /* package */ void icNotifyIMEContext(int state, final String typeHint,
+                                          final String modeHint, final String actionHint,
+                                          final int flags) {
+        if (DEBUG) {
+            assertOnIcThread();
+        }
+
+        // For some input type we will use a widget to display the ui, for those we must not
+        // display the ime. We can display a widget for date and time types and, if the sdk version
+        // is 11 or greater, for datetime/month/week as well.
+        if (typeHint != null && (typeHint.equalsIgnoreCase("date") ||
+                                 typeHint.equalsIgnoreCase("time") ||
+                                 typeHint.equalsIgnoreCase("month") ||
+                                 typeHint.equalsIgnoreCase("week") ||
+                                 typeHint.equalsIgnoreCase("datetime-local"))) {
+            state = SessionTextInput.EditableListener.IME_STATE_DISABLED;
+        }
+
+        final int oldState = mIMEState;
+        mIMEState = state;
+        mIMETypeHint = (typeHint == null) ? "" : typeHint;
+        mIMEModeHint = (modeHint == null) ? "" : modeHint;
+        mIMEActionHint = (actionHint == null) ? "" : actionHint;
+        mIMEFlags = flags;
+
+        if (mListener != null) {
+            mListener.notifyIMEContext(state, typeHint, modeHint, actionHint, flags);
+        }
+
+        // On focus, the notifyIMEContext call comes *before* the
+        // notifyIME(NOTIFY_IME_OF_FOCUS) call, but we need to call restartInput during
+        // notifyIME, so we skip restartInput here. On blur, the notifyIMEContext call
+        // comes *after* the notifyIME(NOTIFY_IME_OF_BLUR) call, and we need to call
+        // restartInput here.
+
+        // In either case, there is nothing to do here if we were disabled before.
+        if (oldState == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+            return;
+        }
+        if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+            icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_BLUR);
+        } else if (mFocusedChild != null) {
+            icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE);
+        }
+    }
+
+    private void icRestartInput(@GeckoSession.TextInputDelegate.RestartReason final int reason) {
+        if (DEBUG) {
+            assertOnIcThread();
+        }
+
+        ThreadUtils.postToUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSoftInputReentrancyGuard.incrementAndGet();
+                mSession.getTextInput().getDelegate().restartInput(mSession, reason);
+
+                postToInputConnection(new Runnable() {
+                    @Override
+                    public void run() {
+                        toggleSoftInput(/* force */ false);
+                    }
+                });
+            }
+        });
+    }
+
+    public void onCreateInputConnection(final EditorInfo outAttrs) {
+        final int state = mIMEState;
+        final String typeHint = mIMETypeHint;
+        final String modeHint = mIMEModeHint;
+        final String actionHint = mIMEActionHint;
+        final int flags = mIMEFlags;
+
+        // Some keyboards require us to fill out outAttrs even if we return null.
+        outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
+        outAttrs.actionLabel = null;
+
+        if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+            outAttrs.inputType = InputType.TYPE_NULL;
+            toggleSoftInput(/* force */ false);
+            return;
+        }
+
+        outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
+        if (state == SessionTextInput.EditableListener.IME_STATE_PASSWORD ||
+                "password".equalsIgnoreCase(typeHint)) {
+            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
+        } else if (typeHint.equalsIgnoreCase("url") ||
+                typeHint.equalsIgnoreCase("mozAwesomebar")) {
+            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
+        } else if (typeHint.equalsIgnoreCase("email")) {
+            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+        } else if (typeHint.equalsIgnoreCase("tel")) {
+            outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
+        } else if (typeHint.equalsIgnoreCase("number") ||
+                typeHint.equalsIgnoreCase("range")) {
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
+                    | InputType.TYPE_NUMBER_FLAG_SIGNED
+                    | InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        } else if (modeHint.equalsIgnoreCase("numeric")) {
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER |
+                    InputType.TYPE_NUMBER_FLAG_SIGNED |
+                    InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        } else if (modeHint.equalsIgnoreCase("digit")) {
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
+        } else {
+            // TYPE_TEXT_FLAG_IME_MULTI_LINE flag makes the fullscreen IME line wrap
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT |
+                    InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
+            if (typeHint.equalsIgnoreCase("textarea") ||
+                    typeHint.length() == 0) {
+                // empty typeHint indicates contentEditable/designMode documents
+                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
+            }
+            if (modeHint.equalsIgnoreCase("uppercase")) {
+                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
+            } else if (modeHint.equalsIgnoreCase("titlecase")) {
+                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
+            } else if (typeHint.equalsIgnoreCase("text") &&
+                    !modeHint.equalsIgnoreCase("autocapitalized")) {
+                outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_NORMAL;
+            } else if (!modeHint.equalsIgnoreCase("lowercase")) {
+                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+            }
+            // auto-capitalized mode is the default for types other than text
+        }
+
+        if (actionHint.equalsIgnoreCase("go")) {
+            outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
+        } else if (actionHint.equalsIgnoreCase("done")) {
+            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
+        } else if (actionHint.equalsIgnoreCase("next")) {
+            outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
+        } else if (actionHint.equalsIgnoreCase("search") ||
+                typeHint.equalsIgnoreCase("search")) {
+            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
+        } else if (actionHint.equalsIgnoreCase("send")) {
+            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
+        } else if (actionHint.length() > 0) {
+            if (DEBUG)
+                Log.w(LOGTAG, "Unexpected actionHint=\"" + actionHint + "\"");
+            outAttrs.actionLabel = actionHint;
+        }
+
+        if ((flags & SessionTextInput.EditableListener.IME_FLAG_PRIVATE_BROWSING) != 0) {
+            outAttrs.imeOptions |= InputMethods.IME_FLAG_NO_PERSONALIZED_LEARNING;
+        }
+
+        toggleSoftInput(/* force */ false);
+    }
+
+    /* package */ void toggleSoftInput(final boolean force) {
+        // Can be called from UI or IC thread.
+        final int state = mIMEState;
+        final int flags = mIMEFlags;
+
+        // There are three paths that toggleSoftInput() can be called:
+        // 1) through calling restartInput(), which then indirectly calls
+        //    onCreateInputConnection() and then toggleSoftInput().
+        // 2) through calling toggleSoftInput() directly from restartInput().
+        //    This path is the fallback in case 1) does not happen.
+        // 3) through a system-generated onCreateInputConnection() call when the activity
+        //    is restored from background, which then calls toggleSoftInput().
+        // mSoftInputReentrancyGuard is needed to ensure that between the different paths,
+        // the soft input is only toggled exactly once.
+
+        ThreadUtils.postToUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final int reentrancyGuard = mSoftInputReentrancyGuard.decrementAndGet();
+                final boolean isReentrant;
+                if (reentrancyGuard < 0) {
+                    mSoftInputReentrancyGuard.incrementAndGet();
+                    isReentrant = false;
+                } else {
+                    isReentrant = reentrancyGuard > 0;
+                }
+
+                // When using Find In Page, we can still receive notifyIMEContext calls due to the
+                // selection changing when highlighting. However in this case we don't want to
+                // show/hide the keyboard because the find box has the focus and is taking input from
+                // the keyboard.
+                final View view = mSession.getTextInput().getView();
+                final boolean isFocused = (view == null) || view.hasFocus();
+
+                final boolean isUserAction = ((flags &
+                        SessionTextInput.EditableListener.IME_FLAG_USER_ACTION) != 0);
+
+                if (!force && (isReentrant || !isFocused || !isUserAction)) {
                     return;
                 }
-                mListener.notifyIMEContext(state, typeHint, modeHint, actionHint, flags);
+                if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+                    mSession.getTextInput().getDelegate().hideSoftInput(mSession);
+                    return;
+                }
+                mSession.getEventDispatcher().dispatch("GeckoView:ZoomToInput", null);
+                mSession.getTextInput().getDelegate().showSoftInput(mSession);
             }
         });
     }
 
     @Override // IGeckoEditableParent
     public void onSelectionChange(final IBinder token,
                                   final int start, final int end) {
         // On Gecko or binder thread.
@@ -1699,60 +1958,59 @@ import android.view.View;
         throw new UnsupportedOperationException("method must be called through mProxy");
     }
 
     @Override
     public String toString() {
         throw new UnsupportedOperationException("method must be called through mProxy");
     }
 
-    public boolean onKeyPreIme(final @Nullable View view, final boolean inputActive,
-                               final int keyCode, final @NonNull KeyEvent event) {
+    public boolean onKeyPreIme(final @Nullable View view, final int keyCode,
+                               final @NonNull KeyEvent event) {
         return false;
     }
 
-    public boolean onKeyDown(final @Nullable View view, final boolean inputActive,
-                             final int keyCode, final @NonNull KeyEvent event) {
-        return processKey(view, inputActive, KeyEvent.ACTION_DOWN, keyCode, event);
+    public boolean onKeyDown(final @Nullable View view, final int keyCode,
+                             final @NonNull KeyEvent event) {
+        return processKey(view, KeyEvent.ACTION_DOWN, keyCode, event);
     }
 
-    public boolean onKeyUp(final @Nullable View view, final boolean inputActive,
-                           final int keyCode, final @NonNull KeyEvent event) {
-        return processKey(view, inputActive, KeyEvent.ACTION_UP, keyCode, event);
+    public boolean onKeyUp(final @Nullable View view, final int keyCode,
+                           final @NonNull KeyEvent event) {
+        return processKey(view, KeyEvent.ACTION_UP, keyCode, event);
     }
 
-    public boolean onKeyMultiple(final @Nullable View view, final boolean inputActive,
-                                 final int keyCode, int repeatCount,
+    public boolean onKeyMultiple(final @Nullable View view, final int keyCode, int repeatCount,
                                  final @NonNull KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
             // KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters()
             final String str = event.getCharacters();
             for (int i = 0; i < str.length(); i++) {
                 final KeyEvent charEvent = getCharKeyEvent(str.charAt(i));
-                if (!processKey(view, inputActive, KeyEvent.ACTION_DOWN,
+                if (!processKey(view, KeyEvent.ACTION_DOWN,
                                 KeyEvent.KEYCODE_UNKNOWN, charEvent) ||
-                    !processKey(view, inputActive, KeyEvent.ACTION_UP,
+                    !processKey(view, KeyEvent.ACTION_UP,
                                 KeyEvent.KEYCODE_UNKNOWN, charEvent)) {
                     return false;
                 }
             }
             return true;
         }
 
         while ((repeatCount--) > 0) {
-            if (!processKey(view, inputActive, KeyEvent.ACTION_DOWN, keyCode, event) ||
-                !processKey(view, inputActive, KeyEvent.ACTION_UP, keyCode, event)) {
+            if (!processKey(view, KeyEvent.ACTION_DOWN, keyCode, event) ||
+                !processKey(view, KeyEvent.ACTION_UP, keyCode, event)) {
                 return false;
             }
         }
         return true;
     }
 
-    public boolean onKeyLongPress(final @Nullable View view, final boolean inputActive,
-                                  final int keyCode, final @NonNull KeyEvent event) {
+    public boolean onKeyLongPress(final @Nullable View view, final int keyCode,
+                                  final @NonNull KeyEvent event) {
         return false;
     }
 
     /**
      * Get a key that represents a given character.
      */
     private static KeyEvent getCharKeyEvent(final char c) {
         final long time = SystemClock.uptimeMillis();
@@ -1765,26 +2023,26 @@ import android.view.View;
 
             @Override
             public int getUnicodeChar(int metaState) {
                 return c;
             }
         };
     }
 
-    private boolean processKey(final @Nullable View view, final boolean inputActive,
-                               final int action, final int keyCode, final @NonNull KeyEvent event) {
+    private boolean processKey(final @Nullable View view, final int action, final int keyCode,
+                               final @NonNull KeyEvent event) {
         if (keyCode > KeyEvent.getMaxKeyCode() || !shouldProcessKey(keyCode, event)) {
             return false;
         }
 
         postToInputConnection(new Runnable() {
             @Override
             public void run() {
-                sendKeyEvent(view, inputActive, action, event);
+                sendKeyEvent(view, action, event);
             }
         });
         return true;
     }
 
     private static boolean shouldProcessKey(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_MENU:
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputConnection.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputConnection.java
@@ -1,45 +1,41 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.geckoview;
 
 import android.annotation.TargetApi;
-import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.media.AudioManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.support.annotation.NonNull;
 import android.text.Editable;
-import android.text.InputType;
 import android.text.Selection;
 import android.text.SpannableString;
-import android.text.Spanned;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 
 import org.mozilla.gecko.Clipboard;
 import org.mozilla.gecko.InputMethods;
-import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 
 /* package */ final class GeckoInputConnection
     extends BaseInputConnection
@@ -54,37 +50,30 @@ import java.lang.reflect.Proxy;
         "org.mozilla.gecko.tests.components.GeckoViewComponent$TextInput";
 
     private static final int INLINE_IME_MIN_DISPLAY_SIZE = 480;
 
     private static Handler sBackgroundHandler;
 
     // Managed only by notifyIMEContext; see comments in notifyIMEContext
     private int mIMEState;
-    private String mIMETypeHint = "";
-    private String mIMEModeHint = "";
     private String mIMEActionHint = "";
-    private int mIMEFlags;
-    private boolean mFocused;
     private int mLastSelectionStart;
     private int mLastSelectionEnd;
 
     private String mCurrentInputMethod = "";
 
     private final GeckoSession mSession;
     private final View mView;
     private final SessionTextInput.EditableClient mEditableClient;
     protected int mBatchEditCount;
     private ExtractedTextRequest mUpdateRequest;
     private final InputConnection mKeyInputConnection;
     private CursorAnchorInfo.Builder mCursorAnchorInfoBuilder;
 
-    // Prevent showSoftInput and hideSoftInput from causing reentrant calls on some devices.
-    private volatile boolean mSoftInputReentrancyGuard;
-
     public static SessionTextInput.InputConnectionClient create(
             final GeckoSession session,
             final View targetView,
             final SessionTextInput.EditableClient editable) {
         SessionTextInput.InputConnectionClient ic = new GeckoInputConnection(session, targetView, editable);
         if (DEBUG) {
             ic = wrapForDebug(ic);
         }
@@ -266,63 +255,20 @@ import java.lang.reflect.Proxy;
     }
 
     @Override // SessionTextInput.InputConnectionClient
     public View getView() {
         return mView;
     }
 
     @NonNull
-    /* package */ SessionTextInput.Delegate getInputDelegate() {
+    /* package */ GeckoSession.TextInputDelegate getInputDelegate() {
         return mSession.getTextInput().getDelegate();
     }
 
-    private void showSoftInputWithToolbar(final boolean showToolbar) {
-        if (mSoftInputReentrancyGuard) {
-            return;
-        }
-
-        getView().post(new Runnable() {
-            @Override
-            public void run() {
-                if (showToolbar) {
-                    mSession.getDynamicToolbarAnimator().showToolbar(/* immediately */ true);
-                }
-                mSession.getEventDispatcher().dispatch("GeckoView:ZoomToInput", null);
-
-                mSoftInputReentrancyGuard = true;
-                getInputDelegate().showSoftInput();
-                mSoftInputReentrancyGuard = false;
-            }
-        });
-    }
-
-    private void hideSoftInput() {
-        if (mSoftInputReentrancyGuard) {
-            return;
-        }
-        getView().post(new Runnable() {
-            @Override
-            public void run() {
-                mSoftInputReentrancyGuard = true;
-                getInputDelegate().hideSoftInput();
-                mSoftInputReentrancyGuard = false;
-            }
-        });
-    }
-
-    private void restartInput(final @SessionTextInput.Delegate.RestartReason int reason) {
-        getView().post(new Runnable() {
-            @Override
-            public void run() {
-                getInputDelegate().restartInput(reason);
-            }
-        });
-    }
-
     @Override // SessionTextInput.EditableListener
     public void onTextChange() {
         final Editable editable = getEditable();
         if (mUpdateRequest == null || editable == null) {
             return;
         }
 
         final ExtractedTextRequest request = mUpdateRequest;
@@ -338,17 +284,17 @@ import java.lang.reflect.Proxy;
             extractedText.text = new SpannableString(editable);
         } else {
             extractedText.text = editable.toString();
         }
 
         getView().post(new Runnable() {
             @Override
             public void run() {
-                getInputDelegate().updateExtractedText(request, extractedText);
+                getInputDelegate().updateExtractedText(mSession, request, extractedText);
             }
         });
     }
 
     @Override // SessionTextInput.EditableListener
     public void onSelectionChange() {
 
         final Editable editable = getEditable();
@@ -366,17 +312,18 @@ import java.lang.reflect.Proxy;
         }
 
         final int compositionStart = getComposingSpanStart(editable);
         final int compositionEnd = getComposingSpanEnd(editable);
 
         getView().post(new Runnable() {
             @Override
             public void run() {
-                getInputDelegate().updateSelection(start, end, compositionStart, compositionEnd);
+                getInputDelegate().updateSelection(mSession, start, end,
+                                                   compositionStart, compositionEnd);
             }
         });
     }
 
     @TargetApi(21)
     @Override // SessionTextInput.EditableListener
     public void updateCompositionRects(final RectF[] rects) {
         if (!(Build.VERSION.SDK_INT >= 21)) {
@@ -432,17 +379,17 @@ import java.lang.reflect.Proxy;
         }
 
         mCursorAnchorInfoBuilder.setComposingText(0, composition);
 
         final CursorAnchorInfo info = mCursorAnchorInfoBuilder.build();
         getView().post(new Runnable() {
             @Override
             public void run() {
-                getInputDelegate().updateCursorAnchorInfo(info);
+                getInputDelegate().updateCursorAnchorInfo(mSession, info);
             }
         });
     }
 
     @Override
     public boolean requestCursorUpdates(int cursorUpdateMode) {
 
         if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0) {
@@ -560,93 +507,20 @@ import java.lang.reflect.Proxy;
     @Override // InputConnection
     public void closeConnection() {
         // Not supported at the moment.
         super.closeConnection();
     }
 
     @Override // SessionTextInput.InputConnectionClient
     public synchronized InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        // Some keyboards require us to fill out outAttrs even if we return null.
-        outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
-        outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
-        outAttrs.actionLabel = null;
-
         if (mIMEState == IME_STATE_DISABLED) {
-            hideSoftInput();
             return null;
         }
 
-        if (mIMEState == IME_STATE_PASSWORD ||
-            "password".equalsIgnoreCase(mIMETypeHint))
-            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
-        else if (mIMETypeHint.equalsIgnoreCase("url") ||
-                 mIMETypeHint.equalsIgnoreCase("mozAwesomebar"))
-            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
-        else if (mIMETypeHint.equalsIgnoreCase("email"))
-            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
-        else if (mIMETypeHint.equalsIgnoreCase("tel"))
-            outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
-        else if (mIMETypeHint.equalsIgnoreCase("number") ||
-                 mIMETypeHint.equalsIgnoreCase("range"))
-            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
-                                 | InputType.TYPE_NUMBER_FLAG_SIGNED
-                                 | InputType.TYPE_NUMBER_FLAG_DECIMAL;
-        else if (mIMETypeHint.equalsIgnoreCase("week") ||
-                 mIMETypeHint.equalsIgnoreCase("month"))
-            outAttrs.inputType = InputType.TYPE_CLASS_DATETIME
-                                  | InputType.TYPE_DATETIME_VARIATION_DATE;
-        else if (mIMEModeHint.equalsIgnoreCase("numeric"))
-            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER |
-                                 InputType.TYPE_NUMBER_FLAG_SIGNED |
-                                 InputType.TYPE_NUMBER_FLAG_DECIMAL;
-        else if (mIMEModeHint.equalsIgnoreCase("digit"))
-            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
-        else {
-            // TYPE_TEXT_FLAG_IME_MULTI_LINE flag makes the fullscreen IME line wrap
-            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT |
-                                  InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
-            if (mIMETypeHint.equalsIgnoreCase("textarea") ||
-                    mIMETypeHint.length() == 0) {
-                // empty mIMETypeHint indicates contentEditable/designMode documents
-                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
-            }
-            if (mIMEModeHint.equalsIgnoreCase("uppercase"))
-                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
-            else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
-                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
-            else if (mIMETypeHint.equalsIgnoreCase("text") &&
-                    !mIMEModeHint.equalsIgnoreCase("autocapitalized"))
-                outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_NORMAL;
-            else if (!mIMEModeHint.equalsIgnoreCase("lowercase"))
-                outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
-            // auto-capitalized mode is the default for types other than text
-        }
-
-        if (mIMEActionHint.equalsIgnoreCase("go"))
-            outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
-        else if (mIMEActionHint.equalsIgnoreCase("done"))
-            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
-        else if (mIMEActionHint.equalsIgnoreCase("next"))
-            outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
-        else if (mIMEActionHint.equalsIgnoreCase("search") ||
-                 mIMETypeHint.equalsIgnoreCase("search"))
-            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
-        else if (mIMEActionHint.equalsIgnoreCase("send"))
-            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
-        else if (mIMEActionHint.length() > 0) {
-            if (DEBUG)
-                Log.w(LOGTAG, "Unexpected mIMEActionHint=\"" + mIMEActionHint + "\"");
-            outAttrs.actionLabel = mIMEActionHint;
-        }
-
-        if ((mIMEFlags & IME_FLAG_PRIVATE_BROWSING) != 0) {
-            outAttrs.imeOptions |= InputMethods.IME_FLAG_NO_PERSONALIZED_LEARNING;
-        }
-
         Context context = getView().getContext();
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         if (Math.min(metrics.widthPixels, metrics.heightPixels) > INLINE_IME_MIN_DISPLAY_SIZE) {
             // prevent showing full-screen keyboard only when the screen is tall enough
             // to show some reasonable amount of the page (see bug 752709)
             outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI
                                    | EditorInfo.IME_FLAG_NO_FULLSCREEN;
         }
@@ -660,25 +534,16 @@ import java.lang.reflect.Proxy;
         String prevInputMethod = mCurrentInputMethod;
         mCurrentInputMethod = InputMethods.getCurrentInputMethod(context);
         if (DEBUG) {
             Log.d(LOGTAG, "IME: CurrentInputMethod=" + mCurrentInputMethod);
         }
 
         outAttrs.initialSelStart = mLastSelectionStart;
         outAttrs.initialSelEnd = mLastSelectionEnd;
-
-        if ((mIMEFlags & IME_FLAG_USER_ACTION) != 0) {
-            if ((context instanceof Activity) &&
-                    ActivityUtils.isFullScreen((Activity) context)) {
-                showSoftInputWithToolbar(false);
-            } else {
-                showSoftInputWithToolbar(true);
-            }
-        }
         return this;
     }
 
     private boolean replaceComposingSpanWithSelection() {
         final Editable content = getEditable();
         if (content == null) {
             return false;
         }
@@ -720,17 +585,17 @@ import java.lang.reflect.Proxy;
             return true;
         }
         return super.setSelection(start, end);
     }
 
     @Override
     public boolean sendKeyEvent(@NonNull KeyEvent event) {
         event = translateKey(event.getKeyCode(), event);
-        mEditableClient.sendKeyEvent(getView(), isInputActive(), event.getAction(), event);
+        mEditableClient.sendKeyEvent(getView(), event.getAction(), event);
         return false; // seems to always return false
     }
 
     private KeyEvent translateKey(final int keyCode, final @NonNull KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_ENTER:
                 if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
                         mIMEActionHint.equalsIgnoreCase("next")) {
@@ -767,127 +632,51 @@ import java.lang.reflect.Proxy;
                     Context viewContext = getView().getContext();
                     AudioManager am = (AudioManager)viewContext.getSystemService(Context.AUDIO_SERVICE);
                     am.dispatchMediaKeyEvent(event);
                 }
                 break;
         }
     }
 
-    @Override // SessionTextInput.InputConnectionClient
-    public synchronized boolean isInputActive() {
-        // Make sure this picks up PASSWORD state as well.
-        return mIMEState != IME_STATE_DISABLED;
-    }
-
     @Override // SessionTextInput.EditableListener
-    public void notifyIME(int type) {
+    public void notifyIME(final int type) {
         switch (type) {
-
             case NOTIFY_IME_OF_FOCUS:
                 // Showing/hiding vkb is done in notifyIMEContext
-                mFocused = true;
                 if (mBatchEditCount != 0) {
                     Log.w(LOGTAG, "resetting with mBatchEditCount = " + mBatchEditCount);
                     mBatchEditCount = 0;
                 }
-                // Do not reset mIMEState here; see comments in notifyIMEContext
-                restartInput(SessionTextInput.Delegate.RESTART_REASON_FOCUS);
                 break;
 
             case NOTIFY_IME_OF_BLUR:
-                // Showing/hiding vkb is done in notifyIMEContext
-                mFocused = false;
                 break;
 
-            case NOTIFY_IME_OPEN_VKB:
-                showSoftInputWithToolbar(false);
-                break;
-
-            case NOTIFY_IME_TO_COMMIT_COMPOSITION: {
-                // Gecko already committed its composition. However, Android keyboards
-                // have trouble dealing with us removing the composition manually on the
-                // Java side. Therefore, we keep the composition intact on the Java side.
-                // The text content should still be in-sync on both sides.
-                //
-                // Nevertheless, if we somehow lost the composition, we must force the
-                // keyboard to reset.
-                final Editable editable = getEditable();
-                if (editable == null) {
-                    break;
-                }
-                final Object[] spans = editable.getSpans(0, editable.length(), Object.class);
-                for (final Object span : spans) {
-                    if ((editable.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
-                        // Still have composition; no need to reset.
-                        return;
-                    }
-                }
-                // No longer have composition; perform reset.
-                restartInput(SessionTextInput.Delegate.RESTART_REASON_CONTENT_CHANGE);
-                break;
-            }
-
             default:
                 if (DEBUG) {
                     throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type);
                 }
                 break;
         }
     }
 
     @Override // SessionTextInput.EditableListener
-    public synchronized void notifyIMEContext(int state, final String typeHint,
+    public synchronized void notifyIMEContext(final int state, final String typeHint,
                                               final String modeHint, final String actionHint,
                                               final int flags) {
-        // For some input type we will use a widget to display the ui, for those we must not
-        // display the ime. We can display a widget for date and time types and, if the sdk version
-        // is 11 or greater, for datetime/month/week as well.
-        if (typeHint != null &&
-            (typeHint.equalsIgnoreCase("date") ||
-             typeHint.equalsIgnoreCase("time") ||
-             typeHint.equalsIgnoreCase("datetime") ||
-             typeHint.equalsIgnoreCase("month") ||
-             typeHint.equalsIgnoreCase("week") ||
-             typeHint.equalsIgnoreCase("datetime-local"))) {
-            state = IME_STATE_DISABLED;
-        }
-
         // mIMEState and the mIME*Hint fields should only be changed by notifyIMEContext,
         // and not reset anywhere else. Usually, notifyIMEContext is called right after a
         // focus or blur, so resetting mIMEState during the focus or blur seems harmless.
         // However, this behavior is not guaranteed. Gecko may call notifyIMEContext
         // independent of focus change; that is, a focus change may not be accompanied by
         // a notifyIMEContext call. So if we reset mIMEState inside focus, there may not
         // be another notifyIMEContext call to set mIMEState to a proper value (bug 829318)
         /* When IME is 'disabled', IME processing is disabled.
            In addition, the IME UI is hidden */
         mIMEState = state;
-        mIMETypeHint = (typeHint == null) ? "" : typeHint;
-        mIMEModeHint = (modeHint == null) ? "" : modeHint;
         mIMEActionHint = (actionHint == null) ? "" : actionHint;
-        mIMEFlags = flags;
 
         // These fields are reset here and will be updated when restartInput is called below
         mUpdateRequest = null;
         mCurrentInputMethod = "";
-
-        View v = getView();
-        if (v == null || !v.hasFocus()) {
-            // When using Find In Page, we can still receive notifyIMEContext calls due to the
-            // selection changing when highlighting. However in this case we don't want to reset/
-            // show/hide the keyboard because the find box has the focus and is taking input from
-            // the keyboard.
-            return;
-        }
-
-        // On focus, the notifyIMEContext call comes *before* the
-        // notifyIME(NOTIFY_IME_OF_FOCUS) call, but we need to call restartInput during
-        // notifyIME, so we skip restartInput here. On blur, the notifyIMEContext call
-        // comes *after* the notifyIME(NOTIFY_IME_OF_BLUR) call, and we need to call
-        // restartInput here.
-        if (mIMEState == IME_STATE_DISABLED || mFocused) {
-            restartInput(mIMEState == IME_STATE_DISABLED ?
-                         SessionTextInput.Delegate.RESTART_REASON_BLUR :
-                         SessionTextInput.Delegate.RESTART_REASON_CONTENT_CHANGE);
-        }
     }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -41,16 +41,19 @@ import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.NonNull;
 import android.support.annotation.StringDef;
 import android.util.Base64;
 import android.util.Log;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
 
 public class GeckoSession extends LayerSession
                           implements Parcelable {
     private static final String LOGTAG = "GeckoSession";
     private static final boolean DEBUG = false;
 
     // Type of changes given to onWindowChanged.
     // Window has been cleared due to the session being closed.
@@ -2913,9 +2916,99 @@ public class GeckoSession extends LayerS
          * @param uri The URI of the content requesting the permission.
          * @param video List of video sources, or null if not requesting video.
          * @param audio List of audio sources, or null if not requesting audio.
          * @param callback Callback interface.
          */
         void onMediaPermissionRequest(GeckoSession session, String uri, MediaSource[] video,
                                       MediaSource[] audio, MediaCallback callback);
     }
+
+    /**
+     * Interface that SessionTextInput uses for performing operations such as opening and closing
+     * the software keyboard. If the delegate is not set, these operations are forwarded to the
+     * system {@link android.view.inputmethod.InputMethodManager} automatically.
+     */
+    public interface TextInputDelegate {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({RESTART_REASON_FOCUS, RESTART_REASON_BLUR, RESTART_REASON_CONTENT_CHANGE})
+        @interface RestartReason {}
+        /** Restarting input due to an input field gaining focus. */
+        int RESTART_REASON_FOCUS = 0;
+        /** Restarting input due to an input field losing focus. */
+        int RESTART_REASON_BLUR = 1;
+        /**
+         * Restarting input due to the content of the input field changing. For example, the
+         * input field type may have changed, or the current composition may have been committed
+         * outside of the input method.
+         */
+        int RESTART_REASON_CONTENT_CHANGE = 2;
+
+        /**
+         * Reset the input method, and discard any existing states such as the current composition
+         * or current autocompletion. Because the current focused editor may have changed, as
+         * part of the reset, a custom input method would normally call {@link
+         * SessionTextInput#onCreateInputConnection} to update its knowledge of the focused editor.
+         * Note that {@code restartInput} should be used to detect changes in focus, rather than
+         * {@link #showSoftInput} or {@link #hideSoftInput}, because focus changes are not always
+         * accompanied by requests to show or hide the soft input. This method is always called,
+         * even in viewless mode.
+         *
+         * @param session Session instance.
+         * @param reason Reason for the reset.
+         */
+        void restartInput(@NonNull GeckoSession session, @RestartReason int reason);
+
+        /**
+         * Display the soft input. May be called consecutively, even if the soft input is
+         * already shown. This method is always called, even in viewless mode.
+         *
+         * @param session Session instance.
+         * @see #hideSoftInput
+         * */
+        void showSoftInput(@NonNull GeckoSession session);
+
+        /**
+         * Hide the soft input. May be called consecutively, even if the soft input is
+         * already hidden. This method is always called, even in viewless mode.
+         *
+         * @param session Session instance.
+         * @see #showSoftInput
+         * */
+        void hideSoftInput(@NonNull GeckoSession session);
+
+        /**
+         * Update the soft input on the current selection. This method is <i>not</i> called
+         * in viewless mode.
+         *
+         * @param session Session instance.
+         * @param selStart Start offset of the selection.
+         * @param selEnd End offset of the selection.
+         * @param compositionStart Composition start offset, or -1 if there is no composition.
+         * @param compositionEnd Composition end offset, or -1 if there is no composition.
+         */
+        void updateSelection(@NonNull GeckoSession session, int selStart, int selEnd,
+                             int compositionStart, int compositionEnd);
+
+        /**
+         * Update the soft input on the current extracted text, as requested through
+         * {@link android.view.inputmethod.InputConnection#getExtractedText}.
+         * Consequently, this method is <i>not</i> called in viewless mode.
+         *
+         * @param session Session instance.
+         * @param request The extract text request.
+         * @param text The extracted text.
+         */
+        void updateExtractedText(@NonNull GeckoSession session,
+                                 @NonNull ExtractedTextRequest request,
+                                 @NonNull ExtractedText text);
+
+        /**
+         * Update the cursor-anchor information as requested through
+         * {@link android.view.inputmethod.InputConnection#requestCursorUpdates}.
+         * Consequently, this method is <i>not</i> called in viewless mode.
+         *
+         * @param session Session instance.
+         * @param info Cursor-anchor information.
+         */
+        void updateCursorAnchorInfo(@NonNull GeckoSession session, @NonNull CursorAnchorInfo info);
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
@@ -5,146 +5,72 @@
 
 package org.mozilla.geckoview;
 
 import org.mozilla.gecko.InputMethods;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.GeckoEditableChild;
 import org.mozilla.gecko.IGeckoEditableParent;
 import org.mozilla.gecko.NativeQueue;
+import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.TargetApi;
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.RectF;
 import android.os.Handler;
-import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.Editable;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
- * SessionTextInput handles text input for GeckoSession through key events or input
- * methods. It is typically used to implement certain methods in View such as {@code
- * onCreateInputConnection()}, by forwarding such calls to corresponding methods in
- * SessionTextInput.
+ * {@code SessionTextInput} handles text input for {@code GeckoSession} through key events or input
+ * methods. It is typically used to implement certain methods in {@link android.view.View}
+ * such as {@link android.view.View#onCreateInputConnection}, by forwarding such calls to
+ * corresponding methods in {@code SessionTextInput}.
+ * <p>
+ * For full functionality, {@code SessionTextInput} requires a {@link android.view.View} to be set
+ * first through {@link #setView}. When a {@link android.view.View} is not set or set to null,
+ * {@code SessionTextInput} will operate in a reduced functionality mode. See {@link
+ * #onCreateInputConnection} and methods in {@link GeckoSession.TextInputDelegate} for changes in
+ * behavior in this viewless mode.
  */
 public final class SessionTextInput {
     /* package */ static final String LOGTAG = "GeckoSessionTextInput";
 
-    /**
-     * Interface that SessionTextInput uses for performing operations such as opening and closing
-     * the software keyboard. If the delegate is not set, these operations are forwarded to the
-     * system InputMethodManager automatically.
-     */
-    public interface Delegate {
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({RESTART_REASON_FOCUS, RESTART_REASON_BLUR, RESTART_REASON_CONTENT_CHANGE})
-        @interface RestartReason {}
-        /** Restarting input due to an input field gaining focus. */
-        int RESTART_REASON_FOCUS = 0;
-        /** Restarting input due to an input field losing focus. */
-        int RESTART_REASON_BLUR = 1;
-        /**
-         * Restarting input due to the content of the input field changing. For example, the
-         * input field type may have changed, or the current composition may have been committed
-         * outside of the input method.
-         */
-        int RESTART_REASON_CONTENT_CHANGE = 2;
-
-        /**
-         * Reset the input method, and discard any existing states such as the current composition
-         * or current autocompletion. Because the current focused editor may have changed, as
-         * part of the reset, a custom input method would normally call {@link
-         * #onCreateInputConnection} to update its knowledge of the focused editor. Note that
-         * {@code restartInput} should be used to detect changes in focus, rather than {@link
-         * #showSoftInput} or {@link #hideSoftInput}, because focus changes are not always
-         * accompanied by requests to show or hide the soft input.
-         *
-         * @param reason Reason for the reset.
-         */
-        void restartInput(@RestartReason int reason);
-
-        /**
-         * Display the soft input.
-         *
-         * @see #hideSoftInput
-         * */
-        void showSoftInput();
-
-        /**
-         * Hide the soft input.
-         *
-         * @see #showSoftInput
-         * */
-        void hideSoftInput();
-
-        /**
-         * Update the soft input on the current selection.
-         *
-         * @param selStart Start offset of the selection.
-         * @param selEnd End offset of the selection.
-         * @param compositionStart Composition start offset, or -1 if there is no composition.
-         * @param compositionEnd Composition end offset, or -1 if there is no composition.
-         */
-        void updateSelection(int selStart, int selEnd, int compositionStart, int compositionEnd);
-
-        /**
-         * Update the soft input on the current extracted text as requested through
-         * InputConnection.getExtractText.
-         *
-         * @param request The extract text request.
-         * @param text The extracted text.
-         */
-        void updateExtractedText(@NonNull ExtractedTextRequest request,
-                                 @NonNull ExtractedText text);
-
-        /**
-         * Update the cursor-anchor information as requested through
-         * InputConnection.requestCursorUpdates.
-         *
-         * @param info Cursor-anchor information.
-         */
-        void updateCursorAnchorInfo(@NonNull CursorAnchorInfo info);
-    }
-
     // Interface to access GeckoInputConnection from SessionTextInput.
     /* package */ interface InputConnectionClient {
         View getView();
         Handler getHandler(Handler defHandler);
         InputConnection onCreateInputConnection(EditorInfo attrs);
-        boolean isInputActive();
     }
 
     // Interface to access GeckoEditable from GeckoInputConnection.
     /* package */ interface EditableClient {
         // The following value is used by requestCursorUpdates
         // ONE_SHOT calls updateCompositionRects() after getting current composing
         // character rects.
         @WrapForJNI final int ONE_SHOT = 1;
         // START_MONITOR start the monitor for composing character rects.  If is is
         // updaed,  call updateCompositionRects()
         @WrapForJNI final int START_MONITOR = 2;
         // ENDT_MONITOR stops the monitor for composing character rects.
         @WrapForJNI final int END_MONITOR = 3;
 
-        void sendKeyEvent(@Nullable View view, boolean inputActive, int action,
-                          @NonNull KeyEvent event);
+        void sendKeyEvent(@Nullable View view, int action, @NonNull KeyEvent event);
         Editable getEditable();
         void setBatchMode(boolean isBatchMode);
         Handler setInputConnectionHandler(@NonNull Handler handler);
         void postToInputConnection(@NonNull Runnable runnable);
         void requestCursorUpdates(int requestMode);
     }
 
     // Interface to access GeckoInputConnection from GeckoEditable.
@@ -171,29 +97,42 @@ public final class SessionTextInput {
         void notifyIMEContext(int state, String typeHint, String modeHint,
                               String actionHint, int flag);
         void onSelectionChange();
         void onTextChange();
         void onDefaultKeyEvent(KeyEvent event);
         void updateCompositionRects(final RectF[] aRects);
     }
 
-    private final class DefaultDelegate implements Delegate {
+    private static final class DefaultDelegate implements GeckoSession.TextInputDelegate {
+        public static final DefaultDelegate INSTANCE = new DefaultDelegate();
+
         private InputMethodManager getInputMethodManager(@Nullable final View view) {
             if (view == null) {
                 return null;
             }
             return (InputMethodManager) view.getContext()
                                             .getSystemService(Context.INPUT_METHOD_SERVICE);
         }
 
         @Override
-        public void restartInput(int reason) {
+        public void restartInput(@NonNull final GeckoSession session, final int reason) {
             ThreadUtils.assertOnUiThread();
-            final View view = getView();
+            final View view = session.getTextInput().getView();
+
+            if (reason == RESTART_REASON_FOCUS) {
+                final Context context = (view != null) ? view.getContext() : null;
+                if ((context instanceof Activity) &&
+                        !ActivityUtils.isFullScreen((Activity) context)) {
+                    // Bug 1293463: show the toolbar to prevent spoofing.
+                    session.getDynamicToolbarAnimator()
+                           .showToolbar(/* immediately */ true);
+                }
+            }
+
             final InputMethodManager imm = getInputMethodManager(view);
             if (imm == null) {
                 return;
             }
 
             // InputMethodManager has internal logic to detect if we are restarting input
             // in an already focused View, which is the case here because all content text
             // fields are inside one LayerView. When this happens, InputMethodManager will
@@ -213,86 +152,91 @@ public final class SessionTextInput {
             try {
                 imm.restartInput(view);
             } catch (RuntimeException e) {
                 Log.e(LOGTAG, "Error restarting input", e);
             }
         }
 
         @Override
-        public void showSoftInput() {
+        public void showSoftInput(@NonNull final GeckoSession session) {
             ThreadUtils.assertOnUiThread();
-            final View view = getView();
+            final View view = session.getTextInput().getView();
             final InputMethodManager imm = getInputMethodManager(view);
             if (imm != null) {
                 if (view.hasFocus() && !imm.isActive(view)) {
                     // Marshmallow workaround: The view has focus but it is not the active
                     // view for the input method. (Bug 1211848)
                     view.clearFocus();
                     view.requestFocus();
                 }
                 imm.showSoftInput(view, 0);
             }
         }
 
         @Override
-        public void hideSoftInput() {
+        public void hideSoftInput(@NonNull final GeckoSession session) {
             ThreadUtils.assertOnUiThread();
-            final View view = getView();
+            final View view = session.getTextInput().getView();
             final InputMethodManager imm = getInputMethodManager(view);
             if (imm != null) {
                 imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
             }
         }
 
         @Override
-        public void updateSelection(final int selStart, final int selEnd,
+        public void updateSelection(@NonNull final GeckoSession session,
+                                    final int selStart, final int selEnd,
                                     final int compositionStart, final int compositionEnd) {
             ThreadUtils.assertOnUiThread();
-            final View view = getView();
+            final View view = session.getTextInput().getView();
             final InputMethodManager imm = getInputMethodManager(view);
             if (imm != null) {
                 imm.updateSelection(view, selStart, selEnd, compositionStart, compositionEnd);
             }
         }
 
         @Override
-        public void updateExtractedText(@NonNull final ExtractedTextRequest request,
+        public void updateExtractedText(@NonNull final GeckoSession session,
+                                        @NonNull final ExtractedTextRequest request,
                                         @NonNull final ExtractedText text) {
             ThreadUtils.assertOnUiThread();
-            final View view = getView();
+            final View view = session.getTextInput().getView();
             final InputMethodManager imm = getInputMethodManager(view);
             if (imm != null) {
                 imm.updateExtractedText(view, request.token, text);
             }
         }
 
         @TargetApi(21)
         @Override
-        public void updateCursorAnchorInfo(@NonNull final CursorAnchorInfo info) {
+        public void updateCursorAnchorInfo(@NonNull final GeckoSession session,
+                                           @NonNull final CursorAnchorInfo info) {
             ThreadUtils.assertOnUiThread();
-            final View view = getView();
+            final View view = session.getTextInput().getView();
             final InputMethodManager imm = getInputMethodManager(view);
             if (imm != null) {
                 imm.updateCursorAnchorInfo(view, info);
             }
         }
     }
 
     private final GeckoSession mSession;
     private final NativeQueue mQueue;
-    private final GeckoEditable mEditable = new GeckoEditable();
-    private final GeckoEditableChild mEditableChild = new GeckoEditableChild(mEditable);
+    private final GeckoEditable mEditable;
+    private final GeckoEditableChild mEditableChild;
     private InputConnectionClient mInputConnection;
-    private Delegate mDelegate;
+    private GeckoSession.TextInputDelegate mDelegate;
 
     /* package */ SessionTextInput(final @NonNull GeckoSession session,
                                    final @NonNull NativeQueue queue) {
         mSession = session;
         mQueue = queue;
+        mEditable = new GeckoEditable(session);
+        mEditableChild = new GeckoEditableChild(mEditable);
         mEditable.setDefaultEditableChild(mEditableChild);
     }
 
     /* package */ void onWindowChanged(final GeckoSession.Window window) {
         if (mQueue.isReady()) {
             window.attachEditable(mEditable, mEditableChild);
         } else {
             mQueue.queueUntilReady(window, "attachEditable",
@@ -323,147 +267,143 @@ public final class SessionTextInput {
         // May be called on any thread.
         if (mInputConnection != null) {
             return mInputConnection.getHandler(defHandler);
         }
         return defHandler;
     }
 
     /**
-     * Get the current View for text input.
+     * Get the current {@link android.view.View} for text input.
      *
      * @return Current text input View or null if not set.
      * @see #setView(View)
      */
     public @Nullable View getView() {
         ThreadUtils.assertOnUiThread();
         return mInputConnection != null ? mInputConnection.getView() : null;
     }
 
     /**
-     * Set the View for text input. The current View is used to interact with the system
-     * input method manager and to display certain text input UI elements.
+     * Set the current {@link android.view.View} for text input. The {@link android.view.View}
+     * is used to interact with the system input method manager and to display certain text input
+     * UI elements. See the {@code SessionTextInput} class documentation for information on
+     * viewless mode, when the current {@link android.view.View} is not set or set to null.
      *
      * @param view Text input View or null to clear current View.
+     * @see #getView()
      */
     public synchronized void setView(final @Nullable View view) {
         ThreadUtils.assertOnUiThread();
 
         if (view == null) {
             mInputConnection = null;
         } else if (mInputConnection == null || mInputConnection.getView() != view) {
             mInputConnection = GeckoInputConnection.create(mSession, view, mEditable);
         }
         mEditable.setListener((EditableListener) mInputConnection);
     }
 
     /**
-     * Get an InputConnection instance. For full functionality, call {@link
-     * #setView(View)} first before calling this method.
+     * Get an {@link android.view.inputmethod.InputConnection} instance. In viewless mode,
+     * this method still fills out the {@link android.view.inputmethod.EditorInfo} object,
+     * but the return value will always be null.
      *
      * @param attrs EditorInfo instance to be filled on return.
-     * @return InputConnection instance or null if input method is not active.
+     * @return InputConnection instance, or null if there is no active input
+     *         (or if in viewless mode).
      */
     public synchronized @Nullable InputConnection onCreateInputConnection(
             final @NonNull EditorInfo attrs) {
         // May be called on any thread.
+        mEditable.onCreateInputConnection(attrs);
+
         if (!mQueue.isReady() || mInputConnection == null) {
             return null;
         }
         return mInputConnection.onCreateInputConnection(attrs);
     }
 
     /**
      * Process a KeyEvent as a pre-IME event.
      *
      * @param keyCode Key code.
      * @param event KeyEvent instance.
      * @return True if the event was handled.
      */
     public boolean onKeyPreIme(final int keyCode, final @NonNull KeyEvent event) {
         ThreadUtils.assertOnUiThread();
-        return mEditable.onKeyPreIme(getView(), isInputActive(), keyCode, event);
+        return mEditable.onKeyPreIme(getView(), keyCode, event);
     }
 
     /**
      * Process a KeyEvent as a key-down event.
      *
      * @param keyCode Key code.
      * @param event KeyEvent instance.
      * @return True if the event was handled.
      */
     public boolean onKeyDown(final int keyCode, final @NonNull KeyEvent event) {
         ThreadUtils.assertOnUiThread();
-        return mEditable.onKeyDown(getView(), isInputActive(), keyCode, event);
+        return mEditable.onKeyDown(getView(), keyCode, event);
     }
 
     /**
      * Process a KeyEvent as a key-up event.
      *
      * @param keyCode Key code.
      * @param event KeyEvent instance.
      * @return True if the event was handled.
      */
     public boolean onKeyUp(final int keyCode, final @NonNull KeyEvent event) {
         ThreadUtils.assertOnUiThread();
-        return mEditable.onKeyUp(getView(), isInputActive(), keyCode, event);
+        return mEditable.onKeyUp(getView(), keyCode, event);
     }
 
     /**
      * Process a KeyEvent as a long-press event.
      *
      * @param keyCode Key code.
      * @param event KeyEvent instance.
      * @return True if the event was handled.
      */
     public boolean onKeyLongPress(final int keyCode, final @NonNull KeyEvent event) {
         ThreadUtils.assertOnUiThread();
-        return mEditable.onKeyLongPress(getView(), isInputActive(), keyCode, event);
+        return mEditable.onKeyLongPress(getView(), keyCode, event);
     }
 
     /**
      * Process a KeyEvent as a multiple-press event.
      *
      * @param keyCode Key code.
      * @param repeatCount Key repeat count.
      * @param event KeyEvent instance.
      * @return True if the event was handled.
      */
     public boolean onKeyMultiple(final int keyCode, final int repeatCount,
                                  final @NonNull KeyEvent event) {
         ThreadUtils.assertOnUiThread();
-        return mEditable.onKeyMultiple(getView(), isInputActive(), keyCode, repeatCount, event);
-    }
-
-    /**
-     * Return whether there is an active input connection, usually as a result of a
-     * focused input field.
-     *
-     * @return True if input is active.
-     */
-    public boolean isInputActive() {
-        ThreadUtils.assertOnUiThread();
-        return mInputConnection != null && mInputConnection.isInputActive();
+        return mEditable.onKeyMultiple(getView(), keyCode, repeatCount, event);
     }
 
     /**
      * Set the current text input delegate.
      *
-     * @param delegate Delegate instance or null to restore to default.
+     * @param delegate TextInputDelegate instance or null to restore to default.
      */
-    public void setDelegate(@Nullable final Delegate delegate) {
+    public void setDelegate(@Nullable final GeckoSession.TextInputDelegate delegate) {
         ThreadUtils.assertOnUiThread();
         mDelegate = delegate;
     }
 
     /**
      * Get the current text input delegate.
      *
-     * @return Delegate instance or a default instance if no delegate has been set.
+     * @return TextInputDelegate instance or a default instance if no delegate has been set.
      */
-    public Delegate getDelegate() {
+    public GeckoSession.TextInputDelegate getDelegate() {
         ThreadUtils.assertOnUiThread();
         if (mDelegate == null) {
-            mDelegate = new DefaultDelegate();
+            mDelegate = DefaultDelegate.INSTANCE;
         }
         return mDelegate;
     }
 }
--- a/mobile/android/installer/allowed-dupes.mn
+++ b/mobile/android/installer/allowed-dupes.mn
@@ -43,12 +43,8 @@ res/table-remove-column-active.gif
 res/table-remove-column-hover.gif
 res/table-remove-column.gif
 res/table-remove-row-active.gif
 res/table-remove-row-hover.gif
 res/table-remove-row.gif
 res/multilocale.txt
 modules/commonjs/index.js
 update.locale
-chrome/chrome/searchplugins/bolcom-fy-NL.xml
-chrome/chrome/searchplugins/bolcom-nl.xml
-chrome/chrome/searchplugins/ddg.xml
-chrome/chrome/searchplugins/duckduckgo.xml
--- a/mobile/locales/Makefile.in
+++ b/mobile/locales/Makefile.in
@@ -1,30 +1,144 @@
 # -*- makefile -*-
 # 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/.
 
+#####################################################################################
+# Dependency build overhead:
+#   o always create/update/hard link targets boomkarks.json & searchplugins
+#   o latest symlink will be correct for the current locale / local build.
+#   o logic is essentially FORCE for language packs w/o all the build overhead
+#   o phase 2: replace hard links with a user function able to derive path
+#     based on current locale.
+#####################################################################################
+
 include $(topsrcdir)/config/config.mk
 
 USE_AUTOTARGETS_MK=1
 include $(topsrcdir)/config/makefiles/makeutils.mk
 
+# Separate items of contention
+tgt-gendir = .deps/generated_$(AB_CD)
+
+GENERATED_DIRS += .deps
+
 $(call errorIfEmpty,MOZ_BRANDING_DIRECTORY)
 SUBMAKEFILES += \
         $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/Makefile \
         $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/Makefile \
         $(NULL)
 
+###########################################################################
+# Default target, preserve existing functionality for:
+#    gmake -C $obj/mobile/locales
+###########################################################################
+search-jar-default: search-jar
+
+
+###########################################################################
+## Searchlist plugin config
+plugin-file-array = \
+  $(srcdir)/search/list.json \
+  $(NULL)
+
+###########################################################################
+plugin_file    = $(firstword $(plugin-file-array))
+plugin-file-ts = $(tgt-gendir)/$(subst $(topsrcdir)/,$(NULL),$(plugin_file)).ts
+
+GARBAGE += $(plugin-file-ts)
+# ---------------------------------------------------------------------------
+# plugin-file-ts track searchlist file used ($path/list.json)
+# and time when the file was last modified.
+###########################################################################
+plugin-file-ts-preqs = \
+  $(call mkdir_deps,$(dir $(plugin-file-ts))) \
+  $(plugin_file) \
+  $(NULL)
+
+###########################################################################
+# Detect locale changes.  Force stale deps when searchlist file
+# or content has changed.
+$(plugin-file-ts): $(plugin-file-ts-preqs)
+	@touch $@
+
+
+###########################################################################
+search-jar-common = tmp-search.jar.mn
+search-jar        = $(tgt-gendir)/$(search-jar-common)
+search-jar-ts     = $(search-jar).ts
+
+GARBAGE += $(search-jar) $(search-jar-ts) $(search-jar-common)
+# ---------------------------------------------------------------------------
+# search-jar contains a list of providers for the search plugin
+###########################################################################
+SEARCH_PLUGINS := $(shell $(call py_action,output_searchplugins_list,$(srcdir)/search/list.json $(AB_CD)))
+$(call errorIfEmpty,SEARCH_PLUGINS)
+
+search-jar-preqs = \
+  $(plugin-file-ts) \
+  $(if $(IS_LANGUAGE_REPACK),FORCE) \
+  $(NULL)
+
+.PHONY: search-jar
+search-jar: $(search-jar)
+$(search-jar): $(search-jar-preqs)
+	@echo '\nGenerating: search-jar'
+	printf '$(AB_CD).jar:' > $@
+	ln -fn $@ .
+	printf '$(foreach plugin,$(SEARCH_PLUGINS),$(subst __PLUGIN_SUBST__,$(plugin), \n locale/$(AB_CD)/browser/searchplugins/__PLUGIN_SUBST__.xml (__PLUGIN_SUBST__.xml)))' >>  $@
+	printf '\n locale/$(AB_CD)/browser/searchplugins/list.json (list.json)' >> $@
+	@echo   >> $@
+
+###################
+search-dir-deps = \
+  $(plugin-file) \
+  $(dir-chrome) \
+  $(NULL)
+
+search-preqs =\
+  $(call mkdir_deps,$(dir $(search-jar-ts))) \
+  $(call mkdir_deps,$(FINAL_TARGET)/chrome) \
+  $(search-jar) \
+  $(search-dir-deps) \
+  $(if $(IS_LANGUAGE_REPACK),FORCE) \
+  $(GLOBAL_DEPS) \
+  $(NULL)
+
+.PHONY: searchplugins
+searchplugins: $(search-preqs)
+	$(call py_action,generate_searchjson,$(srcdir)/search/list.json $(AB_CD) $(tgt-gendir)/list.json)
+	$(call py_action,jar_maker,\
+          $(QUIET) -d $(FINAL_TARGET) \
+          -s $(topsrcdir)/$(relativesrcdir)/searchplugins \
+          -s $(tgt-gendir) \
+          $(MAKE_JARS_FLAGS) $(search-jar))
+	$(TOUCH) $@
+
 include $(topsrcdir)/config/rules.mk
 
+
+#############
+libs-preqs =\
+  $(call mkdir-deps,$(DIST)/install) \
+  $(NULL)
+
 libs-%: AB_CD=$*
-libs-%:
-	$(NSINSTALL) -D $(DIST)/install
+libs-%: $(libs-preqs)
+	$(display-deps)
 	@$(MAKE) -C $(DEPTH)/toolkit/locales libs-$*
+	@$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/pref
 	@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$*
 
 # Tailored target to just add the chrome processing for multi-locale builds
 chrome-%: AB_CD=$*
 chrome-%:
+	$(display-deps)
+	@$(MAKE) -B searchplugins AB_CD=$*
 	@$(MAKE) chrome AB_CD=$*
 	@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$*
+
+NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD))
+
+
+export:: searchplugins
rename from mobile/android/components/search/searchplugins/list.json
rename to mobile/locales/search/list.json
rename from mobile/android/components/search/searchplugins/amazon-au.xml
rename to mobile/locales/searchplugins/amazon-au.xml
rename from mobile/android/components/search/searchplugins/amazon-br.xml
rename to mobile/locales/searchplugins/amazon-br.xml
rename from mobile/android/components/search/searchplugins/amazon-ca.xml
rename to mobile/locales/searchplugins/amazon-ca.xml
rename from mobile/android/components/search/searchplugins/amazon-co-uk.xml
rename to mobile/locales/searchplugins/amazon-co-uk.xml
rename from mobile/android/components/search/searchplugins/amazon-de.xml
rename to mobile/locales/searchplugins/amazon-de.xml
rename from mobile/android/components/search/searchplugins/amazon-fr.xml
rename to mobile/locales/searchplugins/amazon-fr.xml
rename from mobile/android/components/search/searchplugins/amazon-in.xml
rename to mobile/locales/searchplugins/amazon-in.xml
rename from mobile/android/components/search/searchplugins/amazon-it.xml
rename to mobile/locales/searchplugins/amazon-it.xml
rename from mobile/android/components/search/searchplugins/amazon-jp.xml
rename to mobile/locales/searchplugins/amazon-jp.xml
rename from mobile/android/components/search/searchplugins/amazon-mx.xml
rename to mobile/locales/searchplugins/amazon-mx.xml
rename from mobile/android/components/search/searchplugins/amazon-nl.xml
rename to mobile/locales/searchplugins/amazon-nl.xml
rename from mobile/android/components/search/searchplugins/amazondotcom.xml
rename to mobile/locales/searchplugins/amazondotcom.xml
rename from mobile/android/components/search/searchplugins/azerdict.xml
rename to mobile/locales/searchplugins/azerdict.xml
rename from mobile/android/components/search/searchplugins/azet-sk.xml
rename to mobile/locales/searchplugins/azet-sk.xml
rename from mobile/android/components/search/searchplugins/baidu.xml
rename to mobile/locales/searchplugins/baidu.xml
rename from mobile/android/components/search/searchplugins/bing.xml
rename to mobile/locales/searchplugins/bing.xml
rename from mobile/android/components/search/searchplugins/bolcom-fy-NL.xml
rename to mobile/locales/searchplugins/bolcom-fy-NL.xml
rename from mobile/android/components/search/searchplugins/bolcom-nl.xml
rename to mobile/locales/searchplugins/bolcom-nl.xml
rename from mobile/android/components/search/searchplugins/ceneje.xml
rename to mobile/locales/searchplugins/ceneje.xml
rename from mobile/android/components/search/searchplugins/coccoc.xml
rename to mobile/locales/searchplugins/coccoc.xml
rename from mobile/android/components/search/searchplugins/danawa-kr.xml
rename to mobile/locales/searchplugins/danawa-kr.xml
rename from mobile/android/components/search/searchplugins/daum-kr.xml
rename to mobile/locales/searchplugins/daum-kr.xml
rename from mobile/android/components/search/searchplugins/ddg.xml
rename to mobile/locales/searchplugins/ddg.xml
rename from mobile/android/components/search/searchplugins/diec2.xml
rename to mobile/locales/searchplugins/diec2.xml
rename from mobile/android/components/search/searchplugins/drae.xml
rename to mobile/locales/searchplugins/drae.xml
rename from mobile/android/components/search/searchplugins/duckduckgo.xml
rename to mobile/locales/searchplugins/duckduckgo.xml
rename from mobile/android/components/search/searchplugins/elebila.xml
rename to mobile/locales/searchplugins/elebila.xml
rename from mobile/android/components/search/searchplugins/faclair-beag.xml
rename to mobile/locales/searchplugins/faclair-beag.xml
rename from mobile/android/components/search/searchplugins/google-2018.xml
rename to mobile/locales/searchplugins/google-2018.xml
rename from mobile/android/components/search/searchplugins/google.xml
rename to mobile/locales/searchplugins/google.xml
rename from mobile/android/components/search/searchplugins/gulesider-mobile-NO.xml
rename to mobile/locales/searchplugins/gulesider-mobile-NO.xml
rename from mobile/android/components/search/searchplugins/heureka-cz.xml
rename to mobile/locales/searchplugins/heureka-cz.xml
rename from mobile/android/components/search/searchplugins/hotline-ua.xml
rename to mobile/locales/searchplugins/hotline-ua.xml
rename from mobile/android/components/search/searchplugins/leit-is.xml
rename to mobile/locales/searchplugins/leit-is.xml
rename from mobile/android/components/search/searchplugins/leo_ende_de.xml
rename to mobile/locales/searchplugins/leo_ende_de.xml
rename from mobile/android/components/search/searchplugins/list-am.xml
rename to mobile/locales/searchplugins/list-am.xml
rename from mobile/android/components/search/searchplugins/mapy-cz.xml
rename to mobile/locales/searchplugins/mapy-cz.xml
rename from mobile/android/components/search/searchplugins/mercadolibre-ar.xml
rename to mobile/locales/searchplugins/mercadolibre-ar.xml
rename from mobile/android/components/search/searchplugins/mercadolibre-cl.xml
rename to mobile/locales/searchplugins/mercadolibre-cl.xml
rename from mobile/android/components/search/searchplugins/mercadolibre-mx.xml
rename to mobile/locales/searchplugins/mercadolibre-mx.xml
rename from mobile/android/components/search/searchplugins/naver-kr.xml
rename to mobile/locales/searchplugins/naver-kr.xml
rename from mobile/android/components/search/searchplugins/odpiralni.xml
rename to mobile/locales/searchplugins/odpiralni.xml
rename from mobile/android/components/search/searchplugins/pazaruvaj.xml
rename to mobile/locales/searchplugins/pazaruvaj.xml
rename from mobile/android/components/search/searchplugins/pledarigrond.xml
rename to mobile/locales/searchplugins/pledarigrond.xml
rename from mobile/android/components/search/searchplugins/prisjakt-sv-SE.xml
rename to mobile/locales/searchplugins/prisjakt-sv-SE.xml
rename from mobile/android/components/search/searchplugins/qwant.xml
rename to mobile/locales/searchplugins/qwant.xml
rename from mobile/android/components/search/searchplugins/rediff.xml
rename to mobile/locales/searchplugins/rediff.xml
rename from mobile/android/components/search/searchplugins/reta-vortaro.xml
rename to mobile/locales/searchplugins/reta-vortaro.xml
rename from mobile/android/components/search/searchplugins/salidzinilv.xml
rename to mobile/locales/searchplugins/salidzinilv.xml
rename from mobile/android/components/search/searchplugins/seznam-cz.xml
rename to mobile/locales/searchplugins/seznam-cz.xml
rename from mobile/android/components/search/searchplugins/skroutz.xml
rename to mobile/locales/searchplugins/skroutz.xml
rename from mobile/android/components/search/searchplugins/slovnik-sk.xml
rename to mobile/locales/searchplugins/slovnik-sk.xml
rename from mobile/android/components/search/searchplugins/sslv.xml
rename to mobile/locales/searchplugins/sslv.xml
rename from mobile/android/components/search/searchplugins/sztaki-en-hu.xml
rename to mobile/locales/searchplugins/sztaki-en-hu.xml
rename from mobile/android/components/search/searchplugins/taobao.xml
rename to mobile/locales/searchplugins/taobao.xml
rename from mobile/android/components/search/searchplugins/tearma.xml
rename to mobile/locales/searchplugins/tearma.xml
rename from mobile/android/components/search/searchplugins/twitter-ja.xml
rename to mobile/locales/searchplugins/twitter-ja.xml
rename from mobile/android/components/search/searchplugins/twitter.xml
rename to mobile/locales/searchplugins/twitter.xml
rename from mobile/android/components/search/searchplugins/vatera.xml
rename to mobile/locales/searchplugins/vatera.xml
rename from mobile/android/components/search/searchplugins/wikipedia-NN.xml
rename to mobile/locales/searchplugins/wikipedia-NN.xml
rename from mobile/android/components/search/searchplugins/wikipedia-NO.xml
rename to mobile/locales/searchplugins/wikipedia-NO.xml
rename from mobile/android/components/search/searchplugins/wikipedia-an.xml
rename to mobile/locales/searchplugins/wikipedia-an.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ar.xml
rename to mobile/locales/searchplugins/wikipedia-ar.xml
rename from mobile/android/components/search/searchplugins/wikipedia-as.xml
rename to mobile/locales/searchplugins/wikipedia-as.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ast.xml
rename to mobile/locales/searchplugins/wikipedia-ast.xml
rename from mobile/android/components/search/searchplugins/wikipedia-az.xml
rename to mobile/locales/searchplugins/wikipedia-az.xml
rename from mobile/android/components/search/searchplugins/wikipedia-be.xml
rename to mobile/locales/searchplugins/wikipedia-be.xml
rename from mobile/android/components/search/searchplugins/wikipedia-bg.xml
rename to mobile/locales/searchplugins/wikipedia-bg.xml
rename from mobile/android/components/search/searchplugins/wikipedia-bn.xml
rename to mobile/locales/searchplugins/wikipedia-bn.xml
rename from mobile/android/components/search/searchplugins/wikipedia-br.xml
rename to mobile/locales/searchplugins/wikipedia-br.xml
rename from mobile/android/components/search/searchplugins/wikipedia-bs.xml
rename to mobile/locales/searchplugins/wikipedia-bs.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ca.xml
rename to mobile/locales/searchplugins/wikipedia-ca.xml
rename from mobile/android/components/search/searchplugins/wikipedia-cy.xml
rename to mobile/locales/searchplugins/wikipedia-cy.xml
rename from mobile/android/components/search/searchplugins/wikipedia-cz.xml
rename to mobile/locales/searchplugins/wikipedia-cz.xml
rename from mobile/android/components/search/searchplugins/wikipedia-da.xml
rename to mobile/locales/searchplugins/wikipedia-da.xml
rename from mobile/android/components/search/searchplugins/wikipedia-de.xml
rename to mobile/locales/searchplugins/wikipedia-de.xml
rename from mobile/android/components/search/searchplugins/wikipedia-dsb.xml
rename to mobile/locales/searchplugins/wikipedia-dsb.xml
rename from mobile/android/components/search/searchplugins/wikipedia-el.xml
rename to mobile/locales/searchplugins/wikipedia-el.xml
rename from mobile/android/components/search/searchplugins/wikipedia-eo.xml
rename to mobile/locales/searchplugins/wikipedia-eo.xml
rename from mobile/android/components/search/searchplugins/wikipedia-es.xml
rename to mobile/locales/searchplugins/wikipedia-es.xml
rename from mobile/android/components/search/searchplugins/wikipedia-et.xml
rename to mobile/locales/searchplugins/wikipedia-et.xml
rename from mobile/android/components/search/searchplugins/wikipedia-eu.xml
rename to mobile/locales/searchplugins/wikipedia-eu.xml
rename from mobile/android/components/search/searchplugins/wikipedia-fa.xml
rename to mobile/locales/searchplugins/wikipedia-fa.xml
rename from mobile/android/components/search/searchplugins/wikipedia-fi.xml
rename to mobile/locales/searchplugins/wikipedia-fi.xml
rename from mobile/android/components/search/searchplugins/wikipedia-fr.xml
rename to mobile/locales/searchplugins/wikipedia-fr.xml
rename from mobile/android/components/search/searchplugins/wikipedia-fy-NL.xml
rename to mobile/locales/searchplugins/wikipedia-fy-NL.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ga-IE.xml
rename to mobile/locales/searchplugins/wikipedia-ga-IE.xml
rename from mobile/android/components/search/searchplugins/wikipedia-gd.xml
rename to mobile/locales/searchplugins/wikipedia-gd.xml
rename from mobile/android/components/search/searchplugins/wikipedia-gl.xml
rename to mobile/locales/searchplugins/wikipedia-gl.xml
rename from mobile/android/components/search/searchplugins/wikipedia-gn.xml
rename to mobile/locales/searchplugins/wikipedia-gn.xml
rename from mobile/android/components/search/searchplugins/wikipedia-gu.xml
rename to mobile/locales/searchplugins/wikipedia-gu.xml
rename from mobile/android/components/search/searchplugins/wikipedia-he.xml
rename to mobile/locales/searchplugins/wikipedia-he.xml
rename from mobile/android/components/search/searchplugins/wikipedia-hi.xml
rename to mobile/locales/searchplugins/wikipedia-hi.xml
rename from mobile/android/components/search/searchplugins/wikipedia-hr.xml
rename to mobile/locales/searchplugins/wikipedia-hr.xml
rename from mobile/android/components/search/searchplugins/wikipedia-hsb.xml
rename to mobile/locales/searchplugins/wikipedia-hsb.xml
rename from mobile/android/components/search/searchplugins/wikipedia-hu.xml
rename to mobile/locales/searchplugins/wikipedia-hu.xml
rename from mobile/android/components/search/searchplugins/wikipedia-hy-AM.xml
rename to mobile/locales/searchplugins/wikipedia-hy-AM.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ia.xml
rename to mobile/locales/searchplugins/wikipedia-ia.xml
rename from mobile/android/components/search/searchplugins/wikipedia-id.xml
rename to mobile/locales/searchplugins/wikipedia-id.xml
rename from mobile/android/components/search/searchplugins/wikipedia-is.xml
rename to mobile/locales/searchplugins/wikipedia-is.xml
rename from mobile/android/components/search/searchplugins/wikipedia-it.xml
rename to mobile/locales/searchplugins/wikipedia-it.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ja.xml
rename to mobile/locales/searchplugins/wikipedia-ja.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ka.xml
rename to mobile/locales/searchplugins/wikipedia-ka.xml
rename from mobile/android/components/search/searchplugins/wikipedia-kab.xml
rename to mobile/locales/searchplugins/wikipedia-kab.xml
rename from mobile/android/components/search/searchplugins/wikipedia-kk.xml
rename to mobile/locales/searchplugins/wikipedia-kk.xml
rename from mobile/android/components/search/searchplugins/wikipedia-km.xml
rename to mobile/locales/searchplugins/wikipedia-km.xml
rename from mobile/android/components/search/searchplugins/wikipedia-kn.xml
rename to mobile/locales/searchplugins/wikipedia-kn.xml
rename from mobile/android/components/search/searchplugins/wikipedia-lij.xml
rename to mobile/locales/searchplugins/wikipedia-lij.xml
rename from mobile/android/components/search/searchplugins/wikipedia-lo.xml
rename to mobile/locales/searchplugins/wikipedia-lo.xml
rename from mobile/android/components/search/searchplugins/wikipedia-lt.xml
rename to mobile/locales/searchplugins/wikipedia-lt.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ltg.xml
rename to mobile/locales/searchplugins/wikipedia-ltg.xml
rename from mobile/android/components/search/searchplugins/wikipedia-lv.xml
rename to mobile/locales/searchplugins/wikipedia-lv.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ml.xml
rename to mobile/locales/searchplugins/wikipedia-ml.xml
rename from mobile/android/components/search/searchplugins/wikipedia-mr.xml
rename to mobile/locales/searchplugins/wikipedia-mr.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ms.xml
rename to mobile/locales/searchplugins/wikipedia-ms.xml
rename from mobile/android/components/search/searchplugins/wikipedia-my.xml
rename to mobile/locales/searchplugins/wikipedia-my.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ne.xml
rename to mobile/locales/searchplugins/wikipedia-ne.xml
rename from mobile/android/components/search/searchplugins/wikipedia-nl.xml
rename to mobile/locales/searchplugins/wikipedia-nl.xml
rename from mobile/android/components/search/searchplugins/wikipedia-oc.xml
rename to mobile/locales/searchplugins/wikipedia-oc.xml
rename from mobile/android/components/search/searchplugins/wikipedia-or.xml
rename to mobile/locales/searchplugins/wikipedia-or.xml
rename from mobile/android/components/search/searchplugins/wikipedia-pa.xml
rename to mobile/locales/searchplugins/wikipedia-pa.xml
rename from mobile/android/components/search/searchplugins/wikipedia-pl.xml
rename to mobile/locales/searchplugins/wikipedia-pl.xml
rename from mobile/android/components/search/searchplugins/wikipedia-pt.xml
rename to mobile/locales/searchplugins/wikipedia-pt.xml
rename from mobile/android/components/search/searchplugins/wikipedia-rm.xml
rename to mobile/locales/searchplugins/wikipedia-rm.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ro.xml
rename to mobile/locales/searchplugins/wikipedia-ro.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ru.xml
rename to mobile/locales/searchplugins/wikipedia-ru.xml
rename from mobile/android/components/search/searchplugins/wikipedia-sk.xml
rename to mobile/locales/searchplugins/wikipedia-sk.xml
rename from mobile/android/components/search/searchplugins/wikipedia-sl.xml
rename to mobile/locales/searchplugins/wikipedia-sl.xml
rename from mobile/android/components/search/searchplugins/wikipedia-sq.xml
rename to mobile/locales/searchplugins/wikipedia-sq.xml
rename from mobile/android/components/search/searchplugins/wikipedia-sr.xml
rename to mobile/locales/searchplugins/wikipedia-sr.xml
rename from mobile/android/components/search/searchplugins/wikipedia-sv-SE.xml
rename to mobile/locales/searchplugins/wikipedia-sv-SE.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ta.xml
rename to mobile/locales/searchplugins/wikipedia-ta.xml
rename from mobile/android/components/search/searchplugins/wikipedia-te.xml
rename to mobile/locales/searchplugins/wikipedia-te.xml
rename from mobile/android/components/search/searchplugins/wikipedia-th.xml
rename to mobile/locales/searchplugins/wikipedia-th.xml
rename from mobile/android/components/search/searchplugins/wikipedia-tr.xml
rename to mobile/locales/searchplugins/wikipedia-tr.xml
rename from mobile/android/components/search/searchplugins/wikipedia-uk.xml
rename to mobile/locales/searchplugins/wikipedia-uk.xml
rename from mobile/android/components/search/searchplugins/wikipedia-ur.xml
rename to mobile/locales/searchplugins/wikipedia-ur.xml
rename from mobile/android/components/search/searchplugins/wikipedia-uz.xml
rename to mobile/locales/searchplugins/wikipedia-uz.xml
rename from mobile/android/components/search/searchplugins/wikipedia-vi.xml
rename to mobile/locales/searchplugins/wikipedia-vi.xml
rename from mobile/android/components/search/searchplugins/wikipedia-wo.xml
rename to mobile/locales/searchplugins/wikipedia-wo.xml
rename from mobile/android/components/search/searchplugins/wikipedia-zh-CN.xml
rename to mobile/locales/searchplugins/wikipedia-zh-CN.xml
rename from mobile/android/components/search/searchplugins/wikipedia-zh-TW.xml
rename to mobile/locales/searchplugins/wikipedia-zh-TW.xml
rename from mobile/android/components/search/searchplugins/wikipedia.xml
rename to mobile/locales/searchplugins/wikipedia.xml
rename from mobile/android/components/search/searchplugins/wiktionary-kn.xml
rename to mobile/locales/searchplugins/wiktionary-kn.xml
rename from mobile/android/components/search/searchplugins/wiktionary-oc.xml
rename to mobile/locales/searchplugins/wiktionary-oc.xml
rename from mobile/android/components/search/searchplugins/wiktionary-or.xml
rename to mobile/locales/searchplugins/wiktionary-or.xml
rename from mobile/android/components/search/searchplugins/wiktionary-ta.xml
rename to mobile/locales/searchplugins/wiktionary-ta.xml
rename from mobile/android/components/search/searchplugins/wiktionary-te.xml
rename to mobile/locales/searchplugins/wiktionary-te.xml
rename from mobile/android/components/search/searchplugins/yahoo-jp.xml
rename to mobile/locales/searchplugins/yahoo-jp.xml
rename from mobile/android/components/search/searchplugins/yandex-en.xml
rename to mobile/locales/searchplugins/yandex-en.xml
rename from mobile/android/components/search/searchplugins/yandex-ru.xml
rename to mobile/locales/searchplugins/yandex-ru.xml
rename from mobile/android/components/search/searchplugins/yandex-tr.xml
rename to mobile/locales/searchplugins/yandex-tr.xml
rename from mobile/android/components/search/searchplugins/yandex.by.xml
rename to mobile/locales/searchplugins/yandex.by.xml
rename from mobile/android/components/search/searchplugins/yandex.xml
rename to mobile/locales/searchplugins/yandex.xml
--- a/security/manager/ssl/NSSErrorsService.cpp
+++ b/security/manager/ssl/NSSErrorsService.cpp
@@ -164,44 +164,61 @@ ErrorIsOverridable(PRErrorCode code)
     case SSL_ERROR_BAD_CERT_DOMAIN:
       return true;
     // Non-overridable errors.
     default:
       return false;
   }
 }
 
+static const char*
+getOverrideErrorStringName(PRErrorCode aErrorCode)
+{
+  switch (aErrorCode) {
+    case SSL_ERROR_SSL_DISABLED:
+      return "PSMERR_SSL_Disabled";
+    case SSL_ERROR_SSL2_DISABLED:
+      return "PSMERR_SSL2_Disabled";
+    case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
+      return "PSMERR_HostReusedIssuerSerial";
+    case mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED:
+      return "certErrorTrust_MitM";
+    default:
+      return nullptr;
+  }
+}
+
 NS_IMETHODIMP
 NSSErrorsService::GetErrorMessage(nsresult aXPCOMErrorCode, nsAString &aErrorMessage)
 {
   if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY ||
       NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR) {
     return NS_ERROR_FAILURE;
   }
 
   int32_t aNSPRCode = -1 * NS_ERROR_GET_CODE(aXPCOMErrorCode);
 
   if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIStringBundle> theBundle = mPIPNSSBundle;
-  const char *id_str = nsNSSErrors::getOverrideErrorStringName(aNSPRCode);
+  const char* idStr = getOverrideErrorStringName(aNSPRCode);
 
-  if (!id_str) {
-    id_str = nsNSSErrors::getDefaultErrorStringName(aNSPRCode);
+  if (!idStr) {
+    idStr = PR_ErrorToName(aNSPRCode);
     theBundle = mNSSErrorsBundle;
   }
 
-  if (!id_str || !theBundle) {
+  if (!idStr || !theBundle) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoString msg;
-  nsresult rv = theBundle->GetStringFromName(id_str, msg);
+  nsresult rv = theBundle->GetStringFromName(idStr, msg);
   if (NS_SUCCEEDED(rv)) {
     aErrorMessage = msg;
   }
   return rv;
 }
 
 } // namespace psm
 } // namespace mozilla
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -103,17 +103,16 @@ UNIFIED_SOURCES += [
     'nsNSSASN1Object.cpp',
     'nsNSSCallbacks.cpp',
     'nsNSSCertHelper.cpp',
     'nsNSSCertificate.cpp',
     'nsNSSCertificateDB.cpp',
     'nsNSSCertTrust.cpp',
     'nsNSSCertValidity.cpp',
     'nsNSSComponent.cpp',
-    'nsNSSErrors.cpp',
     'nsNSSIOLayer.cpp',
     'nsNSSModule.cpp',
     'nsNSSVersion.cpp',
     'nsNTLMAuthModule.cpp',
     'nsPK11TokenDB.cpp',
     'nsPKCS11Slot.cpp',
     'nsPKCS12Blob.cpp',
     'nsProtectedAuthThread.cpp',
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -98,22 +98,16 @@ nsCertOverrideService::~nsCertOverrideSe
 nsresult
 nsCertOverrideService::Init()
 {
   if (!NS_IsMainThread()) {
     MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
-  // Note that the names of these variables would seem to indicate that at one
-  // point another hash algorithm was used and is still supported for backwards
-  // compatibility. This is not the case. It has always been SHA256.
-  mOidTagForStoringNewHashes = SEC_OID_SHA256;
-  mDottedOidForStoringNewHashes.AssignLiteral("OID.2.16.840.1.101.3.4.2.1");
-
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
 
   // If we cannot add ourselves as a profile change observer, then we will not
   // attempt to read/write any settings file. Otherwise, we would end up
   // reading/writing the wrong settings file after a profile change.
   if (observerService) {
     observerService->AddObserver(this, "profile-before-change", true);
@@ -225,17 +219,17 @@ nsCertOverrideService::Read(const MutexA
     if ((algoIndex         = buffer.FindChar('\t', hostIndex)         + 1) == 0 ||
         (fingerprintIndex  = buffer.FindChar('\t', algoIndex)         + 1) == 0 ||
         (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex)  + 1) == 0 ||
         (dbKeyIndex        = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) {
       continue;
     }
 
     const nsACString& tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1);
-    const nsACString& algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1);
+    // We just ignore the algorithm string.
     const nsACString& fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1);
     const nsACString& bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1);
     const nsACString& db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex);
 
     nsAutoCString host(tmp);
     nsCertOverride::OverrideBits bits;
     nsCertOverride::convertStringToBits(bits_string, bits);
 
@@ -250,22 +244,23 @@ nsCertOverrideService::Read(const MutexA
     if (NS_FAILED(portParseError))
       continue; // Ignore broken entries
 
     host.Truncate(portIndex);
 
     AddEntryToList(host, port,
                    nullptr, // don't have the cert
                    false, // not temporary
-                   algo_string, fingerprint, bits, db_key, aProofOfLock);
+                   fingerprint, bits, db_key, aProofOfLock);
   }
 
   return NS_OK;
 }
 
+static const char sSHA256OIDString[] = "OID.2.16.840.1.101.3.4.2.1";
 nsresult
 nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock)
 {
   // If we don't have any profile, then we won't try to write any file
   if (!mSettingsFile) {
     return NS_OK;
   }
 
@@ -307,18 +302,18 @@ nsCertOverrideService::Write(const Mutex
     }
 
     nsAutoCString bits_string;
     nsCertOverride::convertBitsToString(settings.mOverrideBits, bits_string);
 
     bufferedOutputStream->Write(entry->mHostWithPort.get(),
                                 entry->mHostWithPort.Length(), &unused);
     bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
-    bufferedOutputStream->Write(settings.mFingerprintAlgOID.get(),
-                                settings.mFingerprintAlgOID.Length(), &unused);
+    bufferedOutputStream->Write(sSHA256OIDString, sizeof(sSHA256OIDString) - 1,
+                                &unused);
     bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
     bufferedOutputStream->Write(settings.mFingerprint.get(),
                                 settings.mFingerprint.Length(), &unused);
     bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
     bufferedOutputStream->Write(bits_string.get(),
                                 bits_string.Length(), &unused);
     bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
     bufferedOutputStream->Write(settings.mDBKey.get(),
@@ -384,34 +379,33 @@ nsCertOverrideService::RememberValidityO
     // certificate rather than the full certificate. It makes the display a bit
     // less informative (since we won't have a certificate to display), but it's
     // better than failing the entire operation.
     Unused << PK11_ImportCert(slot.get(), nsscert.get(), CK_INVALID_HANDLE,
                               nickname.get(), false);
   }
 
   nsAutoCString fpStr;
-  rv = GetCertFingerprintByOidTag(nsscert.get(), mOidTagForStoringNewHashes,
-                                  fpStr);
+  rv = GetCertFingerprintByOidTag(nsscert.get(), SEC_OID_SHA256, fpStr);
   if (NS_FAILED(rv))
     return rv;
 
   nsAutoCString dbkey;
   rv = aCert->GetDbKey(dbkey);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   {
     MutexAutoLock lock(mMutex);
     AddEntryToList(aHostName, aPort,
                    aTemporary ? aCert : nullptr,
                      // keep a reference to the cert for temporary overrides
                    aTemporary,
-                   mDottedOidForStoringNewHashes, fpStr,
+                   fpStr,
                    (nsCertOverride::OverrideBits)aOverrideBits,
                    dbkey, lock);
     if (!aTemporary) {
       Write(lock);
     }
   }
 
   return NS_OK;
@@ -427,17 +421,16 @@ nsCertOverrideService::RememberTemporary
   if(aCertFingerprint.IsEmpty() || aHostName.IsEmpty() || (aPort < -1)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   MutexAutoLock lock(mMutex);
   AddEntryToList(aHostName, aPort,
                  nullptr, // No cert to keep alive
                  true, // temporary
-                 mDottedOidForStoringNewHashes,
                  aCertFingerprint,
                  (nsCertOverride::OverrideBits)aOverrideBits,
                  EmptyCString(),  // dbkey
                  lock);
 
   return NS_OK;
 }
 
@@ -475,75 +468,29 @@ nsCertOverrideService::HasMatchingOverri
   }
 
   *aOverrideBits = static_cast<uint32_t>(settings.mOverrideBits);
   *aIsTemporary = settings.mIsTemporary;
 
   nsAutoCString fpStr;
   nsresult rv;
 
-  // This code was originally written in a way that suggested that other hash
-  // algorithms are supported for backwards compatibility. However, this was
-  // always unnecessary, because only SHA256 has ever been used here.
-  if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
-    rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  } else {
-    return NS_ERROR_UNEXPECTED;
+  rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, fpStr);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   *_retval = settings.mFingerprint.Equals(fpStr);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, int32_t aPort,
-                                           nsACString & aHashAlg,
-                                           nsACString & aFingerprint,
-                                           uint32_t *aOverrideBits,
-                                           bool *aIsTemporary,
-                                           bool *_found)
-{
-  NS_ENSURE_ARG_POINTER(_found);
-  NS_ENSURE_ARG_POINTER(aIsTemporary);
-  NS_ENSURE_ARG_POINTER(aOverrideBits);
-  *_found = false;
-  *aOverrideBits = static_cast<uint32_t>(nsCertOverride::OverrideBits::None);
-
-  nsAutoCString hostPort;
-  GetHostWithPort(aHostName, aPort, hostPort);
-  nsCertOverride settings;
-
-  {
-    MutexAutoLock lock(mMutex);
-    nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
-
-    if (entry) {
-      *_found = true;
-      settings = entry->mSettings; // copy
-    }
-  }
-
-  if (*_found) {
-    *aOverrideBits = static_cast<uint32_t>(settings.mOverrideBits);
-    *aIsTemporary = settings.mIsTemporary;
-    aFingerprint = settings.mFingerprint;
-    aHashAlg = settings.mFingerprintAlgOID;
-  }
-
-  return NS_OK;
-}
-
 nsresult
 nsCertOverrideService::AddEntryToList(const nsACString &aHostName, int32_t aPort,
                                       nsIX509Cert *aCert,
                                       const bool aIsTemporary,
-                                      const nsACString &fingerprintAlgOID,
                                       const nsACString &fingerprint,
                                       nsCertOverride::OverrideBits ob,
                                       const nsACString &dbKey,
                                       const MutexAutoLock& aProofOfLock)
 {
   nsAutoCString hostPort;
   GetHostWithPort(aHostName, aPort, hostPort);
 
@@ -555,17 +502,16 @@ nsCertOverrideService::AddEntryToList(co
   }
 
   entry->mHostWithPort = hostPort;
 
   nsCertOverride &settings = entry->mSettings;
   settings.mAsciiHost = aHostName;
   settings.mPort = aPort;
   settings.mIsTemporary = aIsTemporary;
-  settings.mFingerprintAlgOID = fingerprintAlgOID;
   settings.mFingerprint = fingerprint;
   settings.mOverrideBits = ob;
   settings.mDBKey = dbKey;
   // remove whitespace from stored dbKey for backwards compatibility
   settings.mDBKey.StripWhitespace();
   settings.mCert = aCert;
 
   return NS_OK;
@@ -641,21 +587,18 @@ nsCertOverrideService::IsCertUsedForOver
 
       if (( settings.mIsTemporary && !aCheckTemporaries) ||
           (!settings.mIsTemporary && !aCheckPermanents)) {
         continue;
       }
 
       if (matchesDBKey(aCert, settings.mDBKey)) {
         nsAutoCString cert_fingerprint;
-        nsresult rv = NS_ERROR_UNEXPECTED;
-        if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
-          rv = GetCertFingerprintByOidTag(aCert,
-                 mOidTagForStoringNewHashes, cert_fingerprint);
-        }
+        nsresult rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256,
+                                                 cert_fingerprint);
         if (NS_SUCCEEDED(rv) &&
             settings.mFingerprint.Equals(cert_fingerprint)) {
           counter++;
         }
       }
     }
   }
   *_retval = counter;
@@ -671,21 +614,18 @@ nsCertOverrideService::EnumerateCertOver
   for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
     const nsCertOverride &settings = iter.Get()->mSettings;
 
     if (!aCert) {
       aEnumerator(settings, aUserData);
     } else {
       if (matchesDBKey(aCert, settings.mDBKey)) {
         nsAutoCString cert_fingerprint;
-        nsresult rv = NS_ERROR_UNEXPECTED;
-        if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
-          rv = GetCertFingerprintByOidTag(aCert,
-                 mOidTagForStoringNewHashes, cert_fingerprint);
-        }
+        nsresult rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256,
+                                                 cert_fingerprint);
         if (NS_SUCCEEDED(rv) &&
             settings.mFingerprint.Equals(cert_fingerprint)) {
           aEnumerator(settings, aUserData);
         }
       }
     }
   }
   return NS_OK;
--- a/security/manager/ssl/nsCertOverrideService.h
+++ b/security/manager/ssl/nsCertOverrideService.h
@@ -41,29 +41,27 @@ public:
     this->operator=(other);
   }
 
   nsCertOverride &operator=(const nsCertOverride &other)
   {
     mAsciiHost = other.mAsciiHost;
     mPort = other.mPort;
     mIsTemporary = other.mIsTemporary;
-    mFingerprintAlgOID = other.mFingerprintAlgOID;
     mFingerprint = other.mFingerprint;
     mOverrideBits = other.mOverrideBits;
     mDBKey = other.mDBKey;
     mCert = other.mCert;
     return *this;
   }
 
   nsCString mAsciiHost;
   int32_t mPort;
   bool mIsTemporary; // true: session only, false: stored on disk
   nsCString mFingerprint;
-  nsCString mFingerprintAlgOID;
   OverrideBits mOverrideBits;
   nsCString mDBKey;
   nsCOMPtr <nsIX509Cert> mCert;
 
   static void convertBitsToString(OverrideBits ob, nsACString &str);
   static void convertStringToBits(const nsACString &str, OverrideBits &ob);
 };
 
@@ -162,28 +160,24 @@ public:
 
 protected:
     ~nsCertOverrideService();
 
     mozilla::Mutex mMutex;
     nsCOMPtr<nsIFile> mSettingsFile;
     nsTHashtable<nsCertOverrideEntry> mSettingsTable;
 
-    SECOidTag mOidTagForStoringNewHashes;
-    nsCString mDottedOidForStoringNewHashes;
-
     void CountPermanentOverrideTelemetry(const mozilla::MutexAutoLock& aProofOfLock);
 
     void RemoveAllFromMemory();
     nsresult Read(const mozilla::MutexAutoLock& aProofOfLock);
     nsresult Write(const mozilla::MutexAutoLock& aProofOfLock);
     nsresult AddEntryToList(const nsACString &host, int32_t port,
                             nsIX509Cert *aCert,
                             const bool aIsTemporary,
-                            const nsACString &algo_oid,
                             const nsACString &fingerprint,
                             nsCertOverride::OverrideBits ob,
                             const nsACString &dbKey,
                             const mozilla::MutexAutoLock& aProofOfLock);
 };
 
 #define NS_CERTOVERRIDE_CID { /* 67ba681d-5485-4fff-952c-2ee337ffdcd6 */ \
     0x67ba681d,                                                        \
--- a/security/manager/ssl/nsICertOverrideService.idl
+++ b/security/manager/ssl/nsICertOverrideService.idl
@@ -98,37 +98,16 @@ interface nsICertOverrideService : nsISu
   [must_use]
   boolean hasMatchingOverride(in ACString aHostName,
                               in int32_t aPort,
                               in nsIX509Cert aCert,
                               out uint32_t aOverrideBits,
                               out boolean aIsTemporary);
 
   /**
-   *  Retrieve the stored override for the given hostname:port.
-   *
-   *  @param aHostName The host (punycode) whose entry should be tested
-   *  @param aPort The port whose entry should be tested, if it is -1 then it
-   *          is internaly treated as 443
-   *  @param aHashAlg On return value True, the fingerprint hash algorithm
-   *                  as an OID value in dotted notation.
-   *  @param aFingerprint On return value True, the stored fingerprint
-   *  @param aOverrideBits The errors that are currently overriden
-   *  @return whether a matching override entry for aHostNameWithPort
-   *          and aFingerprint is currently on file
-   */
-  [must_use]
-  boolean getValidityOverride(in ACString aHostName,
-                              in int32_t aPort,
-                              out ACString aHashAlg,
-                              out ACString aFingerprint,
-                              out uint32_t aOverrideBits,
-                              out boolean aIsTemporary);
-
-  /**
    *  Remove a override for the given hostname:port.
    *
    *  @param aHostName The host (punycode) whose entry should be cleared.
    *  @param aPort The port whose entry should be cleared.
    *               If it is -1, then it is internaly treated as 443.
    *               If it is 0 and aHostName is "all:temporary-certificates",
    *               then all temporary certificates should be cleared.
    */
--- a/security/manager/ssl/nsNSSComponent.h
+++ b/security/manager/ssl/nsNSSComponent.h
@@ -208,16 +208,9 @@ CheckForSmartCardChanges()
     return NS_ERROR_FAILURE;
   }
   return component->CheckForSmartCardChanges();
 #else
   return NS_OK;
 #endif
 }
 
-class nsNSSErrors
-{
-public:
-  static const char* getDefaultErrorStringName(PRErrorCode err);
-  static const char* getOverrideErrorStringName(PRErrorCode aErrorCode);
-};
-
 #endif // _nsNSSComponent_h_
deleted file mode 100644
--- a/security/manager/ssl/nsNSSErrors.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsNSSComponent.h"
-#include "pkix/pkixnss.h"
-#include "secerr.h"
-#include "sslerr.h"
-
-const char *
-nsNSSErrors::getDefaultErrorStringName(PRErrorCode err)
-{
-  return PR_ErrorToName(err);
-}
-
-const char *
-nsNSSErrors::getOverrideErrorStringName(PRErrorCode aErrorCode)
-{
-  const char *id_str = nullptr;
-
-  switch (aErrorCode) {
-    case SSL_ERROR_SSL_DISABLED:
-      id_str = "PSMERR_SSL_Disabled";
-      break;
-
-    case SSL_ERROR_SSL2_DISABLED:
-      id_str = "PSMERR_SSL2_Disabled";
-      break;
-
-    case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
-      id_str = "PSMERR_HostReusedIssuerSerial";
-      break;
-
-    case mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED:
-      id_str = "certErrorTrust_MitM";
-      break;
-  }
-
-  return id_str;
-}
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -317,11 +317,10 @@ nsresult nsSSLIOLayerAddToSocket(int32_t
                                  const OriginAttributes& originAttributes,
                                  PRFileDesc* fd,
                                  nsISupports** securityInfo,
                                  bool forSTARTTLS,
                                  uint32_t flags,
                                  uint32_t tlsFlags);
 
 nsresult nsSSLIOLayerFreeTLSIntolerantSites();
-nsresult displayUnknownCertErrorAlert(nsNSSSocketInfo* infoObject, int error);
 
 #endif // nsNSSIOLayer_h
--- a/security/manager/ssl/tests/unit/test_js_cert_override_service.js
+++ b/security/manager/ssl/tests/unit/test_js_cert_override_service.js
@@ -22,20 +22,16 @@ const gCertOverrideService = {
   hasMatchingOverride(hostname, port, cert, overrideBits, isTemporary) {
     Assert.equal(hostname, "expired.example.com",
                  "hasMatchingOverride: hostname should be expired.example.com");
     overrideBits.value = Ci.nsICertOverrideService.ERROR_TIME;
     isTemporary.value = false;
     return true;
   },
 
-  getValidityOverride() {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  },
-
   clearValidityOverride() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
 
   isCertUsedForOverrides() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
 
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -94,18 +94,19 @@ if CONFIG['CC_TYPE'] in ('clang', 'gcc')
     CXXFLAGS += ['-Wno-shadow']
     SOURCES['../chromium/sandbox/linux/services/syscall_wrappers.cc'].flags += [
         '-Wno-empty-body',
     ]
 
 # gcc lto likes to put the top level asm in syscall.cc in a different partition
 # from the function using it which breaks the build.  Work around that by
 # forcing there to be only one partition.
-if '-flto' in CONFIG['OS_CXXFLAGS'] and CONFIG['CC_TYPE'] != 'clang':
-    LDFLAGS += ['--param lto-partitions=1']
+for f in CONFIG['OS_CXXFLAGS']:
+    if f.startswith('-flto') and CONFIG['CC_TYPE'] != 'clang':
+        LDFLAGS += ['--param lto-partitions=1']
 
 DEFINES['NS_NO_XPCOM'] = True
 DisableStlWrapping()
 
 LOCAL_INCLUDES += ['/security/sandbox/linux']
 LOCAL_INCLUDES += ['/security/sandbox/chromium-shim']
 LOCAL_INCLUDES += ['/security/sandbox/chromium']
 LOCAL_INCLUDES += ['/nsprpub']
--- a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
+++ b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
@@ -177,20 +177,16 @@ var Authentication = {
 
       let client = new FxAccountsClient();
       let credentials = await client.signIn(account.username, account.password, true);
       await fxAccounts.setSignedInUser(credentials);
       if (!credentials.verified) {
         await this._completeVerification(account.username);
       }
 
-      if (Weave.Status.login !== Weave.LOGIN_SUCCEEDED) {
-        Logger.logInfo("Logging into Weave.");
-        await Weave.Service.login();
-      }
       return true;
     } catch (error) {
       throw new Error("signIn() failed with: " + error.message);
     }
   },
 
   /**
    * Sign out of Firefox Accounts.
--- a/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -637,16 +637,78 @@ linux64-asan/debug:
         tooltool-downloads: public
         need-xvfb: true
     toolchains:
         - linux64-clang-6-pre
         - linux64-gcc
         - linux64-rust
         - linux64-sccache
 
+linux64-lto/opt:
+    description: "Linux64 Opt LTO"
+    index:
+        product: firefox
+        job-name: linux64-lto-opt
+    treeherder:
+        platform: linux64/lto
+        symbol: Bo
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        env:
+            PERFHERDER_EXTRA_OPTIONS: "opt lto"
+        max-run-time: 36000
+    run:
+        using: mozharness
+        actions: [get-secrets build check-test update]
+        config:
+            - builds/releng_base_firefox.py
+            - builds/releng_base_linux_64_builds.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: lto-tc
+        tooltool-downloads: public
+        need-xvfb: true
+    toolchains:
+        - linux64-clang-6-pre
+        - linux64-gcc
+        - linux64-rust
+        - linux64-sccache
+
+linux64-lto/debug:
+    description: "Linux64 Debug LTO"
+    index:
+        product: firefox
+        job-name: linux64-lto-debug
+    treeherder:
+        platform: linux64/lto
+        symbol: Bd
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        env:
+            PERFHERDER_EXTRA_OPTIONS: "debug lto"
+        max-run-time: 36000
+    run:
+        using: mozharness
+        actions: [get-secrets build check-test update]
+        config:
+            - builds/releng_base_firefox.py
+            - builds/releng_base_linux_64_builds.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: lto-tc-and-debug
+        tooltool-downloads: public
+        need-xvfb: true
+    toolchains:
+        - linux64-clang-6-pre
+        - linux64-gcc
+        - linux64-rust
+        - linux64-sccache
+
 linux64-nightly/opt:
     description: "Linux64 Nightly"
     attributes:
         nightly: true
     shipping-phase: build
     shipping-product: firefox
     index:
         product: firefox
--- a/taskcluster/ci/build/macosx.yml
+++ b/taskcluster/ci/build/macosx.yml
@@ -2,17 +2,17 @@ macosx64/debug:
     description: "MacOS X x64 Cross-compile"
     index:
         product: firefox
         job-name: macosx64-debug
     treeherder:
         platform: osx-cross/debug
         symbol: B
         tier: 1
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -35,17 +35,17 @@ macosx64/opt:
     description: "MacOS X x64 Cross-compile"
     index:
         product: firefox
         job-name: macosx64-opt
     treeherder:
         platform: osx-cross/opt
         symbol: B
         tier: 1
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -66,17 +66,17 @@ macosx64/opt:
 macosx64-asan-fuzzing/opt:
     description: "MacOS X x64 Cross-compile Fuzzing ASAN"
     index:
         product: firefox
         job-name: macosx64-fuzzing-asan-opt
     treeherder:
         platform: osx-cross/asan
         symbol: Bof
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             PERFHERDER_EXTRA_OPTIONS: asan-fuzzing
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
@@ -100,17 +100,17 @@ macosx64-dmd/opt:
     description: "MacOS X x64 DMD Cross-compile"
     index:
         product: firefox
         job-name: macosx64-dmd-opt
     treeherder:
         platform: osx-10-10-dmd/opt
         symbol: Bdmd
         tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -140,17 +140,17 @@ macosx64-devedition-nightly/opt:
     index:
         product: devedition
         job-name: macosx64-opt
         type: nightly
     treeherder:
         platform: osx-cross-devedition/opt
         symbol: N
         tier: 1
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -175,17 +175,17 @@ macosx64-noopt/debug:
     description: "MacOS X x64 No-optimize Debug"
     index:
         product: firefox
         job-name: macosx64-noopt-debug
     treeherder:
         platform: osx-cross-noopt/debug
         symbol: B
         tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -210,17 +210,17 @@ macosx64-add-on-devel/opt:
     description: "MacOS X x64 add-on-devel"
     index:
         product: firefox
         job-name: macosx64-add-on-devel
     treeherder:
         platform: osx-cross-add-on-devel/opt
         symbol: B
         tier: 2
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -249,17 +249,17 @@ macosx64-nightly/opt:
     index:
         product: firefox
         job-name: macosx64-opt
         type: nightly
     treeherder:
         platform: osx-cross/opt
         symbol: N
         tier: 1
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 36000
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
@@ -282,17 +282,17 @@ macosx64-ccov/debug:
     description: "MacOS X x64 Cross-compile Code Coverage"
     index:
         product: firefox
         job-name: macosx64-ccov-debug
     treeherder:
         platform: osx-cross-ccov/debug
         symbol: B
         tier: 1
-    worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 5400
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
     run:
         using: mozharness
         actions: [get-secrets build update]
         config:
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -31,16 +31,17 @@ jobs:
     packages:
       - deb7-gdb
       - deb7-git
       - deb7-make
       - deb7-mercurial
       - deb7-python
       - deb7-python3.5
       - deb7-python3-defaults
+      - deb7-python-zstandard
       - deb7-xz-utils
   toolchain-build:
     symbol: I(toolchain)
     parent: debian7-base
     packages:
       - deb7-cmake
       - deb7-ninja
   debian7-amd64-build:
@@ -73,16 +74,17 @@ jobs:
     symbol: I(deb9-base)
     definition: debian-base
     args:
       DIST: stretch
       BASE_TAG: '20170620'
       SNAPSHOT: '20170830T000511Z'
     packages:
       - deb9-mercurial
+      - deb9-python-zstandard
   android-build:
     symbol: I(agb)
     parent: debian9-base
   mingw32-build:
     symbol: I(mingw)
     parent: debian9-base
   index-task:
     symbol: I(idx)
--- a/taskcluster/ci/packages/kind.yml
+++ b/taskcluster/ci/packages/kind.yml
@@ -140,16 +140,29 @@ jobs:
       symbol: Deb7(valgrind)
     run:
       using: debian-package
       dsc:
         url: http://snapshot.debian.org/archive/debian/20170725T040438Z/pool/main/v/valgrind/valgrind_3.13.0-1.dsc
         sha256: ab84e017d1660efd30e9e0593a4c8b976aeda013cefb8c416dd284cc7222c11c
       patch: valgrind-wheezy.diff
 
+  deb7-dh-python:
+    description: "dh-python for Debian wheezy"
+    treeherder:
+      symbol: Deb7(dh-python)
+    run:
+      using: debian-package
+      dsc:
+        url: http://snapshot.debian.org/archive/debian/20170125T211752Z/pool/main/d/dh-python/dh-python_2.20170125.dsc
+        sha256: ef4f2951cea36ae4aac29126a1017505f98b595432fb5bdac0f21b4b4d72c1b4
+      packages:
+        - deb7-python3.5
+        - deb7-python3-defaults
+
   deb7-dpkg-1.17:
     description: "dpkg 1.17 for Debian wheezy"
     treeherder:
       symbol: Deb7(dpkg)
     worker:
       env:
         # The compiler in wheezy doesn't support the stackprotector flags.
         DEB_BUILD_MAINT_OPTIONS: hardening=-stackprotector
@@ -207,8 +220,35 @@ jobs:
     treeherder:
       symbol: Deb7(gdb)
     run:
       using: debian-package
       dsc:
         url: http://snapshot.debian.org/archive/debian/20170119T152956Z/pool/main/g/gdb/gdb_7.12-6.dsc
         sha256: 9727dcb3d6b655e4f2a92110f5db076a490aa50b739804be239905ecff3aacc8
       patch: gdb-wheezy.diff
+
+  deb7-python-zstandard:
+    description: "python-zstandard for Debian wheezy"
+    treeherder:
+      symbol: Deb7(python-zstandard)
+    run:
+      using: debian-package
+      tarball:
+        url: https://github.com/indygreg/python-zstandard/releases/download/0.9.1/python-zstandard-0.9.1.tar.gz
+        sha256: 59c7d6f1f85cebb5124abb50d8ec281c5311e0812e18785e28b197cf1515dd3b
+      patch: python-zstandard-wheezy.diff
+      packages:
+        - deb7-dh-python
+        - deb7-python
+        - deb7-python3.5
+        - deb7-python3-defaults
+
+  deb9-python-zstandard:
+    description: "python-zstandard for Debian stretch"
+    treeherder:
+      symbol: Deb9(python-zstandard)
+    run:
+      using: debian-package
+      dist: stretch
+      tarball:
+        url: https://github.com/indygreg/python-zstandard/releases/download/0.9.1/python-zstandard-0.9.1.tar.gz
+        sha256: 59c7d6f1f85cebb5124abb50d8ec281c5311e0812e18785e28b197cf1515dd3b
--- a/taskcluster/ci/searchfox/kind.yml
+++ b/taskcluster/ci/searchfox/kind.yml
@@ -49,17 +49,17 @@ jobs:
             - linux64-rust
 
     macosx64-searchfox/debug:
         description: "MacOS X x64 Debug Cross-compile Searchfox"
         index:
             job-name: macosx64-searchfox-debug
         treeherder:
             platform: osx-cross/debug
-        worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
+        worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
             max-run-time: 36000
             env:
                 TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/macosx64/cross-releng.manifest"
                 PERFHERDER_EXTRA_OPTIONS: searchfox
         run:
             using: mozharness
             actions: [get-secrets build update]
--- a/taskcluster/docker/debian-base/Dockerfile
+++ b/taskcluster/docker/debian-base/Dockerfile
@@ -51,16 +51,18 @@ RUN /usr/local/sbin/setup_packages.sh $D
     apt-get update && \
     apt-get install \
       git \
       make \
       mercurial \
       python \
       python3.5 \
       python3-minimal \
+      python-zstandard \
+      python3-zstandard \
       xz-utils
 
 # %include testing/mozharness/external_tools/robustcheckout.py
 COPY topsrcdir/testing/mozharness/external_tools/robustcheckout.py /usr/local/mercurial/robustcheckout.py
 
 # %include taskcluster/docker/recipes/hgrc
 COPY topsrcdir/taskcluster/docker/recipes/hgrc /etc/mercurial/hgrc.d/mozilla.rc
 
--- a/taskcluster/taskgraph/transforms/repackage.py
+++ b/taskcluster/taskgraph/transforms/repackage.py
@@ -202,19 +202,17 @@ def make_job_description(config, jobs):
         if locale:
             # Make sure we specify the locale-specific upload dir
             worker['env'].update(LOCALE=locale)
 
         if build_platform.startswith('win'):
             worker_type = 'aws-provisioner-v1/gecko-%s-b-win2012' % level
             run['use-magic-mh-args'] = False
         else:
-            if build_platform.startswith('macosx'):
-                worker_type = 'aws-provisioner-v1/gecko-%s-b-macosx64' % level
-            elif build_platform.startswith('linux'):
+            if build_platform.startswith(('linux', 'macosx')):
                 worker_type = 'aws-provisioner-v1/gecko-%s-b-linux' % level
             else:
                 raise NotImplementedError(
                     'Unsupported build_platform: "{}"'.format(build_platform)
                 )
 
             run['tooltool-downloads'] = 'internal'
             worker['docker-image'] = {"in-tree": "debian7-amd64-build"}
--- a/taskcluster/taskgraph/transforms/repackage_partner.py
+++ b/taskcluster/taskgraph/transforms/repackage_partner.py
@@ -169,17 +169,17 @@ def make_job_description(config, jobs):
 
         worker['env'].update(REPACK_ID=repack_id)
 
         if build_platform.startswith('win'):
             worker_type = 'aws-provisioner-v1/gecko-%s-b-win2012' % level
             run['use-magic-mh-args'] = False
         else:
             if build_platform.startswith('macosx'):
-                worker_type = 'aws-provisioner-v1/gecko-%s-b-macosx64' % level
+                worker_type = 'aws-provisioner-v1/gecko-%s-b-linux' % level
             else:
                 raise NotImplementedError(
                     'Unsupported build_platform: "{}"'.format(build_platform)
                 )
 
             run['tooltool-downloads'] = 'internal'
             worker['docker-image'] = {"in-tree": "debian7-amd64-build"}
 
--- a/taskcluster/taskgraph/util/workertypes.py
+++ b/taskcluster/taskgraph/util/workertypes.py
@@ -4,31 +4,28 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 WORKER_TYPES = {
     'aws-provisioner-v1/gecko-1-b-android': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-1-b-linux': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-1-b-linux-large': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-1-b-linux-xlarge': ('docker-worker', 'linux'),
-    'aws-provisioner-v1/gecko-1-b-macosx64': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-1-b-win2012': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-1-images': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-2-b-android': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-2-b-linux': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-2-b-linux-large': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-2-b-linux-xlarge': ('docker-worker', 'linux'),
-    'aws-provisioner-v1/gecko-2-b-macosx64': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-2-b-win2012': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-2-images': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-3-b-android': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-3-b-linux': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-3-b-linux-large': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-3-b-linux-xlarge': ('docker-worker', 'linux'),
-    'aws-provisioner-v1/gecko-3-b-macosx64': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-3-b-win2012': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-3-images': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-symbol-upload': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-t-linux-large': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-t-linux-medium': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-t-linux-xlarge': ('docker-worker', 'linux'),
     'aws-provisioner-v1/gecko-t-win10-64': ('generic-worker', 'windows'),
     'aws-provisioner-v1/gecko-t-win10-64-gpu': ('generic-worker', 'windows'),
--- a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
@@ -69,17 +69,16 @@ skip-if = appname == 'fennec'
 [test_window_maximize.py]
 skip-if = appname == 'fennec'
 [test_window_minimize.py]
 skip-if = appname == 'fennec' || headless
 [test_window_status_content.py]
 [test_window_status_chrome.py]
 
 [test_screenshot.py]
-skip-if = appname == 'fennec' && debug # Bug 1454680
 [test_cookies.py]
 [test_title.py]
 [test_title_chrome.py]
 skip-if = appname == 'fennec'
 [test_window_type_chrome.py]
 skip-if = appname == 'fennec'
 [test_implicit_waits.py]
 [test_wait.py]
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
@@ -4,18 +4,16 @@
 
 from __future__ import absolute_import
 
 import ConfigParser
 import os
 import re
 import sys
 
-import mozinfo
-
 from six import reraise
 
 from firefox_puppeteer.base import BaseLib
 from firefox_puppeteer.api.appinfo import AppInfo
 
 
 class ActiveUpdate(BaseLib):
 
@@ -179,28 +177,17 @@ class SoftwareUpdate(BaseLib):
         self._active_update = ActiveUpdate(marionette)
 
     @property
     def ABI(self):
         """Get the customized ABI for the update service.
 
         :returns: ABI version
         """
-        abi = self.app_info.XPCOMABI
-        if mozinfo.isMac:
-            abi += self.marionette.execute_script("""
-              let macutils = Components.classes['@mozilla.org/xpcom/mac-utils;1']
-                             .getService(Components.interfaces.nsIMacUtils);
-              if (macutils.isUniversalBinary) {
-                return '-u-' + macutils.architecturesInBinary;
-              }
-              return '';
-            """)
-
-        return abi
+        return self.app_info.XPCOMABI
 
     @property
     def active_update(self):
         """ Holds a reference to an :class:`ActiveUpdate` object."""
         return self._active_update
 
     @property
     def allowed(self):
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_lto_tc.py
@@ -0,0 +1,10 @@
+config = {
+    'default_actions': [
+        'clobber',
+        'build',
+        'check-test',
+        # 'update',
+    ],
+    'stage_platform': 'linux64-lto',
+    'mozconfig_variant': 'nightly-lto',
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_linux_configs/64_lto_tc_and_debug.py
@@ -0,0 +1,11 @@
+config = {
+    'default_actions': [
+        'clobber',
+        'build',
+        'check-test',
+        # 'update',
+    ],
+    'stage_platform': 'linux64-lto-debug',
+    'debug_build': True,
+    'mozconfig_variant': 'debug-lto',
+}
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -411,16 +411,18 @@ class BuildOptionParser(object):
         'cross-debug-artifact': 'builds/releng_sub_%s_configs/%s_cross_debug_artifact.py',
         'cross-noopt-debug': 'builds/releng_sub_%s_configs/%s_cross_noopt_debug.py',
         'cross-fuzzing-asan': 'builds/releng_sub_%s_configs/%s_cross_fuzzing_asan.py',
         'cross-artifact': 'builds/releng_sub_%s_configs/%s_cross_artifact.py',
         'debug': 'builds/releng_sub_%s_configs/%s_debug.py',
         'fuzzing-debug': 'builds/releng_sub_%s_configs/%s_fuzzing_debug.py',
         'asan-and-debug': 'builds/releng_sub_%s_configs/%s_asan_and_debug.py',
         'asan-tc-and-debug': 'builds/releng_sub_%s_configs/%s_asan_tc_and_debug.py',
+        'lto-tc': 'builds/releng_sub_%s_configs/%s_lto_tc.py',
+        'lto-tc-and-debug': 'builds/releng_sub_%s_configs/%s_lto_tc_and_debug.py',
         'stat-and-debug': 'builds/releng_sub_%s_configs/%s_stat_and_debug.py',
         'code-coverage-debug': 'builds/releng_sub_%s_configs/%s_code_coverage_debug.py',
         'code-coverage-opt': 'builds/releng_sub_%s_configs/%s_code_coverage_opt.py',
         'source': 'builds/releng_sub_%s_configs/%s_source.py',
         'noopt-debug': 'builds/releng_sub_%s_configs/%s_noopt_debug.py',
         'api-16-gradle-dependencies': 'builds/releng_sub_%s_configs/%s_api_16_gradle_dependencies.py',
         'api-16': 'builds/releng_sub_%s_configs/%s_api_16.py',
         'api-16-artifact': 'builds/releng_sub_%s_configs/%s_api_16_artifact.py',
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -181,17 +181,18 @@ class Talos(TestingMixin, MercurialScrip
         self.run_local = self.config.get('run_local')
         self.installer_url = self.config.get("installer_url")
         self.talos_json_url = self.config.get("talos_json_url")
         self.talos_json = self.config.get("talos_json")
         self.talos_json_config = self.config.get("talos_json_config")
         self.repo_path = self.config.get("repo_path")
         self.obj_path = self.config.get("obj_path")
         self.tests = None
-        self.gecko_profile = self.config.get('gecko_profile')
+        self.gecko_profile = self.config.get('gecko_profile') or \
+            "--geckoProfile" in self.config.get("talos_extra_options", [])
         self.gecko_profile_interval = self.config.get('gecko_profile_interval')
         self.pagesets_name = None
         self.benchmark_zip = None
         self.mitmproxy_rel_bin = None # some platforms download a mitmproxy release binary
         self.mitmproxy_recording_set = None # zip file found on tooltool that contains all of the mitmproxy recordings
         self.mitmproxy_recordings_file_list = self.config.get('mitmproxy', None) # files inside the recording set
         self.mitmdump = None # path to mitdump tool itself, in py3 venv
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -129008,16 +129008,28 @@
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/css-scoping/shadow-reassign-dynamic-002.html": [
+    [
+     "/css/css-scoping/shadow-reassign-dynamic-002.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-scoping/shadow-root-insert-into-document.html": [
     [
      "/css/css-scoping/shadow-root-insert-into-document.html",
      [
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
@@ -129044,16 +129056,28 @@
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/css-scoping/whitespace-crash-001.html": [
+    [
+     "/css/css-scoping/whitespace-crash-001.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-scrollbars/viewport-scrollbar-body.html": [
     [
      "/css/css-scrollbars/viewport-scrollbar-body.html",
      [
       [
        "/css/css-scrollbars/viewport-scrollbar-body-ref.html",
        "=="
       ]
@@ -520121,16 +520145,20 @@
   "css/css-scoping/shadow-host-with-before-after.html": [
    "b33e82815776f05b6292a88bbb95daa882a50ac6",
    "reftest"
   ],
   "css/css-scoping/shadow-reassign-dynamic-001.html": [
    "11ed4da2e6ce88d8a2b98a8f1c814417ef7770dd",
    "reftest"
   ],
+  "css/css-scoping/shadow-reassign-dynamic-002.html": [
+   "130734170945e395bab92ac0ce799113d9ab9c96",
+   "reftest"
+  ],
   "css/css-scoping/shadow-root-insert-into-document.html": [
    "2cee9fff35c9222074f4ef78dcfcb8a3e02bbc98",
    "reftest"
   ],
   "css/css-scoping/slotted-invalidation.html": [
    "c500e1ceba1b293d45df5f66fd89d4a5d9ceb952",
    "testharness"
   ],
@@ -520157,16 +520185,20 @@
   "css/css-scoping/stylesheet-title-001.html": [
    "2e49b6d74d2021144444ca77e62acbc6aeffac2a",
    "reftest"
   ],
   "css/css-scoping/stylesheet-title-002.html": [
    "5816d3d7af3c4bef07f4a299ab65c74b7b8d80f9",
    "testharness"
   ],
+  "css/css-scoping/whitespace-crash-001.html": [
+   "ffcc7ce387110d170772e0c9178991a2377753ad",
+   "reftest"
+  ],
   "css/css-scroll-anchoring/OWNERS": [
    "d9c8054b356c9273a054a83abeb9be0626c23921",
    "support"
   ],
   "css/css-scroll-anchoring/README.md": [
    "31205944cbcf321f7aa77e3bef0f8835cc7b6d13",
    "support"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/shadow-reassign-dynamic-002.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Scoping: Dynamic reassignment of a slot.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1465572">
+<link rel="match" href="reference/green-box.html"/>
+<div id="host">
+  FAIL
+  <div style="background: green" slot="the-slot"></div>
+</div>
+<script>
+  document.body.offsetTop;
+  host.attachShadow({ mode: "open" }).innerHTML = `
+    <style>::slotted(div) { width: 100px; height: 100px }</style>
+    <p>Test passes if you see a single 100px by 100px green box below.</p>
+    <slot>FAIL</slot>
+  `;
+  document.body.offsetTop;
+  host.shadowRoot.querySelector("slot").setAttribute("name", "the-slot");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/whitespace-crash-001.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Scoping: Dynamic shadow root creation and whitespace optimization crash.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1465572">
+<link rel="match" href="reference/green-box.html"/>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<!--
+  It's important for the test-case that there's whitespace inside the host,
+  and that it's not assigned to any slot.
+-->
+<div id="host">
+  <div style="display: inline" slot="the-slot"></div>
+</div>
+<script>
+  // Flush layout before creating a ShadowRoot, so that the whitespace ends up
+  // suppressed.
+  document.body.offsetTop;
+  host.attachShadow({ mode: "open" }).innerHTML = `
+    <style>
+      ::slotted(div) {
+        width: 100px;
+        height: 100px;
+        background: green;
+      }
+    </style>
+    <slot name="the-slot"></slot>
+  `;
+  document.body.offsetTop;
+  host.firstElementChild.style.display = "block"; // or anything else that reframes the <div>.
+</script>
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1445,17 +1445,17 @@ class Extension extends ExtensionData {
     return (AddonSettings.ALLOW_LEGACY_EXTENSIONS ||
             this.isPrivileged);
   }
 
   saveStartupData() {
     if (this.dontSaveStartupData) {
       return;
     }
-    AddonManagerPrivate.setStartupData(this.id, this.startupData);
+    XPIProvider.setStartupData(this.id, this.startupData);
   }
 
   async _parseManifest() {
     let manifest = await super.parseManifest();
     if (manifest && manifest.permissions.has("mozillaAddons") &&
         !this.isPrivileged) {
       Cu.reportError(`Stripping mozillaAddons permission from ${this.id}`);
       manifest.permissions.delete("mozillaAddons");
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -462,19 +462,28 @@ class Messenger {
               return StrongPromise.wrap(result, channelId, caller);
             } else if (result === true) {
               return StrongPromise.wrap(promise, channelId, caller);
             }
             return response;
           },
         };
 
+        const childManager = this.context.viewType == "background" ? this.context.childManager : null;
         MessageChannel.addListener(this.messageManagers, "Extension:Message", listener);
+        if (childManager) {
+          childManager.callParentFunctionNoReturn("runtime.addMessagingListener",
+                                                  ["onMessage"]);
+        }
         return () => {
           MessageChannel.removeListener(this.messageManagers, "Extension:Message", listener);
+          if (childManager) {
+            childManager.callParentFunctionNoReturn("runtime.removeMessagingListener",
+                                                    ["onMessage"]);
+          }
         };
       },
     }).api();
   }
 
   onMessage(name) {
     return this._onMessage(name, sender => sender.id === this.sender.id);
   }
@@ -547,19 +556,28 @@ class Messenger {
               delete recipient.tab;
             }
             let port = new Port(this.context, mm, this.messageManagers, name, portId, sender, recipient);
             fire.asyncWithoutClone(port.api());
             return true;
           },
         };
 
+        const childManager = this.context.viewType == "background" ? this.context.childManager : null;
         MessageChannel.addListener(this.messageManagers, "Extension:Connect", listener);
+        if (childManager) {
+          childManager.callParentFunctionNoReturn("runtime.addMessagingListener",
+                                                  ["onConnect"]);
+        }
         return () => {
           MessageChannel.removeListener(this.messageManagers, "Extension:Connect", listener);
+          if (childManager) {
+            childManager.callParentFunctionNoReturn("runtime.removeMessagingListener",
+                                                    ["onConnect"]);
+          }
         };
       },
     }).api();
   }
 
   onConnect(name) {
     return this._onConnect(name, sender => sender.id === this.sender.id);
   }
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -1822,16 +1822,29 @@ class EventManager {
 
           let {unregister, convert} = api.primeListener(extension, event, fire, listener.params);
           Object.assign(primed, {unregister, convert});
         }
       }