Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Fri, 10 Aug 2018 00:35:58 +0300
changeset 486014 e51868670f3cd7c2c029cd0843e9919a5664db78
parent 486013 b807f63147aeb40dc4139b258d630cc1739662e0 (current diff)
parent 485939 25a65f1051c29d44aca7ecfe6af6bfada92a912f (diff)
child 486015 60a38b319b323f9b8c44040eedecb49715a129b0
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
browser/base/content/browser.js
browser/base/content/tabbrowser.js
browser/components/customizableui/PanelMultiView.jsm
taskcluster/taskgraph/transforms/tests.py
third_party/webkit/PerformanceTests/asmjs-apps/README_MOZILLA
third_party/webkit/PerformanceTests/asmjs-apps/VERSION
third_party/webkit/PerformanceTests/asmjs-apps/box2d.js
third_party/webkit/PerformanceTests/asmjs-apps/bullet.js
third_party/webkit/PerformanceTests/asmjs-apps/harness.py
third_party/webkit/PerformanceTests/asmjs-apps/harness.sh
third_party/webkit/PerformanceTests/asmjs-apps/lua_binarytrees.js
third_party/webkit/PerformanceTests/asmjs-apps/run.js
third_party/webkit/PerformanceTests/asmjs-apps/zlib.js
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1532705151926" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1533550294490" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2299,16 +2299,20 @@
     <emItem blockID="d664412d-ed08-4892-b247-b007a70856ff" id="/^((@asdfjhsdfuhw)|(@asdfsdfwe)|(@asdieieuss)|(@dghfghfgh)|(@difherk)|(@dsfgtftgjhrdf4)|(@fidfueir)|(@fsgergsdqtyy)|(@hjconsnfes)|(@isdifvdkf)|(@iweruewir)|(@oiboijdjfj)|(@safesearchavs)|(@safesearchavsext)|(@safesearchincognito)|(@safesearchscoutee)|(@sdfykhhhfg)|(@sdiosuff)|(@sdklsajd)|(@sduixcjksd)|(@sicognitores)|(@simtabtest)|(@sodiasudi)|(@test13)|(@test131)|(@test131ver)|(@test132)|(@test13s)|(@testmptys)|(\{ac4e5b0c-13c4-4bfd-a0c3-1e73c81e8bac\})|(\{e78785c3-ec49-44d2-8aac-9ec7293f4a8f\})|(general@filecheckerapp\.com)|(general@safesearch\.net))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="d0a401cb-0c70-4784-8288-b06a88b2ae8a" id="{5834f62d-6164-4cdd-a0a3-c00c66ec9d13}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="cae5d906-0b1d-4d1c-b83f-f9727b8c4a29" id="/^((search-unlisted2@mozilla\.com)|(search-unlisted3@mozilla\.com)|(search-unlisted4@mozilla\.com)|(search-unlisted5@mozilla\.com)|(search-unlisted11@mozilla\.com)|(search-unlisted12@mozilla\.com)|(search-unlisted55@mozilla\.com)|(search-unlisted111@mozilla\.com)|(search-unlisted400@mozilla\.com)|(search-unlisted40110@mozilla\.com)|(search-unlisted17441000051@mozilla\.com)|(search-unlisted174410000522777441@mozilla\.com)|(search-unlisted@mozilla\.com)|({0a054930-63d7-46f4-937a-de80eab21da4})|({0b24cf69-02b8-407d-83db-e7af04fc1f3e})|({0c4df994-4f4a-4646-ae5d-8936be8a4188})|({0d50d8aa-d1ed-4930-b0a0-f3340d2f510e})|({0eb4672d-58a6-4230-b74c-50ca3716c4b0})|({0f9e469e-4245-43f8-a7a8-7e730f80d284})|({0fc9fcc7-2f47-4fd1-a811-6bd4d611294b})|({4479446e-40f3-48af-ab85-7e3bb4468227})|({1a927d5b-42e7-4407-828a-fdc441d0daae})|({1a760841-50c3-4143-9f7e-3c8f04e8f9d1})|({1bd8ba17-b3ed-412e-88db-35bc4d8771d7})|({1c7d6d9e-325a-4260-8213-82d51277fc31})|({01c9a4a4-06dd-426b-9500-2ea6fe841b88})|({1cab8ccf-deff-4743-925d-a47cbd0a6b56})|({1cb0652a-4645-412d-b7e8-0b9e9a83242f})|({1d6634ca-dd37-4a31-aad1-321f05aa2bb3})|({1d9997b2-f61e-429a-8591-999a6d62becc})|({1ed2af70-9e89-42db-a9e8-17ae594003ac})|({01f409a5-d617-47be-a574-d54325fe05d1})|({2a8bec00-0ab0-4b4d-bd3d-4f59eada8fd8})|({2aeb1f92-6ddc-49f5-b7b3-3872d7e019a9})|({2bb68b03-b528-4133-9fc4-4980fbb4e449})|({2cac0be1-10a2-4a0d-b8c5-787837ea5955})|({2d3c5a5a-8e6f-4762-8aff-b24953fe1cc9})|({2ee125f1-5a32-4f8e-b135-6e2a5a51f598})|({2f53e091-4b16-4b60-9cae-69d0c55b2e78})|({3a65e87c-7ffc-408d-927e-ebf1784efd6d})|({3a26e767-b781-4e21-aaf8-ac813d9edc9f})|({3c3ef2a3-0440-4e77-9e3c-1ca8d48f895c})|({3dca6517-0d75-42d2-b966-20467f82dca1})|({3f4191fa-8f16-47d2-9414-36bfc9e0c2bf})|({3f49e12b-bb58-4797-982c-4364030d96d9})|({4aa2f47a-0bae-4a47-8a1b-1b93313a2938})|({04abafc7-7a65-401d-97f3-af2853854373})|({4ad16913-e5cb-4292-974c-d557ef5ec5bb})|({4b1050c6-9139-4126-9331-30a836e75db9})|({4b1777ec-6fe4-4572-9a29-5af206e003bf})|({4beacbbb-1691-40e7-8c1e-4853ce2e2dee})|({4c140bc5-c2ad-41c3-a407-749473530904})|({4cbef3f0-4205-4165-8871-2844f9737602})|({4dac7c77-e117-4cae-a9f0-6bd89e9e26ab})|({04ed02dc-0cb0-40c2-8bc8-6f20843024b8})|({4f6b6aaf-c5a1-4fac-8228-ead4d359dc6d})|({4f8a15fb-45c2-4d3b-afb1-c0c8813a4a5a})|({5af74f5a-652b-4b83-a2a9-f3d21c3c0010})|({5b0f6d3c-10fd-414c-a135-dffd26d7de0f})|({5b421f02-e55e-4b63-b90e-aa0cfea01f53})|({5b620343-cd69-49b8-a7ba-f9d499ee5d3d})|({5c5cf69b-ed92-4429-8d26-ff3bb6c37269})|({5cf77367-b141-4ba4-ac2a-5b2ca3728e81})|({5da81d3d-5db1-432a-affc-4a2fe9a70749})|({5eac1066-90c3-4ba0-b361-e6315dcd6828})|({5ec4c837-59b9-496d-96e2-ff3fa74ca01f})|({5efd8c7a-ff37-41ac-a55c-af4170453fdf})|({5f4e63e4-351f-4a21-a8e5-e50dc72b5566})|({6a934ff5-e41d-43a2-baf5-2d215a869674})|({06a71249-ef35-4f61-b2c8-85c3c6ee5617})|({6ad26473-5822-4142-8881-0c56a8ebc8c0})|({6cee30bc-a27c-43ea-ac72-302862db62b2})|({6ed852d5-a72e-4f26-863f-f660e79a2ebb})|({6eee2d17-f932-4a43-a254-9e2223be8f32})|({6f13489d-b274-45b6-80fa-e9daa140e1a4})|({6fa41039-572b-44a4-acd4-01fdaebf608d})|({7ae85eef-49cf-440d-8d13-2bebf32f14cf})|({7b3c1e86-2599-4e1a-ad98-767ae38286c8})|({7b23c0de-aa3d-447f-9435-1e8eba216f09})|({7b71d75e-51f5-4a71-9207-7acb58827420})|({7c6bf09e-5526-4bce-9548-7458ec56cded})|({7ca54c8d-d515-4f2a-a21f-3d32951491a6})|({7d932012-b4dd-42cc-8a78-b15ca82d0e61})|({7d5e24a1-7bef-4d09-a952-b9519ec00d20})|({7eabad73-919d-4890-b737-8d409c719547})|({7eaf96aa-d4e7-41b0-9f12-775c2ac7f7c0})|({7f8bc48d-1c7c-41a0-8534-54adc079338f})|({7f84c4d8-bdf5-4110-a10d-fa2a6e80ef6a})|({8a6bda75-4668-4489-8869-a6f9ccbfeb84})|({8a0699a0-09c3-4cf1-b38d-fec25441650c})|({8ab8c1a2-70d4-41a8-bf78-0d0df77ac47f})|({8b4cb418-027e-4213-927a-868b33a88b4f})|({8fcfe2b3-598e-4861-a5d4-0d77993f984b})|({9a941038-82fa-4ae4-ba98-f2eb2d195345})|({9b8a3057-8bf4-4a9e-b94b-867e4e71a50c})|({9b8df895-fcdd-452a-8c46-da5be345b5bc})|({09c8fa16-4eec-4f78-b19d-9b24b1b57e1e})|({09cbfddf-5e55-4676-920d-5a16cb9e4cb5})|({9cf8d28f-f546-4871-ac4d-5faff8b5bde3})|({9d592fd5-e655-461a-9b28-9eba85d4c97f})|({9fc6e583-78a5-4a2b-8569-4297bb8b3300})|({014d98ce-dab9-4c1d-8643-166e75d7cb4d})|({18c64b09-4ccb-4c21-ba6f-ebd4a1efa034})|({21d83d85-a636-4b18-955d-376a6b19bd19})|({22ecf14b-ead6-4684-a498-7b2b839a4c97})|({23c65153-c21e-430a-a2dc-0793410a870d})|({29c69b12-8208-457e-92f4-e663b00a1f10})|({30a8d6f1-0401-4327-8c46-2e1ab45dfe77})|({30d63f93-1446-43b3-8219-deefec9c81ce})|({32cb52f8-c78a-423d-b378-0abec72304a6})|({35bfa8c0-68c1-41f8-a5dd-7f3b3c956da9})|({36a4269e-4eef-4538-baea-9dafbf6a8e2f})|({37f8e483-c782-40ed-82e9-36f101b9e41f})|({42a512a8-37e0-4e07-a1db-5b4651d75048})|({43ae5745-c40a-45ab-9c11-74316c0e9fd2})|({53fa8e1c-112b-4013-b582-0d9e8c51ca75})|({56effac7-3ae9-41e3-9b46-51469f67b3b2})|({61a486c0-ce3d-4bf1-b4f2-e186a2adecf1})|({62b55928-80cc-49f7-8a4b-ec06030d6601})|({63df223d-51cf-4f76-aad8-bbc94c895ed2})|({064d8320-e0f3-411f-9ed1-8c1349279d20})|({071b9878-a7d3-4ae3-8ef0-2eaee1923403})|({72c1ca96-c05d-46a7-bce1-c507ec3db4ea})|({76ce213c-8e57-4a14-b60a-67a5519bd7a7})|({78c2f6a0-3b54-4a21-bf25-a3348278c327})|({0079b71b-89c9-4d82-aea3-120ee12d9890})|({81ac42f3-3d17-4cff-85af-8b7f89c8826b})|({81dc4f0e-9dab-4bd2-ab9d-d9365fbf676f})|({82c8ced2-e08c-4d6c-a12b-3e8227d7fc2a})|({83d6f65c-7fc0-47d0-9864-a488bfcaa376})|({83d38ac3-121b-4f28-bf9c-1220bd3c643b})|({84b9121e-55c9-409a-9b28-c588b5096222})|({87ba49bd-daba-4071-aedf-4f32a7e63dbe})|({87c552f9-7dbb-421b-8deb-571d4a2d7a21})|({87dcb9bf-3a3e-4b93-9c85-ba750a55831a})|({89a4f24d-37d5-46e7-9d30-ba4778da1aaa})|({93c524c4-2e92-4dd7-8b37-31a69bc579e8})|({94df38fc-2dbe-4056-9b35-d9858d0264d3})|({95c7ae97-c87e-4827-a2b7-7b9934d7d642})|({95d58338-ba6a-40c8-93fd-05a34731dc0e})|({97c436a9-7232-4495-bf34-17e782d6232c})|({97fca2cd-545f-42ef-ae93-dc13b046bd3b})|({0111c475-01e6-42ea-a9b4-27bed9eb6092})|({115a8321-4414-4f4c-aee6-9f812121b446})|({158a5a56-aca0-418f-bec0-5b3bda6e9d4c})|({243a0246-cbab-4b46-93fb-249039f68d84})|({283d4f2a-bab1-43ce-90be-5129741ac988})|({408a506b-2336-4671-a490-83a1094b4097})|({0432b92a-bfcf-41b9-b5f0-df9629feece1})|({484e0ba4-a20b-4404-bb1b-b93473782ae0})|({486ecaf1-1080-48c1-8973-549bc731ccf9})|({495a84bd-5a0c-4c74-8a50-88a4ba9d74ba})|({520f2c78-7804-4f59-ae74-a192476055ed})|({543f7503-3620-4f41-8f9e-c258fdff07e9})|({0573bea9-7368-49cd-ba10-600be3535a0b})|({605a0c42-86af-40c4-bf39-f14060f316aa})|({618baeb9-e694-4c7b-9328-69f35b6a8839})|({640c40e5-a881-4d16-a4d0-6aa788399dd2})|({713d4902-ae7b-4a9a-bcf5-47f39a73aed0})|({767d394a-aa77-40c9-9365-c1916b4a2f84})|({832ffcf9-55e9-4fd1-b2eb-f19e1fac5089})|({866a0745-8b91-4199-820a-ec17de52b5f2})|({869b5825-e344-4375-839b-085d3c09ab9f})|({919fed43-3961-48d9-b0ef-893054f4f6f1})|({971d6ef0-a085-4a04-83d8-6e489907d926})|({1855d130-4893-4c79-b4aa-cbdf6fee86d3})|({02328ee7-a82b-4983-a5f7-d0fc353698f0})|({2897c767-03aa-4c2f-910a-6d0c0b9b9315})|({3908d078-e1db-40bf-9567-5845aa77b833})|({04150f98-2d7c-4ae2-8979-f5baa198a577})|({4253db7f-5136-42c3-b09d-cf38344d1e16})|({4414af84-1e1f-449b-ac85-b79f812eb69b})|({4739f233-57c1-4466-ad51-224558cf375d})|({5066a3b2-f848-4a59-a297-f268bc3a08b6})|({6072a2a8-f1bc-4c9c-b836-7ac53e3f51e4})|({7854ee87-079f-4a25-8e57-050d131404fe})|({07953f60-447e-4f53-a5ef-ed060487f616})|({8886a262-1c25-490b-b797-2e750dd9f36b})|({12473a49-06df-4770-9c47-a871e1f63aea})|({15508c91-aa0a-4b75-81a2-13055c96281d})|({18868c3a-a209-41a6-855d-f99f782d1606})|({24997a0a-9d9b-4c87-a076-766d44e1f6fd})|({27380afd-f42a-4c25-b57d-b9012e0d5d48})|({28044ca8-8e90-435e-bc63-a757af2fb6be})|({30972e0a-f613-4c46-8c87-2e59878e7180})|({31680d42-c80d-4f8a-86d3-cd4930620369})|({44685ba6-68b3-4895-879e-4efa29dfb578})|({046258c9-75c5-429d-8d5b-386cfbadc39d})|({47352fbf-80d9-4b70-9398-fb7bffa3da53})|({56316a2b-ef89-4366-b4aa-9121a2bb6dea})|({65072bef-041f-492e-8a51-acca2aaeac70})|({677e2d00-264c-4f62-a4e8-2d971349c440})|({72056a58-91a5-4de5-b831-a1fa51f0411a})|({85349ea6-2b5d-496a-9379-d4be82c2c13d})|({98363f8b-d070-47b6-acc6-65b80acac4f3})|({179710ba-0561-4551-8e8d-1809422cb09f})|({207435d0-201d-43f9-bb0f-381efe97501d})|({313e3aef-bdc9-4768-8f1f-b3beb175d781})|({387092cb-d2dc-4da5-9389-4a766c604ec2})|({0599211f-6314-4bf9-854b-84cb18da97f8})|({829827cd-03be-4fed-af96-dd5997806fb4})|({856862a5-8109-47eb-b815-a94059570888})|({1e6f5a54-2c4f-4597-aa9e-3e278c617d38})|({1490068c-d8b7-4bd2-9621-a648942b312c})|({18e5e07b-0cfa-4990-a67b-4512ecbae04b})|({3584581e-c01a-4f53-aec8-ca3293bb550d})|({5280684d-f769-43c9-8eaa-fb04f7de9199})|({5766852a-b384-4276-ad06-70c2283b4792})|({34364255-2a81-4d6e-9760-85fe616abe80})|({45621564-b408-4c29-8515-4cf1f26e4bc3})|({62237447-e365-487e-8fc3-64ddf37bdaed})|({7e7aa524-a8af-4880-8106-102a35cfbf42})|({71639610-9cc3-47e0-86ed-d5b99eaa41d5})|({78550476-29ff-4b7e-b437-195024e7e54e})|({85064550-57a8-4d06-bd4b-66f9c6925bf5})|({93070807-c5cd-4bde-a699-1319140a3a9c})|({11e7b9b3-a769-4d7f-b200-17cffa4f9291})|({22632e5e-95b9-4f05-b4b7-79033d50467f})|({03e10db6-b6a7-466a-a2b3-862e98960a85})|({23775e7d-dfcf-42b1-aaad-8017aa88fc59})|({85e31e7e-3e3a-42d3-9b7b-0a2ff1818b33})|({9e32ca65-4670-41e3-b6bb-8773e6b9bba8})|({6e43af8e-a78e-4beb-991f-7b015234eacc})|({57e61dc7-db04-4cf8-bbd3-62a15fc74138})|({01166e60-d740-440c-b640-6bf964504b3c})|({52e137bc-a330-4c25-a981-6c1ab9feb806})|({488e190b-d1f6-4de8-bffb-0c90cc805b62})|({5e257c96-bfed-457d-b57e-18f31f08d7bb})|({2134e327-8060-441c-ba68-b167b82ff5bc})|({1e68848a-2bb7-425c-81a2-524ab93763eb})|({8e888a6a-ec19-4f06-a77c-6800219c6daf})|({7e907a15-0a4c-4ff4-b64f-5eeb8f841349})|({a0ab16af-3384-4dbe-8722-476ce3947873})|({a0c54bd8-7817-4a40-b657-6dc7d59bd961})|({a0ce2605-b5fc-4265-aa65-863354e85058})|({a1f8e136-bce5-4fd3-9ed1-f260703a5582})|({a3fbc8be-dac2-4971-b76a-908464cfa0e0})|({a5a84c10-f12c-496e-80df-33386b7a1463})|({a5f90823-0a50-414f-ad34-de0f6f26f78e})|({a6b83c45-3f24-4913-a1f7-6f42411bbb54})|({a9eb2583-75e0-435a-bb6c-69d5d9b20e27})|({a32ebb9b-8649-493e-a9e9-f091f6ac1217})|({a83c1cbb-7a41-41e7-a2ae-58efcb4dc2e4})|({a506c5af-0f95-4107-86f8-3de05e2794c9})|({a02001ae-b7ed-45d7-baf2-c07f0a7b6f87})|({a5808da1-5b4f-42f2-b030-161fd11a36f7})|({a18087bb-4980-4349-898c-ca1b7a0e59cd})|({a345865c-44b9-4197-b418-934f191ce555})|({a7487703-02d8-4a82-a7d0-2859de96edb4})|({a2427e23-d349-4b25-b5b8-46960b218079})|({a015e172-2465-40fc-a6ce-d5a59992c56a})|({aaaffe20-3306-4c64-9fe5-66986ebb248e})|({abec23c3-478f-4a5b-8a38-68ccd500ec42})|({ac06c6b2-3fd6-45ee-9237-6235aa347215})|({ac037ad5-2b22-46c7-a2dc-052b799b22b5})|({ac296b47-7c03-486f-a1d6-c48b24419749})|({acbff78b-9765-4b55-84a8-1c6673560c08})|({acfe4807-8c3f-4ecc-85d1-aa804e971e91})|({ada56fe6-f6df-4517-9ed0-b301686a34cc})|({af44c8b4-4fd8-42c3-a18e-c5eb5bd822e2})|({b5a35d05-fa28-41b5-ae22-db1665f93f6b})|({b7b0948c-d050-4c4c-b588-b9d54f014c4d})|({b7f366fa-6c66-46bf-8df2-797c5e52859f})|({b9bb8009-3716-4d0c-bcb4-35f9874e931e})|({b12cfdc7-3c69-43cb-a3fb-38981b68a087})|({b019c485-2a48-4f5b-be13-a7af94bc1a3e})|({b91fcda4-88b0-4a10-9015-9365e5340563})|({b30591d6-ec24-4fae-9df6-2f3fe676c232})|({b99847d6-c932-4b52-9650-af83c9dae649})|({bbe79d30-e023-4e82-b35e-0bfdfe608672})|({bc3c2caf-2710-4246-bd22-b8dc5241693a})|({bc3c7922-e425-47e2-a2dd-0dbb71aa8423})|({bc763c41-09ca-459a-9b22-cf4474f51ebc})|({bd5ba448-b096-4bd0-9582-eb7a5c9c0948})|({be5d0c88-571b-4d01-a27a-cc2d2b75868c})|({be981b5e-1d9d-40dc-bd4f-47a7a027611c})|({be37931c-af60-4337-8708-63889f36445d})|({bea8866f-01f8-49e9-92cd-61e96c05d288})|({bf153de7-cdf2-4554-af46-29dabfb2aa2d})|({c3a2b953-025b-425d-9e6e-f1a26ee8d4c2})|({c3b71705-c3a6-4e32-bd5f-eb814d0e0f53})|({c5d359ff-ae01-4f67-a4f7-bf234b5afd6e})|({c6c8ea62-e0b1-4820-9b7f-827bc5b709f4})|({c8c8e8de-2989-4028-bbf2-d372e219ba71})|({c34f47d1-2302-4200-80d4-4f26e47b2980})|({c178b310-6ed5-4e04-9e71-76518dd5fb3e})|({c2341a34-a3a0-4234-90cf-74df1db0aa49})|({c8399f02-02f4-48e3-baea-586564311f95})|({c41807db-69a1-4c35-86c1-bc63044e4fcb})|({c383716f-b23f-47b2-b6bb-d7c1a7c218af})|({c3447081-f790-45cb-ae03-0d7f1764c88c})|({c445e470-9e5a-4521-8649-93c8848df377})|({c8e14311-4b2d-4eb0-9a6b-062c6912f50e})|({ca4fdfdb-e831-4e6e-aa8b-0f2e84f4ed07})|({ca6cb8b2-a223-496d-b0f6-35c31bc7ca2b})|({cba7ce11-952b-4dcb-ba85-a5b618c92420})|({cc6b2dc7-7d6f-470f-bccc-6a42907162d1})|({cc689da4-203f-4a0c-a7a6-a00a5abe74c5})|({ccb7b5d6-a567-40a2-9686-a097a8b583dd})|({cd28aa38-d2f1-45a3-96c3-6cfd4702ef51})|({cd89045b-2e06-46bb-9e34-48e8799e5ef2})|({cdda1813-51d6-4b1f-8a2f-8f9a74a28e14})|({ce0d1384-b99b-478e-850a-fa6dfbe5a2d4})|({ce93dcc7-f911-4098-8238-7f023dcdfd0d})|({cf9d96ff-5997-439a-b32b-98214c621eee})|({cfa458f9-b49b-4e09-8cb2-5e50bd8937cc})|({cfb50cdf-e371-4d6b-9ef2-fcfe6726db02})|({d1ab5ebd-9505-481d-a6cd-6b9db8d65977})|({d03b6b0f-4d44-4666-a6d6-f16ad9483593})|({d9d8cfc1-7112-40cc-a1e9-0c7b899aae98})|({d47ebc8a-c1ea-4a42-9ca3-f723fff034bd})|({d72d260f-c965-4641-bf49-af4135fc46cb})|({d78d27f4-9716-4f13-a8b6-842c455d6a46})|({d355bee9-07f0-47d3-8de6-59b8eecba57b})|({d461cc1b-8a36-4ff0-b330-1824c148f326})|({d97223b8-44e5-46c7-8ab5-e1d8986daf44})|({d42328e1-9749-46ba-b35c-cce85ddd4ace})|({da7d00bf-f3c8-4c66-8b54-351947c1ef68})|({db84feec-2e1f-48f0-9511-645fe4784feb})|({dc6256cc-b6d0-44ca-b42f-4091f11a9d29})|({dd1cb0ec-be2a-432b-9c90-d64c824ac371})|({dd95dd08-75d1-4f06-a75b-51979cbab247})|({ddae89bd-6793-45d8-8ec9-7f4fb7212378})|({de3b1909-d4da-45e9-8da5-7d36a30e2fc6})|({df09f268-3c92-49db-8c31-6a25a6643896})|({e2a4966f-919d-4afc-a94f-5bd6e0606711})|({e05ba06a-6d6a-4c51-b8fc-60b461ffecaf})|({e7b978ae-ffc2-4998-a99d-0f4e2f24da82})|({e7fb6f2f-52b6-4b02-b410-2937940f5049})|({e08d85c5-4c0f-4ce3-9194-760187ce93ba})|({e08ebf0b-431d-4ed1-88bb-02e5db8b9443})|({e9c47315-2a2b-4583-88f3-43d196fa11af})|({e341ed12-a703-47fe-b8dd-5948c38070e4})|({e804fa4c-08e0-4dae-a237-8680074eba07})|({e8982fbd-1bc2-4726-ad8d-10be90f660bd})|({e40673cd-9027-4f61-956c-2097c03ae2be})|({e72172d1-39c9-4f41-829d-a1b8d845d1ca})|({e73854da-9503-423b-ab27-fafea2fbf443})|({e81e7246-e697-4811-b336-72298d930857})|({ea618d26-780e-4f0f-91fd-2a6911064204})|({ea523075-66cd-4c03-ab04-5219b8dda753})|({eb3ebb14-6ced-4f60-9800-85c3de3680a4})|({ec8c5fee-0a49-44f5-bf55-f763c52889a6})|({eccd286a-5b1d-494d-82b0-92a12213d95a})|({ed352072-ddf0-4cb4-9cb6-d8aa3741c2de})|({edb476af-0505-42af-a7fd-ec9f454804c0})|({ee97f92d-1bfe-4e9d-816c-0dfcd63a6206})|({f0b809eb-be22-432f-b26f-b1cadd1755b9})|({f5ffa269-fbca-4598-bbd8-a8aa9479e0b3})|({f6c543bf-2222-4230-8ecb-f5446095b63d})|({f6df4ef7-14bd-43b5-90c9-7bd02943789c})|({f6f98e6b-f67d-4c53-8b76-0b5b6df79218})|({f38b61f3-3fed-4249-bb3d-e6c8625c7afb})|({f50e0a8f-8c32-4880-bcef-ca978ccd1d83})|({f59c2d3d-58da-4f74-b8c9-faf829f60180})|({f82b3ad5-e590-4286-891f-05adf5028d2f})|({f92c1155-97b3-40f4-9d5b-7efa897524bb})|({f95a3826-5c8e-4f82-b353-21b6c0ca3c58})|({f5758afc-9faf-42bb-9543-a4cfb0bfce9d})|({f447670d-64f5-418f-9b4a-5352d6c8e127})|({f4262989-6de0-4604-918f-663b85fad605})|({fa8bd609-0e06-4ba9-8e2e-5989f0b2e197})|({fa0808f6-25ab-4a8b-bd17-3b275c55ff09})|({fac5816b-fd0f-4db2-a16e-52394b6db41d})|({fc99b961-5878-46b4-b091-6d2f507bf44d})|({fce89242-66d3-4946-9ed0-e66078f172fc})|({fcf72e24-5831-439e-bb07-fd53a9e87a30})|({fdc0601f-1fbb-40a5-84e1-8bbe96b22502})|({feb3c734-4529-4d69-9f3a-2dae18f1d896}))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -319,17 +319,17 @@ var gXPInstallObserver = {
       messageString = PluralForm.get(installInfo.installs.length, messageString);
       messageString = messageString.replace("#1", installInfo.installs.length);
       options.installs = installInfo.installs;
       options.contentWindow = browser.contentWindow;
       options.sourceURI = browser.currentURI;
       options.eventCallback = function(aEvent) {
         switch (aEvent) {
           case "shown":
-            let notificationElement = [...this.owner.panel.childNodes]
+            let notificationElement = [...this.owner.panel.children]
                                       .find(n => n.notification == this);
             if (notificationElement) {
               if (Services.prefs.getBoolPref("xpinstall.customConfirmationUI", false)) {
                 notificationElement.setAttribute("mainactiondisabled", "true");
               } else {
                 notificationElement.button.hidden = true;
               }
             }
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -260,18 +260,18 @@ var ctrlTab = {
   },
 
   updatePreview: function ctrlTab_updatePreview(aPreview, aTab) {
     if (aPreview == this.showAllButton)
       return;
 
     aPreview._tab = aTab;
 
-    if (aPreview._canvas.firstChild) {
-      aPreview._canvas.firstChild.remove();
+    if (aPreview._canvas.firstElementChild) {
+      aPreview._canvas.firstElementChild.remove();
     }
 
     if (aTab) {
       aPreview._canvas.appendChild(tabPreviews.get(aTab));
       aPreview._label.setAttribute("value", aTab.label);
       aPreview.setAttribute("tooltiptext", aTab.label);
       if (aTab.image) {
         aPreview._favicon.setAttribute("src", aTab.image);
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -25,17 +25,17 @@ var CustomizationHandler = {
 
   isCustomizing() {
     return document.documentElement.hasAttribute("customizing");
   },
 
   _customizationStarting() {
     // Disable the toolbar context menu items
     let menubar = document.getElementById("main-menubar");
-    for (let childNode of menubar.childNodes)
+    for (let childNode of menubar.children)
       childNode.setAttribute("disabled", true);
 
     let cmd = document.getElementById("cmd_CustomizeToolbars");
     cmd.setAttribute("disabled", "true");
 
     UpdateUrlbarSearchSplitterState();
 
     PlacesToolbarHelper.customizeStart();
@@ -64,16 +64,16 @@ var CustomizationHandler = {
     UpdateUrlbarSearchSplitterState();
 
     // Update the urlbar
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
 
     // Re-enable parts of the UI we disabled during the dialog
     let menubar = document.getElementById("main-menubar");
-    for (let childNode of menubar.childNodes)
+    for (let childNode of menubar.children)
       childNode.setAttribute("disabled", false);
     let cmd = document.getElementById("cmd_CustomizeToolbars");
     cmd.removeAttribute("disabled");
 
     gBrowser.selectedBrowser.focus();
   }
 };
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -1018,17 +1018,17 @@ BrowserPageActions.sendToDevice = {
     notReady.classList.add(
       "subviewbutton",
       "subviewbutton-iconic",
       "pageAction-sendToDevice-notReady"
     );
     notReady.setAttribute("label", "sendToDevice-notReadyTitle");
     notReady.setAttribute("disabled", "true");
     bodyNode.appendChild(notReady);
-    for (let node of bodyNode.childNodes) {
+    for (let node of bodyNode.children) {
       BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
       BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
     }
   },
 
   onLocationChange() {
     let action = PageActions.actionForID("sendToDevice");
     let browser = gBrowser.selectedBrowser;
@@ -1213,17 +1213,17 @@ BrowserPageActions.shareURL = {
     BrowserPageActions.takeActionTitleFromPanel(action);
   },
 
   onShowingSubview(panelViewNode) {
     let bodyNode = panelViewNode.querySelector(".panel-subview-body");
 
     // We cache the providers + the UI if the user selects the share
     // panel multiple times while the panel is open.
-    if (this._cached && bodyNode.childNodes.length > 0) {
+    if (this._cached && bodyNode.children.length > 0) {
       return;
     }
 
     let sharingService = this._sharingService;
     let url = gBrowser.selectedBrowser.currentURI;
     let currentURI = gURLBar.makeURIReadable(url).displaySpec;
     let shareProviders = sharingService.getSharingProviders(currentURI);
     let fragment = document.createDocumentFragment();
--- a/browser/base/content/browser-sidebar.js
+++ b/browser/base/content/browser-sidebar.js
@@ -165,17 +165,17 @@ var SidebarUI = {
 
   /**
    * Read the positioning pref and position the sidebar and the splitter
    * appropriately within the browser container.
    */
   setPosition() {
     // First reset all ordinals to match DOM ordering.
     let browser = document.getElementById("browser");
-    [...browser.childNodes].forEach((node, i) => {
+    [...browser.children].forEach((node, i) => {
       node.ordinal = i + 1;
     });
 
     if (!this._positionStart) {
       // DOM ordering is:     |  sidebar-box  | splitter |   appcontent  |
       // Want to display as:  |   appcontent  | splitter |  sidebar-box  |
       // So we just swap box and appcontent ordering
       let appcontent = document.getElementById("appcontent");
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -368,18 +368,18 @@ var gSync = {
     if (!createDeviceNodeFn) {
       createDeviceNodeFn = (clientId, name, clientType, lastModified) => {
         let eltName = name ? "menuitem" : "menuseparator";
         return document.createXULElement(eltName);
       };
     }
 
     // remove existing menu items
-    for (let i = devicesPopup.childNodes.length - 1; i >= 0; --i) {
-      let child = devicesPopup.childNodes[i];
+    for (let i = devicesPopup.children.length - 1; i >= 0; --i) {
+      let child = devicesPopup.children[i];
       if (child.classList.contains("sync-menuitem")) {
         child.remove();
       }
     }
 
     if (gSync.syncConfiguredAndLoading) {
       // We can only be in this case in the page action menu.
       return;
@@ -471,17 +471,17 @@ var gSync = {
     signInItem.setAttribute("label", signInToSync);
     // Show an icon if opened in the page action panel:
     if (signInItem.classList.contains("subviewbutton")) {
       signInItem.classList.add("subviewbutton-iconic", "signintosync");
     }
     signInItem.addEventListener("command", () => {
       this.openPrefs("sendtab");
     });
-    fragment.insertBefore(signInItem, fragment.lastChild);
+    fragment.insertBefore(signInItem, fragment.lastElementChild);
   },
 
   _appendSendTabInfoItems(fragment, createDeviceNodeFn, statusLabel, actions) {
     const status = createDeviceNodeFn(null, statusLabel, null);
     status.setAttribute("label", statusLabel);
     status.setAttribute("disabled", true);
     status.classList.add("sync-menuitem");
     fragment.appendChild(status);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -393,17 +393,17 @@ const gClickAndHoldListenersOnElement = 
 
   _mousedownHandler(aEvent) {
     if (aEvent.button != 0 ||
         aEvent.currentTarget.open ||
         aEvent.currentTarget.disabled)
       return;
 
     // Prevent the menupopup from opening immediately
-    aEvent.currentTarget.firstChild.hidden = true;
+    aEvent.currentTarget.firstElementChild.hidden = true;
 
     aEvent.currentTarget.addEventListener("mouseout", this);
     aEvent.currentTarget.addEventListener("mouseup", this);
     this._timers.set(aEvent.currentTarget, setTimeout((b) => this._openMenu(b), 500, aEvent.currentTarget));
   },
 
   _clickHandler(aEvent) {
     if (aEvent.button == 0 &&
@@ -420,17 +420,17 @@ const gClickAndHoldListenersOnElement = 
       // dom.click() triggers a command even if there is a click handler
       // however this can now be prevented with preventDefault().
       aEvent.preventDefault();
     }
   },
 
   _openMenu(aButton) {
     this._cancelHold(aButton);
-    aButton.firstChild.hidden = false;
+    aButton.firstElementChild.hidden = false;
     aButton.open = true;
   },
 
   _mouseoutHandler(aEvent) {
     let buttonRect = aEvent.currentTarget.getBoundingClientRect();
     if (aEvent.clientX >= buttonRect.left &&
         aEvent.clientX <= buttonRect.right &&
         aEvent.clientY >= buttonRect.bottom)
@@ -749,19 +749,19 @@ var gPopupBlockerObserver = {
       // Show the separator if we added any
       // showable popup addresses to the menu.
       if (foundUsablePopupURI)
         blockedPopupsSeparator.removeAttribute("hidden");
     }, null);
   },
 
   onPopupHiding(aEvent) {
-    let item = aEvent.target.lastChild;
+    let item = aEvent.target.lastElementChild;
     while (item && item.id != "blockedPopupsSeparator") {
-      let next = item.previousSibling;
+      let next = item.previousElementSibling;
       item.remove();
       item = next;
     }
   },
 
   showBlockedPopup(aEvent) {
     var target = aEvent.target;
     var popupReportIndex = target.getAttribute("popupReportIndex");
@@ -2763,26 +2763,26 @@ function UpdateUrlbarSearchSplitterState
     if (splitter) {
       splitter.remove();
     }
     return;
   }
 
   // If the splitter is already in the right place, we don't need to do anything:
   if (splitter &&
-      ((splitter.nextSibling == searchbar && splitter.previousSibling == urlbar) ||
-       (splitter.nextSibling == urlbar && splitter.previousSibling == searchbar))) {
+      ((splitter.nextElementSibling == searchbar && splitter.previousElementSibling == urlbar) ||
+       (splitter.nextElementSibling == urlbar && splitter.previousElementSibling == searchbar))) {
     return;
   }
 
   var ibefore = null;
   if (urlbar && searchbar) {
-    if (urlbar.nextSibling == searchbar)
+    if (urlbar.nextElementSibling == searchbar)
       ibefore = searchbar;
-    else if (searchbar.nextSibling == urlbar)
+    else if (searchbar.nextElementSibling == urlbar)
       ibefore = urlbar;
   }
 
   if (ibefore) {
     if (!splitter) {
       splitter = document.createXULElement("splitter");
       splitter.id = "urlbar-search-splitter";
       splitter.setAttribute("resizebefore", "flex");
@@ -4106,17 +4106,17 @@ function FillHistoryMenu(aParent) {
     aParent.addEventListener("DOMMenuItemInactive", function() {
       XULBrowserWindow.setOverLink("");
     });
 
     aParent.hasStatusListener = true;
   }
 
   // Remove old entries if any
-  let children = aParent.childNodes;
+  let children = aParent.children;
   for (var i = children.length - 1; i >= 0; --i) {
     if (children[i].hasAttribute("index"))
       aParent.removeChild(children[i]);
   }
 
   const MAX_HISTORY_MENU_ITEMS = 15;
 
   const tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
@@ -4190,17 +4190,17 @@ function FillHistoryMenu(aParent) {
       }
 
       existingIndex++;
     }
 
     if (!initial) {
       let existingLength = children.length;
       while (existingIndex < existingLength) {
-        aParent.removeChild(aParent.lastChild);
+        aParent.removeChild(aParent.lastElementChild);
         existingIndex++;
       }
     }
   }
 
   let sessionHistory = SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory);
   if (!sessionHistory)
     return false;
@@ -5497,25 +5497,25 @@ nsBrowserAccess.prototype = {
 };
 
 function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
   var popup = aEvent.target;
   if (popup != aEvent.currentTarget)
     return;
 
   // Empty the menu
-  for (var i = popup.childNodes.length - 1; i >= 0; --i) {
-    var deadItem = popup.childNodes[i];
+  for (var i = popup.children.length - 1; i >= 0; --i) {
+    var deadItem = popup.children[i];
     if (deadItem.hasAttribute("toolbarId"))
       popup.removeChild(deadItem);
   }
 
-  var firstMenuItem = aInsertPoint || popup.firstChild;
-
-  let toolbarNodes = gNavToolbox.childNodes;
+  var firstMenuItem = aInsertPoint || popup.firstElementChild;
+
+  let toolbarNodes = gNavToolbox.children;
 
   for (let toolbar of toolbarNodes) {
     if (!toolbar.hasAttribute("toolbarname")) {
       continue;
     }
 
     let menuItem = document.createXULElement("menuitem");
     let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
@@ -5541,17 +5541,17 @@ function onViewToolbarsPopupShowing(aEve
   if (!moveToPanel || !removeFromToolbar) {
     return;
   }
 
   // triggerNode can be a nested child element of a toolbaritem.
   let toolbarItem = popup.triggerNode;
 
   if (toolbarItem && toolbarItem.localName == "toolbarpaletteitem") {
-    toolbarItem = toolbarItem.firstChild;
+    toolbarItem = toolbarItem.firstElementChild;
   } else if (toolbarItem && toolbarItem.localName != "toolbar") {
     while (toolbarItem && toolbarItem.parentNode) {
       let parent = toolbarItem.parentNode;
       if (parent.nodeType !== Node.ELEMENT_NODE ||
           (parent.classList && parent.classList.contains("customization-target")) ||
           parent.getAttribute("overflowfortoolbar") || // Needs to work in the overflow list as well.
           parent.localName == "toolbarpaletteitem" ||
           parent.localName == "toolbar")
@@ -6225,17 +6225,17 @@ function onDownloadsAutoHideChange(event
   let autoHide = event.target.getAttribute("checked") == "true";
   Services.prefs.setBoolPref("browser.download.autohideButton", autoHide);
 }
 
 function getUnwrappedTriggerNode(popup) {
   // Toolbar buttons are wrapped in customize mode. Unwrap if necessary.
   let {triggerNode} = popup;
   if (triggerNode && gCustomizeMode.isWrappedToolbarItem(triggerNode)) {
-    return triggerNode.firstChild;
+    return triggerNode.firstElementChild;
   }
   return triggerNode;
 }
 
 function UpdateManageExtension(popup) {
   let checkbox = popup.querySelector(".customize-context-manageExtension");
   let separator = checkbox.nextElementSibling;
   let node = getUnwrappedTriggerNode(popup);
@@ -6322,21 +6322,21 @@ var gPageStyleMenu = {
       };
     }
 
     return data;
   },
 
   fillPopup(menuPopup) {
     let styleSheetInfo = this._getStyleSheetInfo(gBrowser.selectedBrowser);
-    var noStyle = menuPopup.firstChild;
-    var persistentOnly = noStyle.nextSibling;
-    var sep = persistentOnly.nextSibling;
-    while (sep.nextSibling)
-      menuPopup.removeChild(sep.nextSibling);
+    var noStyle = menuPopup.firstElementChild;
+    var persistentOnly = noStyle.nextElementSibling;
+    var sep = persistentOnly.nextElementSibling;
+    while (sep.nextElementSibling)
+      menuPopup.removeChild(sep.nextElementSibling);
 
     let styleSheets = styleSheetInfo.filteredStyleSheets;
     var currentStyleSheets = {};
     var styleDisabled = styleSheetInfo.authorStyleDisabled;
     var haveAltSheets = false;
     var altStyleSelected = false;
 
     for (let currentStyleSheet of styleSheets) {
@@ -8063,17 +8063,17 @@ TabModalPromptBox.prototype = {
     }
     onCloseCallback.apply(this, args);
   },
 
   appendPrompt(args, onCloseCallback) {
     const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
     let browser = this.browser;
-    browser.parentNode.insertBefore(newPrompt, browser.nextSibling);
+    browser.parentNode.insertBefore(newPrompt, browser.nextElementSibling);
     browser.setAttribute("tabmodalPromptShowing", true);
 
     newPrompt.clientTop; // style flush to assure binding is attached
 
     let prompts = this.listPrompts();
     if (prompts.length > 1) {
       // Let's hide ourself behind the current prompt.
       newPrompt.hidden = true;
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -204,17 +204,17 @@ window._gBrowser = {
 
   get tabContainer() {
     delete this.tabContainer;
     return this.tabContainer = document.getElementById("tabbrowser-tabs");
   },
 
   get tabs() {
     delete this.tabs;
-    return this.tabs = this.tabContainer.childNodes;
+    return this.tabs = this.tabContainer.children;
   },
 
   get tabbox() {
     delete this.tabbox;
     return this.tabbox = document.getElementById("tabbrowser-tabbox");
   },
 
   get tabpanels() {
@@ -312,17 +312,17 @@ window._gBrowser = {
     for (let attribute in this._defaultBrowserAttributes) {
       this._defaultBrowserAttributes[attribute] = browser.getAttribute(attribute);
     }
 
     let tab = this.tabs[0];
     this._selectedTab = tab;
 
     let uniqueId = this._generateUniquePanelID();
-    this.tabpanels.childNodes[0].id = uniqueId;
+    this.tabpanels.children[0].id = uniqueId;
     tab.linkedPanel = uniqueId;
     tab.permanentKey = browser.permanentKey;
     tab._tPos = 0;
     tab._fullyOpen = true;
     tab.linkedBrowser = browser;
     this._tabForBrowser.set(browser, tab);
 
     // Hook the browser up with a progress listener.
@@ -534,17 +534,17 @@ window._gBrowser = {
     aTab.dispatchEvent(event);
 
     return findBar;
   },
 
   _appendStatusPanel() {
     let browser = this.selectedBrowser;
     let browserContainer = this.getBrowserContainer(browser);
-    browserContainer.insertBefore(StatusPanel.panel, browser.parentNode.nextSibling);
+    browserContainer.insertBefore(StatusPanel.panel, browser.parentNode.nextElementSibling);
   },
 
   _updateTabBarForPinnedTabs() {
     this.tabContainer._unlockTabSizing();
     this.tabContainer._positionPinnedTabs();
     this.tabContainer._updateCloseButtons();
   },
 
@@ -3059,24 +3059,24 @@ window._gBrowser = {
       remainingTabs = Array.filter(this.tabs, function(tab) {
         return !tab.closing;
       }, this);
     }
 
     // Try to find a remaining tab that comes after the given tab
     let tab = aTab;
     do {
-      tab = tab.nextSibling;
+      tab = tab.nextElementSibling;
     } while (tab && !remainingTabs.includes(tab));
 
     if (!tab) {
       tab = aTab;
 
       do {
-        tab = tab.previousSibling;
+        tab = tab.previousElementSibling;
       } while (tab && !remainingTabs.includes(tab));
     }
 
     return tab;
   },
 
   _blurTab(aTab) {
     this.selectedTab = this._findTabToBlurTo(aTab);
@@ -3520,16 +3520,22 @@ window._gBrowser = {
     firstInactiveTab.linkedBrowser.addEventListener("EndSwapDocShells", function() {
       for (let i = 1; i < inactiveTabs.length; i++) {
         win.gBrowser.adoptTab(inactiveTabs[i], i);
       }
 
       if (activeTabNewIndex > -1) {
         win.gBrowser.adoptTab(activeTab, activeTabNewIndex, true /* aSelectTab */);
       }
+
+      // Restore tab selection
+      let winVisibleTabs = win.gBrowser.visibleTabs;
+      let winTabLength = winVisibleTabs.length;
+      win.gBrowser.addRangeToMultiSelectedTabs(winVisibleTabs[0],
+                                               winVisibleTabs[winTabLength - 1]);
     }, { once: true });
 
     win = this.replaceTabWithWindow(firstInactiveTab);
     return win;
   },
 
   _updateTabsAfterInsert() {
     for (let i = 0; i < this.tabs.length; i++) {
@@ -3595,19 +3601,19 @@ window._gBrowser = {
     this.tabContainer._setPositionalAttributes();
 
     var evt = document.createEvent("UIEvents");
     evt.initUIEvent("TabMove", true, false, window, oldPosition);
     aTab.dispatchEvent(evt);
   },
 
   moveTabForward() {
-    let nextTab = this.selectedTab.nextSibling;
+    let nextTab = this.selectedTab.nextElementSibling;
     while (nextTab && nextTab.hidden)
-      nextTab = nextTab.nextSibling;
+      nextTab = nextTab.nextElementSibling;
 
     if (nextTab)
       this.moveTabTo(this.selectedTab, nextTab._tPos);
     else if (this.arrowKeysShouldWrap)
       this.moveTabToStart();
   },
 
   /**
@@ -3662,19 +3668,19 @@ window._gBrowser = {
       // for our new tab after we've done swapBrowsersAndCloseOther.
       this.updateCurrentBrowser(true);
     }
 
     return newTab;
   },
 
   moveTabBackward() {
-    let previousTab = this.selectedTab.previousSibling;
+    let previousTab = this.selectedTab.previousElementSibling;
     while (previousTab && previousTab.hidden)
-      previousTab = previousTab.previousSibling;
+      previousTab = previousTab.previousElementSibling;
 
     if (previousTab)
       this.moveTabTo(this.selectedTab, previousTab._tPos);
     else if (this.arrowKeysShouldWrap)
       this.moveTabToEnd();
   },
 
   moveTabToStart() {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -10,17 +10,17 @@
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
     <implementation>
       <!-- Override scrollbox.xml method, since our scrollbox's children are
            inherited from the binding parent -->
       <method name="_getScrollableElements">
         <body><![CDATA[
-          return Array.filter(document.getBindingParent(this).childNodes,
+          return Array.filter(document.getBindingParent(this).children,
                               this._canScrollToElement, this);
         ]]></body>
       </method>
       <method name="_canScrollToElement">
         <parameter name="tab"/>
         <body><![CDATA[
           return !tab._pinnedUnscrollable && !tab.hidden;
         ]]></body>
@@ -104,17 +104,17 @@
         <![CDATA[
           this._tabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
           this._hiddenSoundPlayingTabs = new Set();
 
           let strId = PrivateBrowsingUtils.isWindowPrivate(window) ?
               "emptyPrivateTabTitle" : "emptyTabTitle";
           this.emptyTabTitle = gTabBrowserBundle.GetStringFromName("tabs." + strId);
 
-          var tab = this.firstChild;
+          var tab = this.firstElementChild;
           tab.label = this.emptyTabTitle;
 
           window.addEventListener("resize", this);
 
           Services.prefs.addObserver("privacy.userContext", this);
           this.observe(null, "nsPref:changed", "privacy.userContext.enabled");
 
           XPCOMUtils.defineLazyPreferenceGetter(this, "_tabMinWidthPref",
@@ -198,18 +198,18 @@
               const newTab2 = document.getAnonymousElementByAttribute(this, "anonid", "tabs-newtab-button");
 
               for (let parent of [newTab, newTab2]) {
                 if (!parent)
                   continue;
 
                 gClickAndHoldListenersOnElement.remove(parent);
                 parent.removeAttribute("type");
-                if (parent.firstChild) {
-                  parent.firstChild.remove();
+                if (parent.firstElementChild) {
+                  parent.firstElementChild.remove();
                 }
 
                 if (containersEnabled) {
                   let popup = document.createElementNS(
                                 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                 "menupopup");
                   if (parent.id) {
                     popup.id = "newtab-popup";
@@ -246,17 +246,17 @@
           return document.documentElement.getAttribute("customizing") == "true";
         ]]></getter>
       </property>
 
       <method name="_getVisibleTabs">
         <body><![CDATA[
           // Cannot access gBrowser before it's initialized.
           if (!gBrowser) {
-            return [ this.firstChild ];
+            return [ this.firstElementChild ];
           }
 
           return gBrowser.visibleTabs;
         ]]></body>
       </method>
 
       <method name="_setPositionalAttributes">
         <body><![CDATA[
@@ -438,25 +438,36 @@
             if (isEndTab) {
               let numNormalTabs = tabs.length - numPinned;
               aTabWidth = aTabWidth * (numNormalTabs + 1) / numNormalTabs;
               if (aTabWidth > this._tabDefaultMaxWidth) {
                 aTabWidth = this._tabDefaultMaxWidth;
               }
             }
             aTabWidth += "px";
+            let tabsToReset = [];
             for (let i = numPinned; i < tabs.length; i++) {
               let tab = tabs[i];
               tab.style.setProperty("max-width", aTabWidth, "important");
               if (!isEndTab) { // keep tabs the same width
                 tab.style.transition = "none";
-                window.getComputedStyle(tab); // flush styles to skip animation; see bug 649247
-                tab.style.transition = "";
+                tabsToReset.push(tab);
               }
             }
+
+            if (tabsToReset.length) {
+              window.promiseDocumentFlushed(() => {}).then(() => {
+                window.requestAnimationFrame(() => {
+                  for (let tab of tabsToReset) {
+                    tab.style.transition = "";
+                  }
+                });
+              });
+            }
+
             this._hasTabTempMaxWidth = true;
             gBrowser.addEventListener("mousemove", this);
             window.addEventListener("mouseout", this);
           }
         ]]></body>
       </method>
 
       <method name="_expandSpacerBy">
@@ -512,34 +523,34 @@
 
             let layoutData = this._pinnedTabsLayoutCache;
             let uiDensity = document.documentElement.getAttribute("uidensity");
             if (!layoutData ||
                 layoutData.uiDensity != uiDensity) {
               let arrowScrollbox = this.arrowScrollbox;
               layoutData = this._pinnedTabsLayoutCache = {
                 uiDensity,
-                pinnedTabWidth: this.childNodes[0].getBoundingClientRect().width,
+                pinnedTabWidth: this.children[0].getBoundingClientRect().width,
                 scrollButtonWidth: arrowScrollbox._scrollButtonDown.getBoundingClientRect().width
               };
             }
 
             let width = 0;
             for (let i = numPinned - 1; i >= 0; i--) {
-              let tab = this.childNodes[i];
+              let tab = this.children[i];
               width += layoutData.pinnedTabWidth;
               tab.style.marginInlineStart = -(width + layoutData.scrollButtonWidth) + "px";
               tab._pinnedUnscrollable = true;
             }
             this.style.paddingInlineStart = width + "px";
           } else {
             this.removeAttribute("positionpinnedtabs");
 
             for (let i = 0; i < numPinned; i++) {
-              let tab = this.childNodes[i];
+              let tab = this.children[i];
               tab.style.marginInlineStart = "";
               tab._pinnedUnscrollable = false;
             }
 
             this.style.paddingInlineStart = "";
           }
 
           if (this._lastNumPinned != numPinned) {
@@ -774,17 +785,17 @@
           return tab;
         ]]></body>
       </method>
 
       <method name="_getDropIndex">
         <parameter name="event"/>
         <parameter name="isLink"/>
         <body><![CDATA[
-          var tabs = this.childNodes;
+          var tabs = this.children;
           var tab = this._getDragTargetTab(event, isLink);
           if (window.getComputedStyle(this).direction == "ltr") {
             for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
               if (event.screenX < tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
                 return i;
           } else {
             for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
               if (event.screenX > tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
@@ -873,17 +884,17 @@
         <body>
         <![CDATA[
           if (!aTab) {
             return null;
           }
 
           // Cannot access gBrowser before it's initialized.
           if (!gBrowser) {
-            return this.tabbox.tabpanels.firstChild;
+            return this.tabbox.tabpanels.firstElementChild;
           }
 
           // If the tab's browser is lazy, we need to `_insertBrowser` in order
           // to have a linkedPanel.  This will also serve to bind the browser
           // and make it ready to use when the tab is selected.
           gBrowser._insertBrowser(aTab);
           return document.getElementById(aTab.linkedPanel);
         ]]>
@@ -1354,24 +1365,24 @@
           let maxMargin = Math.min(minMargin + scrollRect.width,
                                    scrollRect.right);
           if (!ltr)
             [minMargin, maxMargin] = [this.clientWidth - maxMargin,
                                       this.clientWidth - minMargin];
           newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
         } else {
           let newIndex = this._getDropIndex(event, effects == "link");
-          if (newIndex == this.childNodes.length) {
-            let tabRect = this.childNodes[newIndex - 1].getBoundingClientRect();
+          if (newIndex == this.children.length) {
+            let tabRect = this.children[newIndex - 1].getBoundingClientRect();
             if (ltr)
               newMargin = tabRect.right - rect.left;
             else
               newMargin = rect.right - tabRect.left;
           } else {
-            let tabRect = this.childNodes[newIndex].getBoundingClientRect();
+            let tabRect = this.children[newIndex].getBoundingClientRect();
             if (ltr)
               newMargin = tabRect.left - rect.left;
             else
               newMargin = rect.right - tabRect.right;
           }
         }
 
         ind.collapsed = false;
@@ -1463,17 +1474,24 @@
                 gBrowser.moveTabTo(tab, dropIndex);
                 if (incrementDropIndex)
                   dropIndex++;
               }
             }
           }
         } else if (draggedTab) {
           let newIndex = this._getDropIndex(event, false);
-          gBrowser.adoptTab(draggedTab, newIndex, true);
+          let newTabs = [];
+          for (let tab of movingTabs) {
+            let newTab = gBrowser.adoptTab(tab, newIndex++, tab == draggedTab);
+            newTabs.push(newTab);
+          }
+
+          // Restore tab selection
+          gBrowser.addRangeToMultiSelectedTabs(newTabs[0], newTabs[newTabs.length - 1]);
         } else {
           // Pass true to disallow dropping javascript: or data: urls
           let links;
           try {
             links = browserDragAndDrop.dropLinks(event, true);
           } catch (ex) {}
 
           if (!links || links.length === 0)
@@ -1590,17 +1608,17 @@
           window.moveTo(left, top);
           window.focus();
         } else {
           let props = { screenX: left, screenY: top, suppressanimation: 1 };
           if (AppConstants.platform != "win") {
             props.outerWidth = winWidth;
             props.outerHeight = winHeight;
           }
-          gBrowser.replaceTabWithWindow(draggedTab, props);
+          gBrowser.replaceTabsWithWindow(draggedTab, props);
         }
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragexit"><![CDATA[
         this._dragTime = 0;
 
         // This does not work at all (see bug 458613)
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -23,16 +23,17 @@ support-files =
 [browser_multiselect_tabs_bookmark.js]
 [browser_multiselect_tabs_clear_selection_when_tab_switch.js]
 [browser_multiselect_tabs_close_other_tabs.js]
 [browser_multiselect_tabs_close_tabs_to_the_right.js]
 [browser_multiselect_tabs_close_using_shortcuts.js]
 [browser_multiselect_tabs_close.js]
 [browser_multiselect_tabs_copy_through_drag_and_drop.js]
 [browser_multiselect_tabs_event.js]
+[browser_multiselect_tabs_move_to_another_window_drag.js]
 [browser_multiselect_tabs_move_to_new_window_contextmenu.js]
 [browser_multiselect_tabs_mute_unmute.js]
 [browser_multiselect_tabs_pin_unpin.js]
 [browser_multiselect_tabs_positional_attrs.js]
 [browser_multiselect_tabs_reload.js]
 [browser_multiselect_tabs_reorder.js]
 [browser_multiselect_tabs_using_Ctrl.js]
 [browser_multiselect_tabs_using_selectedTabs.js]
--- a/browser/base/content/test/tabs/browser_multiselect_tabs_copy_through_drag_and_drop.js
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_copy_through_drag_and_drop.js
@@ -1,12 +1,9 @@
 const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
-function url(tab) {
-  return tab.linkedBrowser.currentURI.spec;
-}
 
 add_task(async function setPref() {
   await SpecialPowers.pushPrefEnv({
     set: [[PREF_MULTISELECT_TABS, true]]
   });
 });
 
 add_task(async function test() {
@@ -43,17 +40,17 @@ add_task(async function test() {
 
   for (let i of [1, 2]) {
     ok(tabs[i].multiselected, "Tab" + i + " is multiselected");
   }
   for (let i of [0, 3, 4, 5]) {
     ok(!tabs[i].multiselected, "Tab" + i + " is not multiselected");
   }
 
-  await BrowserTestUtils.waitForCondition(() => url(tab4) == url(tab1));
-  await BrowserTestUtils.waitForCondition(() => url(tab5) == url(tab2));
+  await BrowserTestUtils.waitForCondition(() => getUrl(tab4) == getUrl(tab1));
+  await BrowserTestUtils.waitForCondition(() => getUrl(tab5) == getUrl(tab2));
 
   ok(true, "Tab1 and tab2 are duplicated succesfully");
 
   for (let tab of tabs.filter(t => t != tab0))
     BrowserTestUtils.removeTab(tab);
 });
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_another_window_drag.js
@@ -0,0 +1,71 @@
+const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
+const PREF_ANIMATIONS_ENABLED = "toolkit.cosmeticAnimations.enabled";
+
+add_task(async function setPref() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      [PREF_MULTISELECT_TABS, true],
+      [PREF_ANIMATIONS_ENABLED, false]
+    ]
+  });
+});
+
+add_task(async function test() {
+  let tab1 = await addTab();
+  let tab2 = await addTab();
+  let tab3 = await addTab("http://mochi.test:8888/3");
+  let tab4 = await addTab();
+  let tab5 = await addTab("http://mochi.test:8888/5");
+
+  is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+
+  await BrowserTestUtils.switchTab(gBrowser, tab2);
+  await triggerClickOn(tab1, { ctrlKey: true });
+
+  ok(tab1.multiselected, "Tab1 is multiselected");
+  ok(tab2.multiselected, "Tab2 is multiselected");
+  ok(!tab3.multiselected, "Tab3 is not multiselected");
+  ok(!tab4.multiselected, "Tab4 is not multiselected");
+  ok(!tab5.multiselected, "Tab5 is not multiselected");
+  is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+
+  let newWindow = gBrowser.replaceTabsWithWindow(tab1);
+
+  // waiting for tab2 to close ensure that the newWindow is created,
+  // thus newWindow.gBrowser used in the second waitForCondition
+  // will not be undefined.
+  await TestUtils.waitForCondition(() => tab2.closing, "Wait for tab2 to close");
+  await TestUtils.waitForCondition(() => newWindow.gBrowser.visibleTabs.length == 2,
+    "Wait for all two tabs to get moved to the new window");
+
+  let gBrowser2 = newWindow.gBrowser;
+  tab1 = gBrowser2.visibleTabs[0];
+  tab2 = gBrowser2.visibleTabs[1];
+
+  if (gBrowser.selectedTab != tab3)
+    await BrowserTestUtils.switchTab(gBrowser, tab3);
+
+  await triggerClickOn(tab5, { ctrlKey: true });
+
+  ok(tab1.multiselected, "Tab1 is multiselected");
+  ok(tab2.multiselected, "Tab2 is multiselected");
+  ok(tab3.multiselected, "Tab3 is multiselected");
+  ok(!tab4.multiselected, "Tab4 is not multiselected");
+  ok(tab5.multiselected, "Tab5 is multiselected");
+
+  await dragAndDrop(tab3, tab1, false, newWindow);
+
+  await TestUtils.waitForCondition(() => gBrowser2.visibleTabs.length == 4,
+    "Moved tab3 and tab5 to second window");
+
+  tab3 = gBrowser2.visibleTabs[1];
+  tab5 = gBrowser2.visibleTabs[2];
+
+  await BrowserTestUtils.waitForCondition(() => getUrl(tab3) == "http://mochi.test:8888/3");
+  await BrowserTestUtils.waitForCondition(() => getUrl(tab5) == "http://mochi.test:8888/5");
+
+  ok(true, "Tab3 and tab5 are duplicated succesfully");
+
+  BrowserTestUtils.closeWindow(newWindow);
+  BrowserTestUtils.removeTab(tab4);
+});
--- a/browser/base/content/test/tabs/head.js
+++ b/browser/base/content/test/tabs/head.js
@@ -152,24 +152,37 @@ async function test_mute_tab(tab, icon, 
   let isAudioPlaying = await is_audio_playing(tab);
   if (isAudioPlaying) {
     await wait_for_tab_playing_event(tab, !expectMuted);
   }
 
   return mutedPromise;
 }
 
-async function dragAndDrop(tab1, tab2, copy) {
+async function dragAndDrop(tab1, tab2, copy, destWindow = window) {
   let rect = tab2.getBoundingClientRect();
   let event = {
     ctrlKey: copy,
     altKey: copy,
     clientX: rect.left + rect.width / 2 + 10,
     clientY: rect.top + rect.height / 2,
   };
 
+  if (destWindow != window) {
+    // Make sure that both tab1 and tab2 are visible
+    window.focus();
+    window.moveTo(rect.left, rect.top + rect.height * 3);
+  }
+
   let originalTPos = tab1._tPos;
-  EventUtils.synthesizeDrop(tab1, tab2, null, copy ? "copy" : "move", window, window, event);
-  if (!copy) {
+  EventUtils.synthesizeDrop(tab1, tab2, null, copy ? "copy" : "move", window, destWindow, event);
+  if (!copy && destWindow == window) {
     await BrowserTestUtils.waitForCondition(() => tab1._tPos != originalTPos,
       "Waiting for tab position to be updated");
+  } else if (destWindow != window) {
+    await BrowserTestUtils.waitForCondition(() => tab1.closing,
+      "Waiting for tab closing");
   }
 }
+
+function getUrl(tab) {
+  return tab.linkedBrowser.currentURI.spec;
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -106,29 +106,29 @@ file, You can obtain one at http://mozil
           var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
           var enabled = controller.isCommandEnabled("cmd_paste");
           if (enabled)
             pasteAndGo.removeAttribute("disabled");
           else
             pasteAndGo.setAttribute("disabled", "true");
         });
 
-        var insertLocation = cxmenu.firstChild;
-        while (insertLocation.nextSibling &&
+        var insertLocation = cxmenu.firstElementChild;
+        while (insertLocation.nextElementSibling &&
                insertLocation.getAttribute("cmd") != "cmd_paste")
-          insertLocation = insertLocation.nextSibling;
+          insertLocation = insertLocation.nextElementSibling;
         if (insertLocation) {
           pasteAndGo = document.createXULElement("menuitem");
           let label = Services.strings.createBundle("chrome://browser/locale/browser.properties").
                                    GetStringFromName("pasteAndGo.label");
           pasteAndGo.setAttribute("label", label);
           pasteAndGo.setAttribute("anonid", "paste-and-go");
           pasteAndGo.setAttribute("oncommand",
               "gURLBar.select(); goDoCommand('cmd_paste'); gURLBar.handleCommand();");
-          cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
+          cxmenu.insertBefore(pasteAndGo, insertLocation.nextElementSibling);
         }
 
         this.popup.addEventListener("popupshowing", () => {
           this._enableOrDisableOneOffSearches();
         }, {capture: true, once: true});
 
         // history dropmarker open state
         this.popup.addEventListener("popupshowing", () => {
@@ -2230,34 +2230,34 @@ file, You can obtain one at http://mozil
             window.windowUtils.getBoundsWithoutFlushing(document.getElementById("nav-bar")).bottom -
             window.windowUtils.getBoundsWithoutFlushing(aInput).bottom);
 
           this.openPopup(aElement, "after_start", 0, yOffset, false, false);
 
           // Do this immediately after we've requested the popup to open. This
           // will cause sync reflows but prevents flickering.
           if (needsHandleOverUnderflow) {
-            for (let item of this.richlistbox.childNodes) {
+            for (let item of this.richlistbox.children) {
               item.handleOverUnderflow();
             }
           }
         ]]></body>
       </method>
 
       <method name="adjustHeight">
         <body>
           <![CDATA[
           // If we were going to shrink later, cancel that for now:
           if (this._shrinkTimeout) {
             clearTimeout(this._shrinkTimeout);
             this._shrinkTimeout = null;
           }
           let lastRowCount = this._lastRowCount;
           // Figure out how many rows to show
-          let rows = this.richlistbox.childNodes;
+          let rows = this.richlistbox.children;
           this._lastRowCount = rows.length;
           let numRows = Math.min(this.matchCount, this.maxRows, rows.length);
 
           // If we're going from 0 to non-0 rows, we might need to remove
           // the height attribute to allow the popup to size. The attribute
           // is set from XUL popup management code.
           if (!lastRowCount && rows.length) {
             this.removeAttribute("height");
@@ -2386,17 +2386,17 @@ file, You can obtain one at http://mozil
           ]]>
         </body>
       </method>
 
       <method name="_selectedOneOffChanged">
         <body><![CDATA[
           // Update all searchengine result items to use the newly selected
           // engine.
-          for (let item of this.richlistbox.childNodes) {
+          for (let item of this.richlistbox.children) {
             if (item.collapsed) {
               break;
             }
             let url = item.getAttribute("url");
             if (url) {
               let action = item._parseActionUrl(url);
               if (action && action.type == "searchengine") {
                 item._adjustAcItem();
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -786,30 +786,30 @@ var CustomizableUIInternal = {
     // through a migration path (bug 938980) or an add-on.
     if (aArea == CustomizableUI.AREA_NAVBAR) {
       aAreaNode.collapsed = false;
     }
 
     this.beginBatchUpdate();
 
     try {
-      let currentNode = container.firstChild;
+      let currentNode = container.firstElementChild;
       let placementsToRemove = new Set();
       for (let id of aPlacements) {
         while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
-          currentNode = currentNode.nextSibling;
+          currentNode = currentNode.nextElementSibling;
         }
 
         // Fix ids for specials and continue, for correctly placed specials.
         if (currentNode && (!currentNode.id || CustomizableUI.isSpecialWidget(currentNode)) &&
             this.matchingSpecials(id, currentNode)) {
           currentNode.id = id;
         }
         if (currentNode && currentNode.id == id) {
-          currentNode = currentNode.nextSibling;
+          currentNode = currentNode.nextElementSibling;
           continue;
         }
 
         if (this.isSpecialWidget(id) && areaIsPanel) {
           placementsToRemove.add(id);
           continue;
         }
 
@@ -849,20 +849,20 @@ var CustomizableUIInternal = {
           this.notifyListeners("onWidgetReset", node, container);
         } else if (gUndoResetting) {
           this.notifyListeners("onWidgetUndoMove", node, container);
         }
       }
 
       if (currentNode) {
         let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
-        let limit = currentNode.previousSibling;
-        let node = container.lastChild;
+        let limit = currentNode.previousElementSibling;
+        let node = container.lastElementChild;
         while (node && node != limit) {
-          let previousSibling = node.previousSibling;
+          let previousSibling = node.previousElementSibling;
           // Nodes opt-in to removability. If they're removable, and we haven't
           // seen them in the placements array, then we toss them into the palette
           // if one exists. If no palette exists, we just remove the node. If the
           // node is not removable, we leave it where it is. However, we can only
           // safely touch elements that have an ID - both because we depend on
           // IDs (or are specials), and because such elements are not intended to
           // be widgets (eg, titlebar-placeholder elements).
           if ((node.id || this.isSpecialWidget(node)) &&
@@ -3898,17 +3898,17 @@ var CustomizableUI = {
     for (let menuChild of aMenuItems) {
       if (menuChild.hidden)
         continue;
 
       let subviewItem;
       if (menuChild.localName == "menuseparator") {
         // Don't insert duplicate or leading separators. This can happen if there are
         // menus (which we don't copy) above the separator.
-        if (!fragment.lastChild || fragment.lastChild.localName == "menuseparator") {
+        if (!fragment.lastElementChild || fragment.lastElementChild.localName == "menuseparator") {
           continue;
         }
         subviewItem = doc.createElementNS(kNSXUL, "menuseparator");
       } else if (menuChild.localName == "menuitem") {
         subviewItem = doc.createElementNS(kNSXUL, "toolbarbutton");
         CustomizableUI.addShortcut(menuChild, subviewItem);
 
         let item = menuChild;
@@ -4435,39 +4435,39 @@ OverflowableToolbar.prototype = {
    *        in some cases (e.g. when we run this method after overflow handling
    *        is re-enabled from customize mode, to ensure correct handling of
    *        initial overflow).
    */
   async onOverflow(aEvent) {
     if (!this._enabled)
       return;
 
-    let child = this._target.lastChild;
+    let child = this._target.lastElementChild;
 
     let thisOverflowResponse = ++this._lastOverflowCounter;
 
     let win = this._target.ownerGlobal;
     let [scrollLeftMin, scrollLeftMax] = await win.promiseDocumentFlushed(() => {
       return [this._target.scrollLeftMin, this._target.scrollLeftMax];
     });
     if (win.closed || this._lastOverflowCounter != thisOverflowResponse) {
       return;
     }
 
     while (child && scrollLeftMin != scrollLeftMax) {
-      let prevChild = child.previousSibling;
+      let prevChild = child.previousElementSibling;
 
       if (child.getAttribute("overflows") != "false") {
         this._collapsed.set(child.id, this._target.clientWidth);
         child.setAttribute("overflowedItem", true);
         child.setAttribute("cui-anchorid", this._chevron.id);
         CustomizableUIInternal.ensureButtonContextMenu(child, this._toolbar, true);
         CustomizableUIInternal.notifyListeners("onWidgetOverflow", child, this._target);
 
-        this._list.insertBefore(child, this._list.firstChild);
+        this._list.insertBefore(child, this._list.firstElementChild);
         if (!this._addedListener) {
           CustomizableUI.addListener(this);
         }
         if (!CustomizableUI.isSpecialWidget(child.id)) {
           this._toolbar.setAttribute("overflowing", "true");
         }
       }
       child = prevChild;
@@ -4508,18 +4508,18 @@ OverflowableToolbar.prototype = {
    *        While there are items in the list, this width won't change, and so
    *        we can avoid flushing layout by providing it and/or caching it.
    *        Note that if `shouldMoveAllItems` is true, we never need the width
    *        anyway.
    */
   _moveItemsBackToTheirOrigin(shouldMoveAllItems, targetWidth) {
     let placements = gPlacements.get(this._toolbar.id);
     let win = this._target.ownerGlobal;
-    while (this._list.firstChild) {
-      let child = this._list.firstChild;
+    while (this._list.firstElementChild) {
+      let child = this._list.firstElementChild;
       let minSize = this._collapsed.get(child.id);
 
       if (!shouldMoveAllItems && minSize) {
         if (!targetWidth) {
           let dwu = win.windowUtils;
           targetWidth = Math.floor(dwu.getBoundsWithoutFlushing(this._target).width);
         }
         if (targetWidth <= minSize) {
@@ -4603,26 +4603,26 @@ OverflowableToolbar.prototype = {
     if (aContainer != this._target && aContainer != this._list) {
       return;
     }
     // When we (re)move an item, update all the items that come after it in the list
     // with the minsize *of the item before the to-be-removed node*. This way, we
     // ensure that we try to move items back as soon as that's possible.
     if (aNode.parentNode == this._list) {
       let updatedMinSize;
-      if (aNode.previousSibling) {
-        updatedMinSize = this._collapsed.get(aNode.previousSibling.id);
+      if (aNode.previousElementSibling) {
+        updatedMinSize = this._collapsed.get(aNode.previousElementSibling.id);
       } else {
         // Force (these) items to try to flow back into the bar:
         updatedMinSize = 1;
       }
-      let nextItem = aNode.nextSibling;
+      let nextItem = aNode.nextElementSibling;
       while (nextItem) {
         this._collapsed.set(nextItem.id, updatedMinSize);
-        nextItem = nextItem.nextSibling;
+        nextItem = nextItem.nextElementSibling;
       }
     }
   },
 
   onWidgetAfterDOMChange(aNode, aNextNode, aContainer) {
     if (aContainer != this._target && aContainer != this._list) {
       return;
     }
@@ -4632,17 +4632,17 @@ OverflowableToolbar.prototype = {
     let wasOverflowed = this._collapsed.has(aNode.id);
 
     // If this wasn't overflowed before...
     if (!wasOverflowed) {
       // ... but it is now, then we added to the overflow panel. Exciting stuff:
       if (nowOverflowed) {
         // NB: we're guaranteed that it has a previousSibling, because if it didn't,
         // we would have added it to the toolbar instead. See getOverflowedNextNode.
-        let prevId = aNode.previousSibling.id;
+        let prevId = aNode.previousElementSibling.id;
         let minSize = this._collapsed.get(prevId);
         this._collapsed.set(aNode.id, minSize);
         aNode.setAttribute("cui-anchorid", this._chevron.id);
         aNode.setAttribute("overflowedItem", true);
         CustomizableUIInternal.ensureButtonContextMenu(aNode, aContainer, true);
         CustomizableUIInternal.notifyListeners("onWidgetOverflow", aNode, this._target);
       } else if (!nowInBar) {
         // If it is not overflowed and not in the toolbar, and was not overflowed
@@ -4664,19 +4664,19 @@ OverflowableToolbar.prototype = {
       let collapsedWidgetIds = Array.from(this._collapsed.keys());
       if (collapsedWidgetIds.every(w => CustomizableUI.isSpecialWidget(w))) {
         this._toolbar.removeAttribute("overflowing");
       }
       if (this._addedListener && !this._collapsed.size) {
         CustomizableUI.removeListener(this);
         this._addedListener = false;
       }
-    } else if (aNode.previousSibling) {
+    } else if (aNode.previousElementSibling) {
       // but if it still is, it must have changed places. Bookkeep:
-      let prevId = aNode.previousSibling.id;
+      let prevId = aNode.previousElementSibling.id;
       let minSize = this._collapsed.get(prevId);
       this._collapsed.set(aNode.id, minSize);
     } else {
       // If it's now the first item in the overflow list,
       // maybe we can return it:
       this._moveItemsBackToTheirOrigin(false);
     }
   },
@@ -4740,17 +4740,17 @@ OverflowableToolbar.prototype = {
   _hideTimeoutId: null,
   _showWithTimeout() {
     this.show().then(() => {
       let window = this._toolbar.ownerGlobal;
       if (this._hideTimeoutId) {
         window.clearTimeout(this._hideTimeoutId);
       }
       this._hideTimeoutId = window.setTimeout(() => {
-        if (!this._panel.firstChild.matches(":hover")) {
+        if (!this._panel.firstElementChild.matches(":hover")) {
           PanelMultiView.hidePopup(this._panel);
         }
       }, OVERFLOW_PANEL_HIDE_DELAY_MS);
     });
   },
 };
 
 CustomizableUIInternal.initialize();
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -142,17 +142,17 @@ const CustomizableWidgets = [
       if (!elementCount)
         return;
 
       let body = document.createElement("vbox");
       body.className = "panel-subview-body";
       body.appendChild(fragment);
       let footer;
       while (--elementCount >= 0) {
-        let element = body.childNodes[elementCount];
+        let element = body.children[elementCount];
         CustomizableUI.addShortcut(element);
         element.classList.add("subviewbutton");
         if (element.classList.contains("restoreallitem")) {
           footer = element;
           element.classList.add("panel-subview-footer");
         } else {
           element.classList.add("subviewbutton-iconic", "bookmark-item");
         }
@@ -396,29 +396,29 @@ const CustomizableWidgets = [
       }
     },
     updateCurrentCharset(aDocument) {
       let currentCharset = aDocument.defaultView.gBrowser.selectedBrowser.characterSet;
       currentCharset = CharsetMenu.foldCharset(currentCharset);
 
       let pinnedContainer = aDocument.getElementById("PanelUI-characterEncodingView-pinned");
       let charsetContainer = aDocument.getElementById("PanelUI-characterEncodingView-charsets");
-      let elements = [...(pinnedContainer.childNodes), ...(charsetContainer.childNodes)];
+      let elements = [...(pinnedContainer.children), ...(charsetContainer.children)];
 
       this._updateElements(elements, currentCharset);
     },
     updateCurrentDetector(aDocument) {
       let detectorContainer = aDocument.getElementById("PanelUI-characterEncodingView-autodetect");
       let currentDetector;
       try {
         currentDetector = Services.prefs.getComplexValue(
           "intl.charset.detector", Ci.nsIPrefLocalizedString).data;
       } catch (e) {}
 
-      this._updateElements(detectorContainer.childNodes, currentDetector);
+      this._updateElements(detectorContainer.children, currentDetector);
     },
     _updateElements(aElements, aCurrentItem) {
       if (!aElements.length) {
         return;
       }
       let disabled = this.maybeDisableMenu(aElements[0].ownerDocument);
       for (let elem of aElements) {
         if (disabled) {
@@ -638,17 +638,17 @@ if (Services.prefs.getBoolPref("identity
 
         this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
         this._clearTabList();
         SyncedTabs.sortTabClientsByLastUsed(clients);
         let fragment = doc.createDocumentFragment();
 
         for (let client of clients) {
           // add a menu separator for all clients other than the first.
-          if (fragment.lastChild) {
+          if (fragment.lastElementChild) {
             let separator = doc.createElementNS(kNSXUL, "menuseparator");
             fragment.appendChild(separator);
           }
           if (paginationInfo && paginationInfo.clientId == client.id) {
             this._appendClient(client, fragment, paginationInfo.maxTabs);
           } else {
             this._appendClient(client, fragment);
           }
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -436,17 +436,17 @@ CustomizeMode.prototype = {
       // And drop all area references.
       this.areas.clear();
 
       // Let everybody in this window know that we're starting to
       // exit customization mode.
       CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);
 
       window.PanelUI.menuButton.disabled = false;
-      let overflowContainer = document.getElementById("widget-overflow-mainView").firstChild;
+      let overflowContainer = document.getElementById("widget-overflow-mainView").firstElementChild;
       overflowContainer.appendChild(window.PanelUI.overflowFixedList);
       document.getElementById("nav-bar-overflow-button").disabled = false;
       let panelContextMenu = document.getElementById(kPanelItemContextMenu);
       this._previousPanelContextMenuParent.appendChild(panelContextMenu);
 
       // We need to set this._customizing to false before removing the tab
       // or the TabSelect event handler will think that we are exiting
       // customization mode for a second time.
@@ -579,18 +579,18 @@ CustomizeMode.prototype = {
           animationNode.addEventListener("animationend", cleanupWidgetAnimationEnd);
         });
       });
     });
   },
 
   async addToToolbar(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
-    if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
-      aNode = aNode.firstChild;
+    if (aNode.localName == "toolbarpaletteitem" && aNode.firstElementChild) {
+      aNode = aNode.firstElementChild;
     }
     let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode);
     if (widgetAnimationPromise) {
       await widgetAnimationPromise;
     }
 
     let widgetToAdd = aNode.id;
     if (CustomizableUI.isSpecialWidget(widgetToAdd) && aNode.closest("#customization-palette")) {
@@ -616,18 +616,18 @@ CustomizeMode.prototype = {
       } else {
         aNode.classList.remove("animate-out");
       }
     }
   },
 
   async addToPanel(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
-    if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
-      aNode = aNode.firstChild;
+    if (aNode.localName == "toolbarpaletteitem" && aNode.firstElementChild) {
+      aNode = aNode.firstElementChild;
     }
     let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode);
     if (widgetAnimationPromise) {
       await widgetAnimationPromise;
     }
 
     let panel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL;
     CustomizableUI.addWidgetToArea(aNode.id, panel);
@@ -664,18 +664,18 @@ CustomizeMode.prototype = {
           }
         });
       });
     }
   },
 
   async removeFromArea(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
-    if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
-      aNode = aNode.firstChild;
+    if (aNode.localName == "toolbarpaletteitem" && aNode.firstElementChild) {
+      aNode = aNode.firstElementChild;
     }
     let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode);
     if (widgetAnimationPromise) {
       await widgetAnimationPromise;
     }
 
     CustomizableUI.removeWidgetFromArea(aNode.id);
     if (!this._customizing) {
@@ -741,21 +741,21 @@ CustomizeMode.prototype = {
     let wrapper = this.createOrUpdateWrapper(widgetNode, aPlace);
     wrapper.appendChild(widgetNode);
     return wrapper;
   },
 
   depopulatePalette() {
     return (async () => {
       this.visiblePalette.hidden = true;
-      let paletteChild = this.visiblePalette.firstChild;
+      let paletteChild = this.visiblePalette.firstElementChild;
       let nextChild;
       while (paletteChild) {
         nextChild = paletteChild.nextElementSibling;
-        let itemId = paletteChild.firstChild.id;
+        let itemId = paletteChild.firstElementChild.id;
         if (CustomizableUI.isSpecialWidget(itemId)) {
           this.visiblePalette.removeChild(paletteChild);
         } else {
           // XXXunf Currently this doesn't destroy the (now unused) node in the
           //       API provider case. It would be good to do so, but we need to
           //       keep strong refs to it in CustomizableUI (can't iterate of
           //       WeakMaps), and there's the question of what behavior
           //       wrappers should have if consumers keep hold of them.
@@ -920,17 +920,17 @@ CustomizeMode.prototype = {
     if (aWrapper.nodeName != "toolbarpaletteitem") {
       return aWrapper;
     }
     aWrapper.removeEventListener("mousedown", this);
     aWrapper.removeEventListener("mouseup", this);
 
     let place = aWrapper.getAttribute("place");
 
-    let toolbarItem = aWrapper.firstChild;
+    let toolbarItem = aWrapper.firstElementChild;
     if (!toolbarItem) {
       log.error("no toolbarItem child for " + aWrapper.tagName + "#" + aWrapper.id);
       aWrapper.remove();
       return null;
     }
 
     if (aWrapper.hasAttribute("itemobserves")) {
       toolbarItem.setAttribute("observes", aWrapper.getAttribute("itemobserves"));
@@ -1454,27 +1454,27 @@ CustomizeMode.prototype = {
         LightweightThemeManager.setLocalTheme(button.theme);
         recommendedThemes = recommendedThemes.filter((aTheme) => { return aTheme.id != button.theme.id; });
         lwthemePrefs.setStringPref("recommendedThemes",
                                    JSON.stringify(recommendedThemes));
         onThemeSelected(panel);
       });
       panel.insertBefore(button, footer);
     }
-    let hideRecommendedLabel = (footer.previousSibling == recommendedLabel);
+    let hideRecommendedLabel = (footer.previousElementSibling == recommendedLabel);
     recommendedLabel.hidden = hideRecommendedLabel;
   },
 
   _clearLWThemesMenu(panel) {
     let footer = this.$("customization-lwtheme-menu-footer");
     let recommendedLabel = this.$("customization-lwtheme-menu-recommended");
     for (let element of [footer, recommendedLabel]) {
-      while (element.previousSibling &&
-             element.previousSibling.localName == "toolbarbutton") {
-        element.previousSibling.remove();
+      while (element.previousElementSibling &&
+             element.previousElementSibling.localName == "toolbarbutton") {
+        element.previousElementSibling.remove();
       }
     }
 
     // Workaround for bug 1059934
     panel.removeAttribute("height");
   },
 
   _onUIChange() {
@@ -1662,17 +1662,17 @@ CustomizeMode.prototype = {
     while (item && item.localName != "toolbarpaletteitem") {
       if (item.localName == "toolbar" || item.id == kPaletteId ||
           item.id == "customization-panelHolder") {
         return;
       }
       item = item.parentNode;
     }
 
-    let draggedItem = item.firstChild;
+    let draggedItem = item.firstElementChild;
     let placeForItem = CustomizableUI.getPlaceForItem(item);
 
     let dt = aEvent.dataTransfer;
     let documentId = aEvent.target.ownerDocument.documentElement.id;
 
     dt.mozSetDataAt(kDragDataTypePrefix + documentId, draggedItem.id, 0);
     dt.effectAllowed = "move";
 
@@ -1695,22 +1695,22 @@ CustomizeMode.prototype = {
     this._initializeDragAfterMove = () => {
       // For automated tests, we sometimes start exiting customization mode
       // before this fires, which leaves us with placeholders inserted after
       // we've exited. So we need to check that we are indeed customizing.
       if (this._customizing && !this._transitioning) {
         item.hidden = true;
         DragPositionManager.start(this.window);
         let canUsePrevSibling = placeForItem == "toolbar" || placeForItem == "menu-panel";
-        if (item.nextSibling) {
-          this._setDragActive(item.nextSibling, "before", draggedItem.id, placeForItem);
-          this._dragOverItem = item.nextSibling;
-        } else if (canUsePrevSibling && item.previousSibling) {
-          this._setDragActive(item.previousSibling, "after", draggedItem.id, placeForItem);
-          this._dragOverItem = item.previousSibling;
+        if (item.nextElementSibling) {
+          this._setDragActive(item.nextElementSibling, "before", draggedItem.id, placeForItem);
+          this._dragOverItem = item.nextElementSibling;
+        } else if (canUsePrevSibling && item.previousElementSibling) {
+          this._setDragActive(item.previousElementSibling, "after", draggedItem.id, placeForItem);
+          this._dragOverItem = item.previousElementSibling;
         }
         let currentArea = this._getCustomizableParent(item);
         currentArea.setAttribute("draggingover", "true");
       }
       this._initializeDragAfterMove = null;
       this.window.clearTimeout(this._dragInitializeTimeout);
     };
     this._dragInitializeTimeout = this.window.setTimeout(this._initializeDragAfterMove, 0);
@@ -1760,27 +1760,27 @@ CustomizeMode.prototype = {
 
     // We need to determine the place that the widget is being dropped in
     // the target.
     let dragOverItem, dragValue;
     if (targetNode == targetArea.customizationTarget) {
       // We'll assume if the user is dragging directly over the target, that
       // they're attempting to append a child to that target.
       dragOverItem = (targetAreaType == "toolbar"
-                        ? this._findVisiblePreviousSiblingNode(targetNode.lastChild)
-                        : targetNode.lastChild) ||
+                        ? this._findVisiblePreviousSiblingNode(targetNode.lastElementChild)
+                        : targetNode.lastElementChild) ||
                      targetNode;
       dragValue = "after";
     } else {
       let targetParent = targetNode.parentNode;
       let position = Array.indexOf(targetParent.children, targetNode);
       if (position == -1) {
         dragOverItem = (targetAreaType == "toolbar"
-                          ? this._findVisiblePreviousSiblingNode(targetNode.lastChild)
-                          : targetNode.lastChild);
+                          ? this._findVisiblePreviousSiblingNode(targetNode.lastElementChild)
+                          : targetNode.lastElementChild);
         dragValue = "after";
       } else {
         dragOverItem = targetParent.children[position];
         if (targetAreaType == "toolbar") {
           // Check if the aDraggedItem is hovered past the first half of dragOverItem
           let itemRect = this._getBoundsWithoutFlushing(dragOverItem);
           let dropTargetCenter = itemRect.left + (itemRect.width / 2);
           let existingDir = dragOverItem.getAttribute("dragover");
@@ -1846,24 +1846,24 @@ CustomizeMode.prototype = {
     // Do nothing if the target area or origin area are not customizable.
     if (!targetArea || !originArea) {
       return;
     }
     let targetNode = this._dragOverItem;
     let dropDir = targetNode.getAttribute("dragover");
     // Need to insert *after* this node if we promised the user that:
     if (targetNode != targetArea && dropDir == "after") {
-      if (targetNode.nextSibling) {
-        targetNode = targetNode.nextSibling;
+      if (targetNode.nextElementSibling) {
+        targetNode = targetNode.nextElementSibling;
       } else {
         targetNode = targetArea;
       }
     }
     if (targetNode.tagName == "toolbarpaletteitem") {
-      targetNode = targetNode.firstChild;
+      targetNode = targetNode.firstElementChild;
     }
 
     this._cancelDragActive(this._dragOverItem, null, true);
 
     try {
       this._applyDrop(aEvent, targetArea, originArea, draggedItemId, targetNode);
     } catch (ex) {
       log.error(ex, ex.stack);
@@ -1954,24 +1954,24 @@ CustomizeMode.prototype = {
     // We need to determine the place that the widget is being dropped in
     // the target.
     let placement;
     let itemForPlacement = aTargetNode;
     // Skip the skipintoolbarset items when determining the place of the item:
     while (itemForPlacement && itemForPlacement.getAttribute("skipintoolbarset") == "true" &&
            itemForPlacement.parentNode &&
            itemForPlacement.parentNode.nodeName == "toolbarpaletteitem") {
-      itemForPlacement = itemForPlacement.parentNode.nextSibling;
+      itemForPlacement = itemForPlacement.parentNode.nextElementSibling;
       if (itemForPlacement && itemForPlacement.nodeName == "toolbarpaletteitem") {
-        itemForPlacement = itemForPlacement.firstChild;
+        itemForPlacement = itemForPlacement.firstElementChild;
       }
     }
     if (itemForPlacement) {
       let targetNodeId = (itemForPlacement.nodeName == "toolbarpaletteitem") ?
-                            itemForPlacement.firstChild && itemForPlacement.firstChild.id :
+                            itemForPlacement.firstElementChild && itemForPlacement.firstElementChild.id :
                             itemForPlacement.id;
       placement = CustomizableUI.getPlacementOfWidget(targetNodeId);
     }
     if (!placement) {
       log.debug("Could not get a position for " + aTargetNode.nodeName + "#" + aTargetNode.id + "." + aTargetNode.className);
     }
     let position = placement ? placement.position : null;
 
@@ -2188,17 +2188,17 @@ CustomizeMode.prototype = {
     let currentArea = this._getCustomizableParent(aDraggedItem);
     // Return the size for this target from cache, if it exists.
     let size = itemMap.get(targetArea);
     if (size)
       return size;
 
     // Calculate size of the item when it'd be dropped in this position.
     let currentParent = aDraggedItem.parentNode;
-    let currentSibling = aDraggedItem.nextSibling;
+    let currentSibling = aDraggedItem.nextElementSibling;
     const kAreaType = "cui-areatype";
     let areaType, currentType;
 
     if (targetArea != currentArea) {
       // Move the widget temporarily next to the placeholder.
       aDragOverNode.parentNode.insertBefore(aDraggedItem, aDragOverNode);
       // Update the node's areaType.
       areaType = CustomizableUI.getAreaType(targetArea.id);
@@ -2317,18 +2317,18 @@ CustomizeMode.prototype = {
       aElement = aElement.parentNode;
     }
     return aElement;
   },
 
   _findVisiblePreviousSiblingNode(aReferenceNode) {
     while (aReferenceNode &&
            aReferenceNode.localName == "toolbarpaletteitem" &&
-           aReferenceNode.firstChild.hidden) {
-      aReferenceNode = aReferenceNode.previousSibling;
+           aReferenceNode.firstElementChild.hidden) {
+      aReferenceNode = aReferenceNode.previousElementSibling;
     }
     return aReferenceNode;
   },
 
   onPaletteContextMenuShowing(event) {
    let isFlexibleSpace = event.target.triggerNode.id.includes("wrapper-customizableui-special-spring");
    event.target.querySelector(".customize-context-addToPanel").disabled = isFlexibleSpace;
  },
--- a/browser/components/customizableui/DragPositionManager.jsm
+++ b/browser/components/customizableui/DragPositionManager.jsm
@@ -86,17 +86,17 @@ AreaPositionManager.prototype = {
       let targetBounds = this._lazyStoreGet(closest);
       let farSide = this._dir == "ltr" ? "right" : "left";
       let outsideX = targetBounds[farSide];
       // Check if we're closer to the next target than to this one:
       // Only move if we're not targeting a node in a different row:
       if (aY > targetBounds.top && aY < targetBounds.bottom) {
         if ((this._dir == "ltr" && aX > outsideX) ||
             (this._dir == "rtl" && aX < outsideX)) {
-          return closest.nextSibling || aContainer;
+          return closest.nextElementSibling || aContainer;
         }
       }
     }
     return closest;
   },
 
   /**
    * "Insert" a "placeholder" by shifting the subsequent children out of the
@@ -123,20 +123,20 @@ AreaPositionManager.prototype = {
         }
         // Determine the CSS transform based on the next node:
         child.style.transform = this._diffWithNext(child, aSize);
       } else {
         // If we're not shifting this node, reset the transform
         child.style.transform = "";
       }
     }
-    if (aContainer.lastChild && aIsFromThisArea &&
+    if (aContainer.lastElementChild && aIsFromThisArea &&
         !this._lastPlaceholderInsertion) {
       // Flush layout:
-      aContainer.lastChild.getBoundingClientRect();
+      aContainer.lastElementChild.getBoundingClientRect();
       // then remove all the [notransition]
       for (let child of aContainer.children) {
         child.removeAttribute("notransition");
       }
     }
     this._lastPlaceholderInsertion = aBefore;
   },
 
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -567,19 +567,19 @@ var PanelMultiView = class extends Assoc
    * sure they will not be removed together with the <panelmultiview> element.
    */
   _moveOutKids() {
     let viewCacheId = this.node.getAttribute("viewCacheId");
     if (!viewCacheId) {
       return;
     }
 
-    // Node.children and Node.childNodes is live to DOM changes like the
+    // Node.children and Node.children is live to DOM changes like the
     // ones we're about to do, so iterate over a static copy:
-    let subviews = Array.from(this._viewStack.childNodes);
+    let subviews = Array.from(this._viewStack.children);
     let viewCache = this.document.getElementById(viewCacheId);
     for (let subview of subviews) {
       viewCache.appendChild(subview);
     }
   }
 
   /**
    * Slides in the specified view as a subview.
@@ -870,17 +870,17 @@ var PanelMultiView = class extends Assoc
       // getter also provides an indication that the view node shouldn't be
       // moved around, otherwise the state of the browser would get disrupted.
       let width = prevPanelView.knownWidth;
       let height = prevPanelView.knownHeight;
       viewRect = Object.assign({height, width}, viewNode.customRectGetter());
       nextPanelView.visible = true;
       // Until the header is visible, it has 0 height.
       // Wait for layout before measuring it
-      let header = viewNode.firstChild;
+      let header = viewNode.firstElementChild;
       if (header && header.classList.contains("panel-header")) {
         viewRect.height += await window.promiseDocumentFlushed(() => {
           return this._dwu.getBoundsWithoutFlushing(header).height;
         });
       }
       await nextPanelView.descriptionHeightWorkaround();
     } else {
       this._offscreenViewStack.style.minHeight = olderView.knownHeight + "px";
@@ -1198,17 +1198,17 @@ var PanelView = class extends Associated
     }
   }
 
   /**
    * Adds a header with the given title, or removes it if the title is empty.
    */
   set headerText(value) {
     // If the header already exists, update or remove it as requested.
-    let header = this.node.firstChild;
+    let header = this.node.firstElementChild;
     if (header && header.classList.contains("panel-header")) {
       if (value) {
         header.querySelector("label").setAttribute("value", value);
       } else {
         header.remove();
       }
       return;
     }
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -94,17 +94,17 @@ const PanelUI = {
     for (let event of this.kEvents) {
       this.notificationPanel.addEventListener(event, this);
     }
 
     // We do this sync on init because in order to have the overflow button show up
     // we need to know whether anything is in the permanent panel area.
     this.overflowFixedList.hidden = false;
     // Also unhide the separator. We use CSS to hide/show it based on the panel's content.
-    this.overflowFixedList.previousSibling.hidden = false;
+    this.overflowFixedList.previousElementSibling.hidden = false;
     CustomizableUI.registerMenuPanel(this.overflowFixedList, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
     this.updateOverflowStatus();
 
     Services.obs.notifyObservers(null, "appMenu-notifications-request", "refresh");
 
     this._initialized = true;
   },
 
@@ -503,18 +503,18 @@ const PanelUI = {
     // panel is shown, so their space is reserved. The part of this function
     // that adds the elements is the least expensive anyways.
     this.clearLibraryRecentHighlights();
     if (!highlights.length) {
       return;
     }
 
     let container = this.libraryRecentHighlights;
-    container.hidden = container.previousSibling.hidden =
-      container.previousSibling.previousSibling.hidden = false;
+    container.hidden = container.previousElementSibling.hidden =
+      container.previousElementSibling.previousElementSibling.hidden = false;
     let fragment = document.createDocumentFragment();
     for (let highlight of highlights) {
       let button = document.createElement("toolbarbutton");
       button.classList.add("subviewbutton", "highlight", "subviewbutton-iconic", "bookmark-item");
       let title = highlight.title || highlight.url;
       button.setAttribute("label", title);
       button.setAttribute("tooltiptext", title);
       button.setAttribute("type", "highlight-" + highlight.type);
@@ -542,18 +542,18 @@ const PanelUI = {
   /**
    * Remove all the nodes from the 'Recent Highlights' section and hide it as well.
    */
   clearLibraryRecentHighlights() {
     let container = this.libraryRecentHighlights;
     while (container.firstChild) {
       container.firstChild.remove();
     }
-    container.hidden = container.previousSibling.hidden =
-      container.previousSibling.previousSibling.hidden = true;
+    container.hidden = container.previousElementSibling.hidden =
+      container.previousElementSibling.previousElementSibling.hidden = true;
   },
 
   /**
    * Event handler; invoked when an item of the Recent Highlights is clicked.
    *
    * @param {MouseEvent} event Click event, originating from the Highlight.
    */
   onLibraryHighlightClick(event) {
--- a/browser/components/customizableui/content/toolbar.xml
+++ b/browser/components/customizableui/content/toolbar.xml
@@ -25,17 +25,17 @@
                 toolbox.palette = node;
                 toolbox.removeChild(node);
                 break;
               }
             }
           }
 
           // pass the current set of children for comparison with placements:
-          let children = Array.from(this.childNodes)
+          let children = Array.from(this.children)
                               .filter(node => node.getAttribute("skipintoolbarset") != "true" && node.id)
                               .map(node => node.id);
           CustomizableUI.registerToolbarNode(this, children);
       ]]></constructor>
 
       <method name="insertItem">
         <parameter name="aId"/>
         <parameter name="aBeforeElt"/>
@@ -104,26 +104,26 @@
           return this._toolbox;
         ]]></getter>
       </property>
 
       <property name="currentSet">
         <getter><![CDATA[
           let currentWidgets = new Set();
           for (let node of this.customizationTarget.children) {
-            let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
+            let realNode = node.localName == "toolbarpaletteitem" ? node.firstElementChild : node;
             if (realNode.getAttribute("skipintoolbarset") != "true") {
               currentWidgets.add(realNode.id);
             }
           }
           if (this.getAttribute("overflowing") == "true") {
             let overflowTarget = this.getAttribute("overflowtarget");
             let overflowList = this.ownerDocument.getElementById(overflowTarget);
             for (let node of overflowList.children) {
-              let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
+              let realNode = node.localName == "toolbarpaletteitem" ? node.firstElementChild : node;
               if (realNode.getAttribute("skipintoolbarset") != "true") {
                 currentWidgets.add(realNode.id);
               }
             }
           }
           let orderedPlacements = CustomizableUI.getWidgetIdsInArea(this.id);
           return orderedPlacements.filter(w => currentWidgets.has(w)).join(",");
         ]]></getter>
--- a/browser/components/downloads/DownloadsSubview.jsm
+++ b/browser/components/downloads/DownloadsSubview.jsm
@@ -58,17 +58,17 @@ class DownloadsSubview extends Downloads
       contextMenu.setAttribute("closemenu", "none");
       contextMenu.setAttribute("id", this.context);
       contextMenu.removeAttribute("onpopupshown");
       contextMenu.setAttribute("onpopupshowing",
         "DownloadsSubview.updateContextMenu(document.popupNode, this);");
       contextMenu.setAttribute("onpopuphidden", "DownloadsSubview.onContextMenuHidden(this);");
       let clearButton = contextMenu.querySelector("menuitem[command='downloadsCmd_clearDownloads']");
       clearButton.hidden = false;
-      clearButton.previousSibling.hidden = true;
+      clearButton.previousElementSibling.hidden = true;
       contextMenu.querySelector("menuitem[command='cmd_delete']")
         .setAttribute("command", "downloadsCmd_delete");
     }
     this.panelview.appendChild(contextMenu);
     this.container.setAttribute("context", this.context);
 
     this._downloadsData = DownloadsCommon.getData(this.window, true, true, true);
     this._downloadsData.addView(this);
@@ -96,17 +96,17 @@ class DownloadsSubview extends Downloads
    * downloads.
    */
   onDownloadBatchEnded() {
     let {window} = this;
     window.clearTimeout(this._batchTimeout);
     let waitForMs = 200;
     if (this.batchFragment.childElementCount) {
       // Prepend the batch fragment.
-      this.container.insertBefore(this.batchFragment, this.container.firstChild || null);
+      this.container.insertBefore(this.batchFragment, this.container.firstElementChild || null);
       waitForMs = 0;
     }
     // Wait a wee bit to dispatch the event, because another batch may start
     // right away.
     this._batchTimeout = window.setTimeout(() => {
       this._updateStatsFromDisk();
       this.panelview.dispatchEvent(new window.CustomEvent("DownloadsLoaded"));
     }, waitForMs);
@@ -169,17 +169,17 @@ class DownloadsSubview extends Downloads
       let idleOptions = { timeout: kMaxWaitForIdleMs };
       // Start with getting an idle moment to (maybe) refresh the list of downloads.
       await new Promise(resolve => this.window.requestIdleCallback(resolve), idleOptions);
       // In the meantime, this instance could have been destroyed, so take note.
       if (this.destroyed)
         return;
 
       let count = 0;
-      for (let button of this.container.childNodes) {
+      for (let button of this.container.children) {
         if (this.destroyed)
           return;
         if (!button._shell)
           continue;
 
         await button._shell.refresh();
 
         // Make sure to request a new idle moment every `kRefreshBatchSize` buttons.
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -396,17 +396,17 @@ var DownloadsPanel = {
       }
       aEvent.preventDefault();
       return;
     }
 
     if (aEvent.keyCode == aEvent.DOM_VK_DOWN) {
       // If the last element in the list is selected, or the footer is already
       // focused, focus the footer.
-      if (richListBox.selectedItem === richListBox.lastChild ||
+      if (richListBox.selectedItem === richListBox.lastElementChild ||
           document.activeElement.parentNode.id === "downloadsFooter") {
         DownloadsFooter.focus();
         aEvent.preventDefault();
         return;
       }
     }
 
     // Pass keypress events to the richlistbox view when it's focused.
@@ -420,19 +420,19 @@ var DownloadsPanel = {
    * as the the accel-V "paste" event, which initiates a file download if the
    * pasted item can be resolved to a URI.
    */
   _onKeyDown(aEvent) {
     // If the footer is focused and the downloads list has at least 1 element
     // in it, focus the last element in the list when going up.
     if (aEvent.keyCode == aEvent.DOM_VK_UP &&
         document.activeElement.parentNode.id === "downloadsFooter" &&
-        DownloadsView.richListBox.firstChild) {
+        DownloadsView.richListBox.firstElementChild) {
       DownloadsView.richListBox.focus();
-      DownloadsView.richListBox.selectedItem = DownloadsView.richListBox.lastChild;
+      DownloadsView.richListBox.selectedItem = DownloadsView.richListBox.lastElementChild;
       aEvent.preventDefault();
       return;
     }
 
     let pasting = aEvent.keyCode == aEvent.DOM_VK_V &&
                   aEvent.getModifierState("Accel");
 
     if (!pasting) {
@@ -723,17 +723,17 @@ var DownloadsView = {
     DownloadsCommon.log("Adding a new DownloadsViewItem to the downloads list.",
                         "aNewest =", aNewest);
 
     let element = document.createElement("richlistitem");
     let viewItem = new DownloadsViewItem(download, element);
     this._visibleViewItems.set(download, viewItem);
     this._itemsForElements.set(element, viewItem);
     if (aNewest) {
-      this.richListBox.insertBefore(element, this.richListBox.firstChild);
+      this.richListBox.insertBefore(element, this.richListBox.firstElementChild);
     } else {
       this.richListBox.appendChild(element);
     }
   },
 
   /**
    * Removes the view item associated with the specified data item.
    */
--- a/browser/components/extensions/parent/ext-menus.js
+++ b/browser/components/extensions/parent/ext-menus.js
@@ -71,32 +71,32 @@ var gMenuBuilder = {
     const children = this.buildChildren(root, contextData);
     const visible = children.slice(0, ACTION_MENU_TOP_LEVEL_LIMIT);
 
     this.xulMenu = menu;
     menu.addEventListener("popuphidden", this);
 
     if (visible.length) {
       const separator = menu.ownerDocument.createElement("menuseparator");
-      menu.insertBefore(separator, menu.firstChild);
+      menu.insertBefore(separator, menu.firstElementChild);
       this.itemsToCleanUp.add(separator);
 
       for (const child of visible) {
         this.itemsToCleanUp.add(child);
         menu.insertBefore(child, separator);
       }
     }
     this.afterBuildingMenu(contextData);
   },
 
   buildElementWithChildren(item, contextData) {
     const element = this.buildSingleElement(item, contextData);
     const children = this.buildChildren(item, contextData);
     if (children.length) {
-      element.firstChild.append(...children);
+      element.firstElementChild.append(...children);
     }
     return element;
   },
 
   buildChildren(item, contextData) {
     let groupName;
     let children = [];
     for (let child of item.children) {
@@ -113,17 +113,17 @@ var gMenuBuilder = {
         children.push(this.buildElementWithChildren(child, contextData));
       }
     }
     return children;
   },
 
   createTopLevelElement(root, contextData) {
     let rootElement = this.buildElementWithChildren(root, contextData);
-    if (!rootElement.firstChild || !rootElement.firstChild.childNodes.length) {
+    if (!rootElement.firstElementChild || !rootElement.firstElementChild.children.length) {
       // If the root has no visible children, there is no reason to show
       // the root menu item itself either.
       return null;
     }
     rootElement.setAttribute("ext-type", "top-level-menu");
     rootElement = this.removeTopLevelMenuIfNeeded(rootElement);
 
     // Display the extension icon on the root element.
@@ -151,19 +151,19 @@ var gMenuBuilder = {
       separator.remove();
       this.itemsToCleanUp.clear();
     }
   },
 
   removeTopLevelMenuIfNeeded(element) {
     // If there is only one visible top level element we don't need the
     // root menu element for the extension.
-    let menuPopup = element.firstChild;
-    if (menuPopup && menuPopup.childNodes.length == 1) {
-      let onlyChild = menuPopup.firstChild;
+    let menuPopup = element.firstElementChild;
+    if (menuPopup && menuPopup.children.length == 1) {
+      let onlyChild = menuPopup.firstElementChild;
 
       // Keep single checkbox items in the submenu on Linux since
       // the extension icon overlaps the checkbox otherwise.
       if (AppConstants.platform === "linux" && onlyChild.getAttribute("type") === "checkbox") {
         return element;
       }
 
       onlyChild.remove();
--- a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
@@ -198,17 +198,17 @@ add_task(async function test_tabs_showhi
   // hidden.  The rest of the tabs should be hidden at this point.  Hidden
   // status was already validated inside the extension, this double checks
   // from chrome code.
   let otherwin;
   for (let win of BrowserWindowIterator()) {
     if (win != window) {
       otherwin = win;
     }
-    let tabs = Array.from(win.gBrowser.tabs.values());
+    let tabs = Array.from(win.gBrowser.tabs);
     ok(!tabs[0].hidden, "first tab not hidden");
     for (let i = 1; i < tabs.length; i++) {
       ok(tabs[i].hidden, "tab hidden value is correct");
       let id = SessionStore.getCustomTabValue(tabs[i], "hiddenBy");
       is(id, extension.id, "tab hiddenBy value is correct");
       await TabStateFlusher.flush(tabs[i].linkedBrowser);
     }
 
@@ -217,17 +217,17 @@ add_task(async function test_tabs_showhi
   }
 
   // Close the other window then restore it to test that the tabs are
   // restored with proper hidden state, and the correct extension id.
   await BrowserTestUtils.closeWindow(otherwin);
 
   otherwin = SessionStore.undoCloseWindow(0);
   await BrowserTestUtils.waitForEvent(otherwin, "load");
-  let tabs = Array.from(otherwin.gBrowser.tabs.values());
+  let tabs = Array.from(otherwin.gBrowser.tabs);
   ok(!tabs[0].hidden, "first tab not hidden");
   for (let i = 1; i < tabs.length; i++) {
     ok(tabs[i].hidden, "tab hidden value is correct");
     let id = SessionStore.getCustomTabValue(tabs[i], "hiddenBy");
     is(id, extension.id, "tab hiddenBy value is correct");
   }
 
   // Test closing the last visible tab, the next tab which is hidden should become
@@ -237,17 +237,17 @@ add_task(async function test_tabs_showhi
   ok(!otherwin.gBrowser.selectedTab.hidden, "tab was unhidden");
 
   // Showall will unhide any remaining hidden tabs.
   extension.sendMessage("showall");
   await extension.awaitMessage("shown");
 
   // Check from chrome code that all tabs are visible again.
   for (let win of BrowserWindowIterator()) {
-    let tabs = Array.from(win.gBrowser.tabs.values());
+    let tabs = Array.from(win.gBrowser.tabs);
     for (let i = 0; i < tabs.length; i++) {
       ok(!tabs[i].hidden, "tab hidden value is correct");
     }
   }
 
   // Close second window.
   await BrowserTestUtils.closeWindow(otherwin);
 
--- a/browser/components/payments/content/paymentDialogFrameScript.js
+++ b/browser/components/payments/content/paymentDialogFrameScript.js
@@ -91,17 +91,17 @@ let PaymentFrameScript = {
 
       getDefaultPreferences() {
         let prefValues = Cu.cloneInto({
           saveCreditCardDefaultChecked:
             Services.prefs.getBoolPref(SAVE_CREDITCARD_DEFAULT_PREF, false),
           saveAddressDefaultChecked:
             Services.prefs.getBoolPref(SAVE_ADDRESS_DEFAULT_PREF, false),
         }, waivedContent);
-        return prefValues;
+        return Cu.cloneInto(prefValues, waivedContent);
       },
     };
     waivedContent.PaymentDialogUtils = Cu.cloneInto(PaymentDialogUtils, waivedContent, {
       cloneFunctions: true,
     });
   },
 
   sendToChrome({detail}) {
--- a/browser/components/payments/res/containers/address-form.js
+++ b/browser/components/payments/res/containers/address-form.js
@@ -138,21 +138,25 @@ export default class AddressForm extends
     if (editing) {
       record = addresses[addressPage.guid];
       if (!record) {
         throw new Error("Trying to edit a non-existing address: " + addressPage.guid);
       }
       // When editing an existing record, prevent changes to persistence
       this.persistCheckbox.hidden = true;
     } else {
-      let defaults = PaymentDialogUtils.getDefaultPreferences();
+      let {saveAddressDefaultChecked} = PaymentDialogUtils.getDefaultPreferences();
+      if (typeof saveAddressDefaultChecked != "boolean") {
+        throw new Error(`Unexpected non-boolean value for saveAddressDefaultChecked from
+          PaymentDialogUtils.getDefaultPreferences(): ${typeof saveAddressDefaultChecked}`);
+      }
       // Adding a new record: default persistence to the pref value when in a not-private session
       this.persistCheckbox.hidden = false;
       this.persistCheckbox.checked = state.isPrivate ? false :
-                                                       defaults.saveAddressDefaultChecked;
+                                                       saveAddressDefaultChecked;
     }
 
     this.formHandler.loadRecord(record);
 
     // Add validation to some address fields
     this.updateRequiredState();
 
     let shippingAddressErrors = request.paymentDetails.shippingAddressErrors;
--- a/browser/components/payments/res/containers/basic-card-form.js
+++ b/browser/components/payments/res/containers/basic-card-form.js
@@ -153,25 +153,29 @@ export default class BasicCardForm exten
     } else {
       this.pageTitleHeading.textContent = this.dataset.addBasicCardTitle;
       // Use a currently selected shipping address as the default billing address
       record.billingAddressGUID = basicCardPage.billingAddressGUID;
       if (!record.billingAddressGUID && selectedShippingAddress) {
         record.billingAddressGUID = selectedShippingAddress;
       }
 
-      let defaults = PaymentDialogUtils.getDefaultPreferences();
+      let {saveCreditCardDefaultChecked} = PaymentDialogUtils.getDefaultPreferences();
+      if (typeof saveCreditCardDefaultChecked != "boolean") {
+        throw new Error(`Unexpected non-boolean value for saveCreditCardDefaultChecked from
+          PaymentDialogUtils.getDefaultPreferences(): ${typeof saveCreditCardDefaultChecked}`);
+      }
       // Adding a new record: default persistence to pref value when in a not-private session
       this.persistCheckbox.hidden = false;
       if (basicCardPage.hasOwnProperty("persistCheckboxValue")) {
         // returning to this page, use previous checked state
         this.persistCheckbox.checked = basicCardPage.persistCheckboxValue;
       } else {
         this.persistCheckbox.checked = state.isPrivate ? false :
-                                                         defaults.saveCreditCardDefaultChecked;
+                                                         saveCreditCardDefaultChecked;
       }
     }
 
     this.formHandler.loadRecord(record, addresses, basicCardPage.preserveFieldValues);
 
     this.form.querySelector(".billingAddressRow").hidden = false;
 
     let billingAddressSelect = this.form.querySelector("#billingAddressGUID");
--- a/browser/components/places/content/bookmarksSidebar.js
+++ b/browser/components/places/content/bookmarksSidebar.js
@@ -26,17 +26,19 @@ function init() {
   }
 
   document.getElementById("bookmarks-view").place =
     "place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
 }
 
 function searchBookmarks(aSearchString) {
   var tree = document.getElementById("bookmarks-view");
-  if (!aSearchString)
+  if (!aSearchString) {
+    // eslint-disable-next-line no-self-assign
     tree.place = tree.place;
-  else
+  } else {
     tree.applyFilter(aSearchString,
                      PlacesUtils.bookmarks.userContentRoots);
+  }
 }
 
 window.addEventListener("SidebarFocused",
                         () => document.getElementById("search-box").focus());
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -228,47 +228,47 @@ PlacesViewBase.prototype = {
     return this.controller.buildContextMenu(aPopup);
   },
 
   destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
     this._contextMenuShown = null;
   },
 
   clearAllContents(aPopup) {
-    let kid = aPopup.firstChild;
+    let kid = aPopup.firstElementChild;
     while (kid) {
-      let next = kid.nextSibling;
+      let next = kid.nextElementSibling;
       if (!kid.classList.contains("panel-header")) {
         kid.remove();
       }
       kid = next;
     }
     aPopup._emptyMenuitem = aPopup._startMarker = aPopup._endMarker = null;
   },
 
   _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) {
     // Ensure markers are here when `invalidateContainer` is called before the
     // popup is shown, which may the case for panelviews, for example.
     this._ensureMarkers(aPopup);
     // Remove Places nodes from the popup.
     let child = aPopup._startMarker;
-    while (child.nextSibling != aPopup._endMarker) {
-      let sibling = child.nextSibling;
+    while (child.nextElementSibling != aPopup._endMarker) {
+      let sibling = child.nextElementSibling;
       if (sibling._placesNode && !aDelay) {
         aPopup.removeChild(sibling);
       } else if (sibling._placesNode && aDelay) {
         // HACK (bug 733419): the popups originating from the OS X native
         // menubar don't live-update while open, thus we don't clean it
         // until the next popupshowing, to avoid zombie menuitems.
         if (!aPopup._delayedRemovals)
           aPopup._delayedRemovals = [];
         aPopup._delayedRemovals.push(sibling);
-        child = child.nextSibling;
+        child = child.nextElementSibling;
       } else {
-        child = child.nextSibling;
+        child = child.nextElementSibling;
       }
     }
   },
 
   _rebuildPopup: function PVB__rebuildPopup(aPopup) {
     let resultNode = aPopup._placesNode;
     if (!resultNode.containerOpen)
       return;
@@ -317,18 +317,18 @@ PlacesViewBase.prototype = {
       aPopup._emptyMenuitem.className = "bookmark-item";
       if (typeof this.options.extraClasses.entry == "string")
         aPopup._emptyMenuitem.classList.add(this.options.extraClasses.entry);
     }
 
     if (aEmpty) {
       aPopup.setAttribute("emptyplacesresult", "true");
       // Don't add the menuitem if there is static content.
-      if (!aPopup._startMarker.previousSibling &&
-          !aPopup._endMarker.nextSibling)
+      if (!aPopup._startMarker.previousElementSibling &&
+          !aPopup._endMarker.nextElementSibling)
         aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
     } else {
       aPopup.removeAttribute("emptyplacesresult");
       try {
         aPopup.removeChild(aPopup._emptyMenuitem);
       } catch (ex) {}
     }
   },
@@ -480,18 +480,18 @@ PlacesViewBase.prototype = {
     }
 
     if (aStatus == Ci.mozILivemark.STATUS_LOADING ||
         aStatus == Ci.mozILivemark.STATUS_FAILED) {
       // Status has changed, update the cached status menuitem.
       let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ?
                        "bookmarksLivemarkLoading" : "bookmarksLivemarkFailed";
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
-      if (aPopup._startMarker.nextSibling != statusMenuitem)
-        aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
+      if (aPopup._startMarker.nextElementSibling != statusMenuitem)
+        aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextElementSibling);
     } else if (aPopup._statusMenuitem.parentNode == aPopup) {
       // The livemark has finished loading.
       aPopup.removeChild(aPopup._statusMenuitem);
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aPlacesNode, aValue) {
     let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
@@ -592,30 +592,30 @@ PlacesViewBase.prototype = {
       elt = elt.parentNode;
 
     if (parentElt._built) {
       parentElt.removeChild(elt);
 
       // Figure out if we need to show the "<Empty>" menu-item.
       // TODO Bug 517701: This doesn't seem to handle the case of an empty
       // root.
-      if (parentElt._startMarker.nextSibling == parentElt._endMarker)
+      if (parentElt._startMarker.nextElementSibling == parentElt._endMarker)
         this._setEmptyPopupStatus(parentElt, true);
     }
   },
 
   nodeHistoryDetailsChanged:
   function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
     if (aPlacesNode.parent &&
         this.controller.hasCachedLivemarkInfo(aPlacesNode.parent)) {
       // Find the node in the parent.
       let popup = this._getDOMNodeForPlacesNode(aPlacesNode.parent);
-      for (let child = popup._startMarker.nextSibling;
+      for (let child = popup._startMarker.nextElementSibling;
            child != popup._endMarker;
-           child = child.nextSibling) {
+           child = child.nextElementSibling) {
         if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
           if (aPlacesNode.accessCount)
             child.setAttribute("visited", "true");
           else
             child.removeAttribute("visited");
           break;
         }
       }
@@ -630,20 +630,20 @@ PlacesViewBase.prototype = {
   batching() { },
 
   nodeInserted:
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (!parentElt._built)
       return;
 
-    let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
+    let index = Array.prototype.indexOf.call(parentElt.children, parentElt._startMarker) +
                 aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
-                               parentElt.childNodes[index] || parentElt._endMarker);
+                               parentElt.children[index] || parentElt._endMarker);
     this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
     // Note: the current implementation of moveItem does not actually
@@ -660,19 +660,19 @@ PlacesViewBase.prototype = {
     // we need to do in that case.
     if (elt == this._rootElt)
       return;
 
     let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
     if (parentElt._built) {
       // Move the node.
       parentElt.removeChild(elt);
-      let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
+      let index = Array.prototype.indexOf.call(parentElt.children, parentElt._startMarker) +
                   aNewIndex + 1;
-      parentElt.insertBefore(elt, parentElt.childNodes[index]);
+      parentElt.insertBefore(elt, parentElt.children[index]);
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
         aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
       this.invalidateContainer(aPlacesNode);
@@ -705,17 +705,17 @@ PlacesViewBase.prototype = {
           }, () => undefined);
       }
     }
   },
 
   _populateLivemarkPopup: function PVB__populateLivemarkPopup(aPopup) {
     this._setLivemarkSiteURIMenuItem(aPopup);
     // Show the loading status only if there are no entries yet.
-    if (aPopup._startMarker.nextSibling == aPopup._endMarker)
+    if (aPopup._startMarker.nextElementSibling == aPopup._endMarker)
       this._setLivemarkStatusMenuItem(aPopup, Ci.mozILivemark.STATUS_LOADING);
 
     PlacesUtils.livemarks.getLivemark({ id: aPopup._placesNode.itemId })
       .then(aLivemark => {
         let placesNode = aPopup._placesNode;
         if (!placesNode.containerOpen)
           return;
 
@@ -805,24 +805,24 @@ PlacesViewBase.prototype = {
       return;
 
     let hasMultipleURIs = false;
 
     // Check if the popup contains at least 2 menuitems with places nodes.
     // We don't currently support opening multiple uri nodes when they are not
     // populated by the result.
     if (aPopup._placesNode.childCount > 0) {
-      let currentChild = aPopup.firstChild;
+      let currentChild = aPopup.firstElementChild;
       let numURINodes = 0;
       while (currentChild) {
         if (currentChild.localName == "menuitem" && currentChild._placesNode) {
           if (++numURINodes == 2)
             break;
         }
-        currentChild = currentChild.nextSibling;
+        currentChild = currentChild.nextElementSibling;
       }
       hasMultipleURIs = numURINodes > 1;
     }
 
     let isLiveMark = false;
     if (this.controller.hasCachedLivemarkInfo(aPopup._placesNode)) {
       hasMultipleURIs = true;
       isLiveMark = true;
@@ -877,34 +877,34 @@ PlacesViewBase.prototype = {
 
   _ensureMarkers: function PVB__ensureMarkers(aPopup) {
     if (aPopup._startMarker)
       return;
 
     // _startMarker is an hidden menuseparator that lives before places nodes.
     aPopup._startMarker = document.createElement("menuseparator");
     aPopup._startMarker.hidden = true;
-    aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
+    aPopup.insertBefore(aPopup._startMarker, aPopup.firstElementChild);
 
     // _endMarker is a DOM node that lives after places nodes, specified with
     // the 'insertionPoint' option or will be a hidden menuseparator.
     let node = this.options.insertionPoint ?
                aPopup.querySelector(this.options.insertionPoint) : null;
     if (node) {
       aPopup._endMarker = node;
     } else {
       aPopup._endMarker = document.createElement("menuseparator");
       aPopup._endMarker.hidden = true;
     }
     aPopup.appendChild(aPopup._endMarker);
 
     // Move the markers to the right position.
     let firstNonStaticNodeFound = false;
-    for (let i = 0; i < aPopup.childNodes.length; i++) {
-      let child = aPopup.childNodes[i];
+    for (let i = 0; i < aPopup.children.length; i++) {
+      let child = aPopup.children[i];
       // Menus that have static content at the end, but are initially empty,
       // use a special "builder" attribute to figure out where to start
       // inserting places nodes.
       if (child.getAttribute("builder") == "end") {
         aPopup.insertBefore(aPopup._endMarker, child);
         break;
       }
 
@@ -1153,20 +1153,20 @@ PlacesToolbar.prototype = {
     else
       aInsertionNode.appendChild(button);
     return button;
   },
 
   _updateChevronPopupNodesVisibility:
   function PT__updateChevronPopupNodesVisibility() {
     // Note the toolbar by default builds less nodes than the chevron popup.
-    for (let toolbarNode = this._rootElt.firstChild,
-         node = this._chevronPopup._startMarker.nextSibling;
+    for (let toolbarNode = this._rootElt.firstElementChild,
+         node = this._chevronPopup._startMarker.nextElementSibling;
          toolbarNode && node;
-         toolbarNode = toolbarNode.nextSibling, node = node.nextSibling) {
+         toolbarNode = toolbarNode.nextElementSibling, node = node.nextElementSibling) {
       node.hidden = toolbarNode.style.visibility != "hidden";
     }
   },
 
   _onChevronPopupShowing:
   function PT__onChevronPopupShowing(aEvent) {
     // Handle popupshowing only for the chevron popup, not for nested ones.
     if (aEvent.target != this._chevronPopup)
@@ -1287,17 +1287,17 @@ PlacesToolbar.prototype = {
 
     let childOverflowed = false;
 
     // We're about to potentially update a bunch of nodes, so we do it
     // in a requestAnimationFrame so that other JS that's might execute
     // in the same tick can avoid flushing styles and layout for these
     // changes.
     window.requestAnimationFrame(() => {
-      for (let child of this._rootElt.childNodes) {
+      for (let child of this._rootElt.children) {
         // Once a child overflows, all the next ones will.
         if (!childOverflowed) {
           let childRect = dwu.getBoundsWithoutFlushing(child);
           childOverflowed = this.isRTL ? (childRect.left < scrollRect.left)
                                        : (childRect.right > scrollRect.right);
         }
 
         if (childOverflowed) {
@@ -1323,32 +1323,32 @@ PlacesToolbar.prototype = {
 
     this._updatingNodesVisibility = false;
   },
 
   nodeInserted:
   function PT_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (parentElt == this._rootElt) { // Node is on the toolbar.
-      let children = this._rootElt.childNodes;
+      let children = this._rootElt.children;
       // Nothing to do if it's a never-visible node, but note it's possible
       // we are appending.
       if (aIndex > children.length)
         return;
 
       // Note that childCount is already accounting for the node being added,
       // thus we must subtract one node from it.
       if (this._resultNode.childCount - 1 > children.length) {
         if (aIndex == children.length) {
           // If we didn't build all the nodes and new node is being appended,
           // we can skip it as well.
           return;
         }
         // Keep the number of built nodes consistent.
-        this._rootElt.removeChild(this._rootElt.lastChild);
+        this._rootElt.removeChild(this._rootElt.lastElementChild);
       }
 
       let button = this._insertNewItem(aPlacesNode, this._rootElt,
                                        children[aIndex] || null);
       let prevSiblingOverflowed = aIndex > 0 && aIndex <= children.length &&
                                   children[aIndex - 1].style.visibility == "hidden";
       if (prevSiblingOverflowed) {
         button.style.visibility = "hidden";
@@ -1374,19 +1374,19 @@ PlacesToolbar.prototype = {
         return;
 
       // Here we need the <menu>.
       if (elt.localName == "menupopup")
         elt = elt.parentNode;
 
       let overflowed = elt.style.visibility == "hidden";
       this._removeChild(elt);
-      if (this._resultNode.childCount > this._rootElt.childNodes.length) {
+      if (this._resultNode.childCount > this._rootElt.children.length) {
         // A new node should be built to keep a coherent number of children.
-        this._insertNewItem(this._resultNode.getChild(this._rootElt.childNodes.length),
+        this._insertNewItem(this._resultNode.getChild(this._rootElt.children.length),
                             this._rootElt);
       }
       if (!overflowed)
         this.updateNodesVisibility();
       return;
     }
 
     PlacesViewBase.prototype.nodeRemoved.apply(this, arguments);
@@ -1394,46 +1394,46 @@ PlacesToolbar.prototype = {
 
   nodeMoved:
   function PT_nodeMoved(aPlacesNode,
                         aOldParentPlacesNode, aOldIndex,
                         aNewParentPlacesNode, aNewIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
     if (parentElt == this._rootElt) { // Node is on the toolbar.
       // Do nothing if the node will never be visible.
-      let lastBuiltIndex = this._rootElt.childNodes.length - 1;
+      let lastBuiltIndex = this._rootElt.children.length - 1;
       if (aOldIndex > lastBuiltIndex && aNewIndex > lastBuiltIndex + 1)
         return;
 
       let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
       if (elt) {
         // Here we need the <menu>.
         if (elt.localName == "menupopup")
           elt = elt.parentNode;
         this._removeChild(elt);
       }
 
       if (aNewIndex > lastBuiltIndex + 1) {
-        if (this._resultNode.childCount > this._rootElt.childNodes.length) {
+        if (this._resultNode.childCount > this._rootElt.children.length) {
           // If the element was built and becomes non built, another node should
           // be built to keep a coherent number of children.
-          this._insertNewItem(this._resultNode.getChild(this._rootElt.childNodes.length),
+          this._insertNewItem(this._resultNode.getChild(this._rootElt.children.length),
                               this._rootElt);
         }
         return;
       }
 
       if (!elt) {
         // The node has not been inserted yet, so we must create it.
-        elt = this._insertNewItem(aPlacesNode, this._rootElt, this._rootElt.childNodes[aNewIndex]);
+        elt = this._insertNewItem(aPlacesNode, this._rootElt, this._rootElt.children[aNewIndex]);
         let icon = aPlacesNode.icon;
         if (icon)
           elt.setAttribute("image", icon);
       } else {
-        this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
+        this._rootElt.insertBefore(elt, this._rootElt.children[aNewIndex]);
       }
 
       // The chevron view may get nodeMoved after the toolbar.  In such a case,
       // we should ensure (by manually swapping menuitems) that the actual nodes
       // are in the final position before updateNodesVisibility tries to update
       // their visibility, or the chevron may go out of sync.
       // Luckily updateNodesVisibility runs on a timer, so, by the time it updates
       // nodes, the menu has already handled the notification.
@@ -1511,19 +1511,19 @@ PlacesToolbar.prototype = {
                  openTimer: null,
                  hoverTime: 350,
                  closeTimer: null },
 
   _clearOverFolder: function PT__clearOverFolder() {
     // The mouse is no longer dragging over the stored menubutton.
     // Close the menubutton, clear out drag styles, and clear all
     // timers for opening/closing it.
-    if (this._overFolder.elt && this._overFolder.elt.lastChild) {
-      if (!this._overFolder.elt.lastChild.hasAttribute("dragover")) {
-        this._overFolder.elt.lastChild.hidePopup();
+    if (this._overFolder.elt && this._overFolder.elt.lastElementChild) {
+      if (!this._overFolder.elt.lastElementChild.hasAttribute("dragover")) {
+        this._overFolder.elt.lastElementChild.hidePopup();
       }
       this._overFolder.elt.removeAttribute("dragover");
       this._overFolder.elt = null;
     }
     if (this._overFolder.openTimer) {
       this._overFolder.openTimer.cancel();
       this._overFolder.openTimer = null;
     }
@@ -1544,17 +1544,17 @@ PlacesToolbar.prototype = {
     if (!PlacesUtils.nodeIsFolder(this._resultNode))
       return null;
 
     let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
     let elt = aEvent.target;
     if (elt._placesNode && elt != this._rootElt &&
         elt.localName != "menupopup") {
       let eltRect = elt.getBoundingClientRect();
-      let eltIndex = Array.prototype.indexOf.call(this._rootElt.childNodes, elt);
+      let eltIndex = Array.prototype.indexOf.call(this._rootElt.children, elt);
       if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
           !PlacesUIUtils.isFolderReadOnly(elt._placesNode, this)) {
         // This is a folder.
         // If we are in the middle of it, drop inside it.
         // Otherwise, drop before it, with regards to RTL mode.
         let threshold = eltRect.width * 0.25;
         if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
                        : (aEvent.clientX < eltRect.left + threshold)) {
@@ -1578,17 +1578,17 @@ PlacesToolbar.prototype = {
               parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode),
               tagName
             });
           dropPoint.beforeIndex = eltIndex;
           dropPoint.folderElt = elt;
         } else {
           // Drop after this folder.
           let beforeIndex =
-            (eltIndex == this._rootElt.childNodes.length - 1) ?
+            (eltIndex == this._rootElt.children.length - 1) ?
             -1 : eltIndex + 1;
 
           dropPoint.ip =
             new PlacesInsertionPoint({
               parentId: PlacesUtils.getConcreteItemId(this._resultNode),
               parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
               index: beforeIndex,
               orientation: Ci.nsITreeView.DROP_BEFORE
@@ -1608,17 +1608,17 @@ PlacesToolbar.prototype = {
               parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
               index: eltIndex,
               orientation: Ci.nsITreeView.DROP_BEFORE
             });
           dropPoint.beforeIndex = eltIndex;
         } else {
           // Drop after this bookmark.
           let beforeIndex =
-            eltIndex == this._rootElt.childNodes.length - 1 ?
+            eltIndex == this._rootElt.children.length - 1 ?
             -1 : eltIndex + 1;
           dropPoint.ip =
             new PlacesInsertionPoint({
               parentId: PlacesUtils.getConcreteItemId(this._resultNode),
               parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
               index: beforeIndex,
               orientation: Ci.nsITreeView.DROP_BEFORE
             });
@@ -1653,17 +1653,17 @@ PlacesToolbar.prototype = {
     } else if (aTimer == this._ibTimer) {
       // * Timer to turn off indicator bar.
       this._dropIndicator.collapsed = true;
       this._ibTimer = null;
     } else if (aTimer == this._overFolder.openTimer) {
       // * Timer to open a menubutton that's being dragged over.
       // Set the autoopen attribute on the folder's menupopup so that
       // the menu will automatically close when the mouse drags off of it.
-      this._overFolder.elt.lastChild.setAttribute("autoopened", "true");
+      this._overFolder.elt.lastElementChild.setAttribute("autoopened", "true");
       this._overFolder.elt.open = true;
       this._overFolder.openTimer = null;
     } else if (aTimer == this._overFolder.closeTimer) {
       // * Timer to close a menubutton that's been dragged off of.
       // Close the menubutton if we are not dragging over it or one of
       // its children.  The autoopened attribute will let the menu know to
       // close later if the menu is still being dragged over.
       let currentPlacesNode = PlacesControllerDragHelper.currentDropTarget;
@@ -1723,17 +1723,17 @@ PlacesToolbar.prototype = {
         aEvent.preventDefault();
         // Open the menu.
         draggedElt.open = true;
         return;
       }
 
       // If the menu is open, close it.
       if (draggedElt.open) {
-        draggedElt.lastChild.hidePopup();
+        draggedElt.lastElementChild.hidePopup();
         draggedElt.open = false;
       }
     }
 
     // Activate the view and cache the dragged element.
     this._draggedElt = draggedElt._placesNode;
     this._rootElt.focus();
 
@@ -1777,33 +1777,33 @@ PlacesToolbar.prototype = {
       // show indicator bar and move it to the appropriate drop point.
       let ind = this._dropIndicator;
       ind.parentNode.collapsed = false;
       let halfInd = ind.clientWidth / 2;
       let translateX;
       if (this.isRTL) {
         halfInd = Math.ceil(halfInd);
         translateX = 0 - this._rootElt.getBoundingClientRect().right - halfInd;
-        if (this._rootElt.firstChild) {
+        if (this._rootElt.firstElementChild) {
           if (dropPoint.beforeIndex == -1)
-            translateX += this._rootElt.lastChild.getBoundingClientRect().left;
+            translateX += this._rootElt.lastElementChild.getBoundingClientRect().left;
           else {
-            translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
+            translateX += this._rootElt.children[dropPoint.beforeIndex]
                               .getBoundingClientRect().right;
           }
         }
       } else {
         halfInd = Math.floor(halfInd);
         translateX = 0 - this._rootElt.getBoundingClientRect().left +
                      halfInd;
-        if (this._rootElt.firstChild) {
+        if (this._rootElt.firstElementChild) {
           if (dropPoint.beforeIndex == -1)
-            translateX += this._rootElt.lastChild.getBoundingClientRect().right;
+            translateX += this._rootElt.lastElementChild.getBoundingClientRect().right;
           else {
-            translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
+            translateX += this._rootElt.children[dropPoint.beforeIndex]
                               .getBoundingClientRect().left;
           }
         }
       }
 
       ind.style.transform = "translate(" + Math.round(translateX) + "px)";
       ind.style.marginInlineStart = (-ind.clientWidth) + "px";
       ind.collapsed = false;
@@ -2048,17 +2048,17 @@ PlacesPanelMenuView.prototype = {
   },
 
   nodeInserted:
   function PAMV_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (parentElt != this._rootElt)
       return;
 
-    let children = this._rootElt.childNodes;
+    let children = this._rootElt.children;
     this._insertNewItem(aPlacesNode, this._rootElt,
       aIndex < children.length ? children[aIndex] : null);
   },
 
   nodeRemoved:
   function PAMV_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (parentElt != this._rootElt)
@@ -2073,17 +2073,17 @@ PlacesPanelMenuView.prototype = {
                           aOldParentPlacesNode, aOldIndex,
                           aNewParentPlacesNode, aNewIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
     if (parentElt != this._rootElt)
       return;
 
     let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
     this._removeChild(elt);
-    this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
+    this._rootElt.insertBefore(elt, this._rootElt.children[aNewIndex]);
   },
 
   nodeAnnotationChanged:
   function PAMV_nodeAnnotationChanged(aPlacesNode, aAnno) {
     let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
     // There's no UI representation for the root node.
     if (elt == this._rootElt)
       return;
@@ -2265,17 +2265,17 @@ this.PlacesPanelview = class extends Pla
     }
 
     if (empty) {
       panelview.setAttribute("emptyplacesresult", "true");
       // Don't add the menuitem if there is static content.
       // We also support external usage for custom crafted panels - which'll have
       // no markers present.
       if (!panelview._startMarker ||
-          (!panelview._startMarker.previousSibling && !panelview._endMarker.nextSibling)) {
+          (!panelview._startMarker.previousElementSibling && !panelview._endMarker.nextElementSibling)) {
         panelview.insertBefore(panelview._emptyMenuitem, panelview._endMarker);
       }
     } else {
       panelview.removeAttribute("emptyplacesresult");
       try {
         panelview.removeChild(panelview._emptyMenuitem);
       } catch (ex) {}
     }
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -559,18 +559,18 @@ PlacesController.prototype = {
   buildContextMenu: function PC_buildContextMenu(aPopup) {
     var metadata = this._buildSelectionMetadata();
     var ip = this._view.insertionPoint;
     var noIp = !ip || ip.isTag;
 
     var separator = null;
     var visibleItemsBeforeSep = false;
     var usableItemCount = 0;
-    for (var i = 0; i < aPopup.childNodes.length; ++i) {
-      var item = aPopup.childNodes[i];
+    for (var i = 0; i < aPopup.children.length; ++i) {
+      var item = aPopup.children[i];
       if (item.getAttribute("ignoreitem") == "true") {
         continue;
       }
       if (item.localName != "menuseparator") {
         // We allow pasting into tag containers, so special case that.
         var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" &&
                          noIp && !(ip && ip.isTag && item.id == "placesContext_paste");
         var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" &&
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -355,18 +355,18 @@ var gEditItemOverlay = {
     folderMenuItem.className = "menuitem-iconic folder-icon";
     aMenupopup.appendChild(folderMenuItem);
     return folderMenuItem;
   },
 
   async _initFolderMenuList(aSelectedFolderGuid) {
     // clean up first
     var menupopup = this._folderMenuList.menupopup;
-    while (menupopup.childNodes.length > 6)
-      menupopup.removeChild(menupopup.lastChild);
+    while (menupopup.children.length > 6)
+      menupopup.removeChild(menupopup.lastElementChild);
 
     // Build the static list
     if (!this._staticFoldersListBuilt) {
       let unfiledItem = this._element("unfiledRootItem");
       unfiledItem.label = PlacesUtils.getString("OtherBookmarksFolderTitle");
       unfiledItem.folderGuid = PlacesUtils.bookmarks.unfiledGuid;
       let bmMenuItem = this._element("bmRootItem");
       bmMenuItem.label = PlacesUtils.getString("BookmarksMenuFolderTitle");
@@ -409,17 +409,17 @@ var gEditItemOverlay = {
     var defaultItem = this._getFolderMenuItem(aSelectedFolderGuid, title);
     this._folderMenuList.selectedItem = defaultItem;
 
     // Set a selectedIndex attribute to show special icons
     this._folderMenuList.setAttribute("selectedIndex",
                                       this._folderMenuList.selectedIndex);
 
     // Hide the folders-separator if no folder is annotated as recently-used
-    this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6);
+    this._element("foldersSeparator").hidden = (menupopup.children.length <= 6);
     this._folderMenuList.disabled = this.readOnly;
   },
 
   QueryInterface:
   ChromeUtils.generateQI([Ci.nsINavBookmarkObserver]),
 
   _element(aID) {
     return document.getElementById("editBMPanel_" + aID);
@@ -659,23 +659,23 @@ var gEditItemOverlay = {
    *        The identifier of the bookmarks folder.
    * @param aTitle
    *        The title to use in case of menuitem creation.
    * @return handle to the menuitem.
    */
   _getFolderMenuItem(aFolderGuid, aTitle) {
     let menupopup = this._folderMenuList.menupopup;
     let menuItem = Array.prototype.find.call(
-      menupopup.childNodes, item => item.folderGuid === aFolderGuid);
+      menupopup.children, item => item.folderGuid === aFolderGuid);
     if (menuItem !== undefined)
       return menuItem;
 
     // 3 special folders + separator + folder-items-count limit
-    if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
-      menupopup.removeChild(menupopup.lastChild);
+    if (menupopup.children.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
+      menupopup.removeChild(menupopup.lastElementChild);
 
     return this._appendFolderItemToMenupopup(menupopup, aFolderGuid, aTitle);
   },
 
   async onFolderMenuListCommand(aEvent) {
     // Check for _paneInfo existing as the dialog may be closing but receiving
     // async updates from unresolved promises.
     if (!this._paneInfo) {
@@ -747,17 +747,17 @@ var gEditItemOverlay = {
     if (tagsSelectorRow.collapsed)
       return;
 
     let selectedIndex = tagsSelector.selectedIndex;
     let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label
                                          : null;
 
     while (tagsSelector.hasChildNodes()) {
-      tagsSelector.removeChild(tagsSelector.lastChild);
+      tagsSelector.removeChild(tagsSelector.lastElementChild);
     }
 
     let tagsInField = this._getTagsArrayFromTagsInputField();
     let allTags = await PlacesUtils.bookmarks.fetchTags();
     let fragment = document.createDocumentFragment();
     for (let i = 0; i < allTags.length; i++) {
       let tag = allTags[i].name;
       let elt = document.createElement("richlistitem");
@@ -945,17 +945,17 @@ var gEditItemOverlay = {
     if (aItemId == this._paneInfo.itemId || aGuid == this._paneInfo.itemGuid) {
       this._paneInfo.title = aNewTitle;
       this._initTextField(this._namePicker, aNewTitle);
     } else if (this._paneInfo.visibleRows.has("folderRow")) {
       // If the title of a folder which is listed within the folders
       // menulist has been changed, we need to update the label of its
       // representing element.
       let menupopup = this._folderMenuList.menupopup;
-      for (let menuitem of menupopup.childNodes) {
+      for (let menuitem of menupopup.children) {
         if ("folderGuid" in menuitem && menuitem.folderGuid == aGuid) {
           menuitem.label = aNewTitle;
           break;
         }
       }
     }
     // We need to also update title of recent folders.
     if (this._recentFolders) {
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -95,18 +95,18 @@
                 parentId: PlacesUtils.getConcreteItemId(resultNode),
                 parentGuid: PlacesUtils.getConcreteItemGuid(resultNode)
               });
               // We can set folderElt if we are dropping over a static menu that
               // has an internal placespopup.
               let isMenu = elt.localName == "menu" ||
                  (elt.localName == "toolbarbutton" &&
                   elt.getAttribute("type") == "menu");
-              if (isMenu && elt.lastChild &&
-                  elt.lastChild.hasAttribute("placespopup"))
+              if (isMenu && elt.lastElementChild &&
+                  elt.lastElementChild.hasAttribute("placespopup"))
                 dropPoint.folderElt = elt;
               return dropPoint;
             }
 
             let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
                             elt._placesNode.title : null;
             if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
                  !PlacesUIUtils.isFolderReadOnly(elt._placesNode, this._rootView)) ||
@@ -209,18 +209,18 @@
           return timer;
         },
 
         notify: function OF__notify(aTimer) {
           // Function to process all timer notifications.
 
           if (aTimer == this._folder.openTimer) {
             // Timer to open a submenu that's being dragged over.
-            this._folder.elt.lastChild.setAttribute("autoopened", "true");
-            this._folder.elt.lastChild.openPopup();
+            this._folder.elt.lastElementChild.setAttribute("autoopened", "true");
+            this._folder.elt.lastElementChild.openPopup();
             this._folder.openTimer = null;
           } else if (aTimer == this._folder.closeTimer) {
             // Timer to close a submenu that's been dragged off of.
             // Only close the submenu if the mouse isn't being dragged over any
             // of its child menus.
             var draggingOverChild = PlacesControllerDragHelper
                                     .draggingOverChildNode(this._folder.elt);
             if (draggingOverChild)
@@ -264,19 +264,19 @@
             parent = parent.parentNode;
           }
         },
 
         //  The mouse is no longer dragging over the stored menubutton.
         //  Close the menubutton, clear out drag styles, and clear all
         //  timers for opening/closing it.
         clear: function OF__clear() {
-          if (this._folder.elt && this._folder.elt.lastChild) {
-            if (!this._folder.elt.lastChild.hasAttribute("dragover"))
-              this._folder.elt.lastChild.hidePopup();
+          if (this._folder.elt && this._folder.elt.lastElementChild) {
+            if (!this._folder.elt.lastElementChild.hasAttribute("dragover"))
+              this._folder.elt.lastElementChild.hidePopup();
             // remove menuactive style
             this._folder.elt.removeAttribute("_moz-menuactive");
             this._folder.elt = null;
           }
           if (this._folder.openTimer) {
             this._folder.openTimer.cancel();
             this._folder.openTimer = null;
           }
@@ -431,28 +431,28 @@
           event.stopPropagation();
           return;
         }
 
         // We should display the drop indicator relative to the arrowscrollbox.
         let scrollbox = this._scrollBox.boxObject;
         let newMarginTop = 0;
         if (scrollDir == 0) {
-          let elt = this.firstChild;
+          let elt = this.firstElementChild;
           while (elt && event.screenY > elt.boxObject.screenY +
                                         elt.boxObject.height / 2)
-            elt = elt.nextSibling;
+            elt = elt.nextElementSibling;
           newMarginTop = elt ? elt.boxObject.screenY - scrollbox.screenY :
                                scrollbox.height;
         } else if (scrollDir == 1)
           newMarginTop = scrollbox.height;
 
         // Set the new marginTop based on arrowscrollbox.
         newMarginTop += scrollbox.y - this._scrollBox.boxObject.y;
-        this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
+        this._indicatorBar.firstElementChild.style.marginTop = newMarginTop + "px";
         this._indicatorBar.hidden = false;
 
         event.preventDefault();
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragexit"><![CDATA[
         PlacesControllerDragHelper.currentDropTarget = null;
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -535,27 +535,27 @@
           let stringBundle = document.getBindingParent(this)._stringBundle;
 
           let pasteAndSearch, suggestMenuItem;
           let element, label, akey;
 
           element = document.createElementNS(kXULNS, "menuseparator");
           aMenu.appendChild(element);
 
-          let insertLocation = aMenu.firstChild;
-          while (insertLocation.nextSibling &&
+          let insertLocation = aMenu.firstElementChild;
+          while (insertLocation.nextElementSibling &&
                  insertLocation.getAttribute("cmd") != "cmd_paste")
-            insertLocation = insertLocation.nextSibling;
+            insertLocation = insertLocation.nextElementSibling;
           if (insertLocation) {
             element = document.createElementNS(kXULNS, "menuitem");
             label = stringBundle.getString("cmd_pasteAndSearch");
             element.setAttribute("label", label);
             element.setAttribute("anonid", "paste-and-search");
             element.setAttribute("oncommand", "BrowserSearch.pasteAndSearch(event)");
-            aMenu.insertBefore(element, insertLocation.nextSibling);
+            aMenu.insertBefore(element, insertLocation.nextElementSibling);
             pasteAndSearch = element;
           }
 
           element = document.createElementNS(kXULNS, "menuitem");
           label = stringBundle.getString("cmd_clearHistory");
           akey = stringBundle.getString("cmd_clearHistory_accesskey");
           element.setAttribute("label", label);
           element.setAttribute("accesskey", akey);
@@ -1381,19 +1381,19 @@
           let isOneOffSelected =
             this.selectedButton &&
             this.selectedButton.classList.contains("searchbar-engine-one-off-item");
           // Typing de-selects the settings or opensearch buttons at the bottom
           // of the search panel, as typing shows the user intends to search.
           if (this.selectedButton && !isOneOffSelected)
             this.selectedButton = null;
           if (this.query) {
-            groupText = headerSearchText.previousSibling.value +
+            groupText = headerSearchText.previousElementSibling.value +
                         '"' + headerSearchText.value + '"' +
-                        headerSearchText.nextSibling.value;
+                        headerSearchText.nextElementSibling.value;
             if (!isOneOffSelected)
               this.header.selectedIndex = 1;
           } else {
             let noSearchHeader =
               document.getAnonymousElementByAttribute(this, "anonid",
                                                       "searchbar-oneoffheader-search");
             groupText = noSearchHeader.value;
             if (!isOneOffSelected)
@@ -1450,22 +1450,22 @@
             // width has changed.
             if (this._engines && this._textboxWidth == textboxWidth) {
               return;
             }
             this._textboxWidth = textboxWidth;
           }
 
           // Finally, build the list of one-off buttons.
-          while (this.buttons.firstChild != this.settingsButtonCompact)
-            this.buttons.firstChild.remove();
+          while (this.buttons.firstElementChild != this.settingsButtonCompact)
+            this.buttons.firstElementChild.remove();
           // Remove the trailing empty text node introduced by the binding's
           // content markup above.
-          if (this.settingsButtonCompact.nextSibling)
-            this.settingsButtonCompact.nextSibling.remove();
+          if (this.settingsButtonCompact.nextElementSibling)
+            this.settingsButtonCompact.nextElementSibling.remove();
 
           let engines = this.engines;
           let oneOffCount = engines.length;
           let collapsed = !oneOffCount ||
                           (oneOffCount == 1 && engines[0].name == Services.search.currentEngine.name);
 
           // header is a xul:deck so collapsed doesn't work on it, see bug 589569.
           this.header.hidden = this.buttons.collapsed = collapsed;
@@ -1555,17 +1555,17 @@
             if (rowCount == 1 && hasDummyItems) {
               // When there's only one row, make the compact settings button
               // hug the right edge of the panel.  It may not due to the panel's
               // width not being an integral multiple of the button width.  (See
               // the "There will be an emtpy area" comment above.)  Increase the
               // width of the last dummy item by the remainder.
               let remainder = panelWidth - (enginesPerRow * buttonWidth);
               let width = remainder + buttonWidth;
-              let lastDummyItem = this.settingsButtonCompact.previousSibling;
+              let lastDummyItem = this.settingsButtonCompact.previousElementSibling;
               lastDummyItem.setAttribute("width", width);
             }
           }
         ]]></body>
       </method>
 
       <!-- If a page offers more than this number of engines, the add-engines
            menu button is shown, instead of showing the engines directly in the
@@ -1736,31 +1736,31 @@
           }
         ]]></body>
       </method>
 
       <method name="getSelectableButtons">
         <parameter name="aIncludeNonEngineButtons"/>
         <body><![CDATA[
           let buttons = [];
-          for (let oneOff = this.buttons.firstChild; oneOff; oneOff = oneOff.nextSibling) {
+          for (let oneOff = this.buttons.firstElementChild; oneOff; oneOff = oneOff.nextElementSibling) {
             // oneOff may be a text node since the list xul:description contains
             // whitespace and the compact settings button.  See the markup
             // above.  _rebuild removes text nodes, but it may not have been
             // called yet (because e.g. the popup hasn't been opened yet).
             if (oneOff.nodeType == Node.ELEMENT_NODE) {
               if (oneOff.classList.contains("dummy") ||
                   oneOff.classList.contains("search-setting-button-compact"))
                 break;
               buttons.push(oneOff);
             }
           }
 
           if (aIncludeNonEngineButtons) {
-            for (let addEngine = this.addEngines.firstChild; addEngine; addEngine = addEngine.nextSibling) {
+            for (let addEngine = this.addEngines.firstElementChild; addEngine; addEngine = addEngine.nextElementSibling) {
               buttons.push(addEngine);
             }
             buttons.push(this.compact ? this.settingsButtonCompact : this.settingsButton);
           }
 
           return buttons;
         ]]></body>
       </method>
--- a/browser/components/search/content/searchReset.js
+++ b/browser/components/search/content/searchReset.js
@@ -67,16 +67,17 @@ function savePref(value) {
 
 function record(result) {
   Services.telemetry.getHistogramById("SEARCH_RESET_RESULT").add(result);
 }
 
 function keepCurrentEngine() {
   // Calling the currentEngine setter will force a correct loadPathHash to be
   // written for this engine, so that we don't prompt the user again.
+  // eslint-disable-next-line no-self-assign
   Services.search.currentEngine = Services.search.currentEngine;
   record(TELEMETRY_RESULT_ENUM.KEPT_CURRENT);
   savePref("declined");
   doSearch();
 }
 
 function changeSearchEngine() {
   let engine = Services.search.originalDefaultEngine;
--- a/browser/components/syncedtabs/TabListView.js
+++ b/browser/components/syncedtabs/TabListView.js
@@ -90,18 +90,18 @@ TabListView.prototype = {
     this._clearChilden(this.list);
     for (let client of state.clients) {
       if (state.filter) {
         this._renderFilteredClient(client);
       } else {
         this._renderClient(client);
       }
     }
-    if (this.list.firstChild) {
-      const firstTab = this.list.firstChild.querySelector(".item.tab:first-child .item-title");
+    if (this.list.firstElementChild) {
+      const firstTab = this.list.firstElementChild.querySelector(".item.tab:first-child .item-title");
       if (firstTab) {
         firstTab.setAttribute("tabindex", 2);
       }
     }
   },
 
   destroy() {
     this._teardownContextMenu();
@@ -516,17 +516,17 @@ TabListView.prototype = {
 
     menu.openPopupAtScreen(event.screenX, event.screenY, true, event);
   },
 
   adjustContextMenu(menu) {
     let item = this.container.querySelector(".item.selected");
     let showTabOptions = this._isTab(item);
 
-    let el = menu.firstChild;
+    let el = menu.firstElementChild;
 
     while (el) {
       let show = false;
       if (showTabOptions) {
         if (el.getAttribute("id") == "syncedTabsOpenSelectedInPrivateWindow") {
           show = PrivateBrowsingUtils.enabled;
         } else if (el.getAttribute("id") != "syncedTabsOpenAllInTabs" &&
                    el.getAttribute("id") != "syncedTabsManageDevices") {
@@ -537,17 +537,17 @@ TabListView.prototype = {
         show = tabs.length > 0;
       } else if (el.getAttribute("id") == "syncedTabsRefresh") {
         show = true;
       } else if (el.getAttribute("id") == "syncedTabsManageDevices") {
         show = true;
       }
       el.hidden = !show;
 
-      el = el.nextSibling;
+      el = el.nextElementSibling;
     }
   },
 
   /**
    * Find the parent item element, from a given child element.
    * @param {Element} node - Child element.
    * @return {Element} Element for the item, or null if not found.
    */
@@ -584,25 +584,25 @@ TabListView.prototype = {
     // if the node is not a client, find its position within the parent
     if (parent !== itemNode) {
       childPosition = this._indexOfNode(itemNode.parentNode, itemNode);
     }
     return [parentPosition, childPosition];
   },
 
   _indexOfNode(parent, child) {
-    return Array.prototype.indexOf.call(parent.childNodes, child);
+    return Array.prototype.indexOf.call(parent.children, child);
   },
 
   _isTab(item) {
     return item && item.classList.contains("tab");
   },
 
   _isClient(item) {
     return item && item.classList.contains("client");
   },
 
   _openAllClientTabs(clientNode, where) {
-    const tabs = clientNode.querySelector(".item-tabs-list").childNodes;
+    const tabs = clientNode.querySelector(".item-tabs-list").children;
     const urls = [...tabs].map(tab => tab.dataset.url);
     this.props.onOpenTabs(urls, where);
   }
 };
--- a/browser/modules/AsyncTabSwitcher.jsm
+++ b/browser/modules/AsyncTabSwitcher.jsm
@@ -409,17 +409,17 @@ class AsyncTabSwitcher {
     if (this.visibleTab !== showTab) {
       this.tabbrowser._adjustFocusBeforeTabSwitch(this.visibleTab, showTab);
       this.visibleTab = showTab;
 
       this.maybeVisibleTabs.add(showTab);
 
       let tabpanels = this.tabbrowser.tabpanels;
       let showPanel = this.tabbrowser.tabContainer.getRelatedElement(showTab);
-      let index = Array.indexOf(tabpanels.childNodes, showPanel);
+      let index = Array.indexOf(tabpanels.children, showPanel);
       if (index != -1) {
         this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
         tabpanels.setAttribute("selectedIndex", index);
         if (showTab === this.requestedTab) {
           if (this._requestingTab) {
             /*
              * If _requestingTab is set, that means that we're switching the
              * visibility of the tab synchronously, and we need to wait for
--- a/browser/modules/TabsList.jsm
+++ b/browser/modules/TabsList.jsm
@@ -114,20 +114,20 @@ class TabsListBase {
     } else if (this.filterFn(tab)) {
       // The tab now matches our criteria, add a row for it.
       this._addTab(tab);
     }
   }
 
   _addTab(newTab) {
     let newRow = this._createRow(newTab);
-    let nextTab = newTab.nextSibling;
+    let nextTab = newTab.nextElementSibling;
 
     while (nextTab && !this.filterFn(nextTab)) {
-      nextTab = nextTab.nextSibling;
+      nextTab = nextTab.nextElementSibling;
     }
 
     if (nextTab) {
       // If we found a tab after this one in the list, insert the new row before it.
       let nextRow = this.tabToElement.get(nextTab);
       nextRow.parentNode.insertBefore(newRow, nextRow);
     } else {
       // If there's no next tab then insert it as usual.
@@ -152,17 +152,17 @@ const TABS_PANEL_EVENTS = {
   show: "ViewShowing",
   hide: "PanelMultiViewHidden",
 };
 
 class TabsPanel extends TabsListBase {
   constructor(opts) {
     super({
       ...opts,
-      containerNode: opts.containerNode || opts.view.firstChild,
+      containerNode: opts.containerNode || opts.view.firstElementChild,
     });
     this.view = opts.view;
     this.view.addEventListener(TABS_PANEL_EVENTS.show, this);
     this.panelMultiView = null;
   }
 
   handleEvent(event) {
     switch (event.type) {
@@ -245,17 +245,17 @@ class TabsPanel extends TabsListBase {
 
     return row;
   }
 
   _setRowAttributes(row, tab) {
     setAttributes(row, {selected: tab.selected});
 
     let busy = tab.getAttribute("busy");
-    let button = row.firstChild;
+    let button = row.firstElementChild;
     setAttributes(button, {
       busy,
       label: tab.label,
       image: !busy && tab.getAttribute("image"),
       iconloadingprincipal: tab.getAttribute("iconloadingprincipal"),
     });
 
     this._setImageAttributes(row, tab);
@@ -264,17 +264,17 @@ class TabsPanel extends TabsListBase {
     setAttributes(secondaryButton, {
       muted: tab.muted,
       soundplaying: tab.soundPlaying,
       hidden: !(tab.muted || tab.soundPlaying),
     });
   }
 
   _setImageAttributes(row, tab) {
-    let button = row.firstChild;
+    let button = row.firstElementChild;
     let image = this.doc.getAnonymousElementByAttribute(
       button, "class", "toolbarbutton-icon") ||
       this.doc.getAnonymousElementByAttribute(
         button, "class", "toolbarbutton-icon tab-throbber-fallback");
 
     if (image) {
       let busy = tab.getAttribute("busy");
       let progress = tab.getAttribute("progress");
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -67,17 +67,17 @@ def rust_compiler(rustc_info, cargo_info
         die(dedent('''\
         Rust compiler not found.
         To compile rust language sources, you must have 'rustc' in your path.
         See https://www.rust-lang.org/ for more information.
 
         You can install rust by running './mach bootstrap'
         or by directly running the installer from https://rustup.rs/
         '''))
-    rustc_min_version = Version('1.27.0')
+    rustc_min_version = Version('1.28.0')
     cargo_min_version = rustc_min_version
 
     version = rustc_info.version
     if version < rustc_min_version:
         die(dedent('''\
         Rust compiler {} is too old.
 
         To compile Rust language sources please install at least
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -3391,17 +3391,23 @@ function makeNodesForProperties(objProps
 }
 
 function setNodeFullText(loadedProps, node) {
   if (nodeHasFullText(node)) {
     return node;
   }
 
   if (nodeIsLongString(node)) {
-    node.contents.value.fullText = loadedProps.fullText;
+    const {fullText} = loadedProps;
+
+    if (node.contents.value) {
+      node.contents.value.fullText = fullText;
+    } else if (node.contents.getterValue) {
+      node.contents.getterValue.fullText = fullText;
+    }
   }
 
   return node;
 }
 
 function makeNodeForPrototype(objProps, parent) {
   const { prototype } = objProps || {};
 
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -303,16 +303,17 @@ skip-if = true #	Bug 1404382
 [browser_webconsole_inspect_cross_domain_object.js]
 [browser_webconsole_keyboard_accessibility.js]
 [browser_webconsole_location_debugger_link.js]
 [browser_webconsole_location_scratchpad_link.js]
 [browser_webconsole_location_styleeditor_link.js]
 [browser_webconsole_logErrorInPage.js]
 [browser_webconsole_loglimit.js]
 [browser_webconsole_logWarningInPage.js]
+[browser_webconsole_longstring_getter.js]
 [browser_webconsole_longstring.js]
 [browser_webconsole_message_categories.js]
 [browser_webconsole_multiple_windows_and_tabs.js]
 [browser_webconsole_network_attach.js]
 [browser_webconsole_network_exceptions.js]
 [browser_webconsole_network_messages_expand.js]
 skip-if = (os == 'linux') || (os == 'win' && os_version == '10.0' && debug && bits == 64)  # Bug 1429361, disabled on Linux/Win for frequent failures
 [browser_webconsole_network_messages_openinnet.js]
@@ -360,16 +361,17 @@ skip-if = verify
 [browser_webconsole_split_focus.js]
 [browser_webconsole_split_persist.js]
 [browser_webconsole_stacktrace_location_debugger_link.js]
 [browser_webconsole_stacktrace_location_scratchpad_link.js]
 [browser_webconsole_strict_mode_errors.js]
 [browser_webconsole_string.js]
 [browser_webconsole_telemetry_filters_changed.js]
 [browser_webconsole_telemetry_jump_to_definition.js]
+[browser_webconsole_telemetry_object_expanded.js]
 [browser_webconsole_time_methods.js]
 [browser_webconsole_timestamps.js]
 [browser_webconsole_trackingprotection_errors.js]
 tags = trackingprotection
 [browser_webconsole_view_source.js]
 [browser_webconsole_visibility_messages.js]
 [browser_webconsole_warn_about_replaced_api.js]
 [browser_webconsole_websocket.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_longstring_getter.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that getter properties that return long strings can be expanded. See Bug 1481833.
+
+"use strict";
+
+const LONGSTRING = "a ".repeat(10000);
+const TEST_URI = `data:text/html,Test expanding longString getter property
+  <svg>
+    <image xlink:href="data:image/png;base64,${LONGSTRING}"></image>
+  </svg>
+  <script>
+    console.dir("Test message", document.querySelector("svg image").href);
+  </script>`;
+
+add_task(async function() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  // Retrieve the logged message.
+  const message = await waitFor(() => findMessage(hud, "Test message"));
+
+  // Wait until the SVGAnimatedString is expanded.
+  await waitFor(() => message.querySelectorAll(".arrow").length > 1);
+
+  const arrow = message.querySelectorAll(".arrow")[1];
+  ok(arrow, "longString expand arrow is shown");
+
+  info("wait for long string expansion");
+  const onLongStringFullTextDisplayed = waitFor(() => findMessage(hud, LONGSTRING));
+  arrow.click();
+  await onLongStringFullTextDisplayed;
+
+  ok(true, "The full text of the longString is displayed");
+
+  info("wait for long string collapse");
+  const onLongStringCollapsed = waitFor(() => !findMessage(hud, LONGSTRING));
+  arrow.click();
+  await onLongStringCollapsed;
+
+  ok(true, "The longString can be collapsed");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_telemetry_object_expanded.js
@@ -0,0 +1,71 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the object_expanded telemetry event.
+
+"use strict";
+
+const TEST_URI = `data:text/html,<meta charset=utf8><script>
+  console.log("test message", [1,2,3]);
+</script>`;
+
+const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
+
+add_task(async function() {
+  // Let's reset the counts.
+  Services.telemetry.clearEvents();
+
+  // Ensure no events have been logged
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  ok(!snapshot.parent, "No events have been logged for the main process");
+
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  const message = await waitFor(() => findMessage(hud, "test message"));
+
+  info("Click on the arrow icon to expand the node");
+  const arrowIcon = message.querySelector(".arrow");
+  arrowIcon.click();
+
+  // let's wait until we have 2 arrows (i.e. the object was expanded)
+  await waitFor(() => message.querySelectorAll(".arrow").length === 2);
+
+  let events = getObjectExpandedEventsExtra();
+  is(events.length, 1, "There was 1 event logged");
+  const [event] = events;
+  ok(event.session_id > 0, "There is a valid session_id in the logged event");
+
+  info("Click on the second arrow icon to expand the prototype node");
+  const secondArrowIcon = message.querySelectorAll(".arrow")[1];
+  secondArrowIcon.click();
+  // let's wait until we have more than 2 arrows displayed, i.e. the prototype node was
+  // expanded.
+  await waitFor(() => message.querySelectorAll(".arrow").length > 2);
+
+  events = getObjectExpandedEventsExtra();
+  is(events.length, 1, "There was an event logged when expanding a child node");
+
+  info("Click the first arrow to collapse the object");
+  arrowIcon.click();
+  // Let's wait until there's only one arrow visible, i.e. the node is collapsed.
+  await waitFor(() => message.querySelectorAll(".arrow").length === 1);
+
+  ok(!snapshot.parent, "There was no event logged when collapsing the node");
+});
+
+function getObjectExpandedEventsExtra() {
+  // Retrieve and clear telemetry events.
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+
+  const events = snapshot.parent.filter(event =>
+    event[1] === "devtools.main" &&
+    event[2] === "object_expanded" &&
+    event[3] === "webconsole"
+  );
+
+  // Since we already know we have the correct event, we only return the `extra` field
+  // that was passed to it (which is event[5] here).
+  return events.map(event => event[5]);
+}
--- a/devtools/client/webide/content/runtimedetails.js
+++ b/devtools/client/webide/content/runtimedetails.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const {Connection} = require("devtools/shared/client/connection-manager");
-const {RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
+const {RuntimeTypes} = require("devtools/client/webide/modules/runtime-types");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 const UNRESTRICTED_HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Running_and_debugging_apps#Unrestricted_app_debugging_%28including_certified_apps_main_process_etc.%29";
 
 window.addEventListener("load", function() {
   document.querySelector("#close").onclick = CloseUI;
   document.querySelector("#devtools-check button").onclick = EnableCertApps;
   document.querySelector("#adb-check button").onclick = RootADB;
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -8,17 +8,18 @@ const {FileUtils} = require("resource://
 const EventEmitter = require("devtools/shared/event-emitter");
 const {OS} = require("resource://gre/modules/osfile.jsm");
 const {AppProjects} = require("devtools/client/webide/modules/app-projects");
 const TabStore = require("devtools/client/webide/modules/tab-store");
 const {AppValidator} = require("devtools/client/webide/modules/app-validator");
 const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
 const {getDeviceFront} = require("devtools/shared/fronts/device");
 const {getPreferenceFront} = require("devtools/shared/fronts/preference");
-const {RuntimeScanners, RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
+const {RuntimeScanners} = require("devtools/client/webide/modules/runtimes");
+const {RuntimeTypes} = require("devtools/client/webide/modules/runtime-types");
 const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var AppManager = exports.AppManager = {
 
   DEFAULT_PROJECT_ICON: "chrome://webide/skin/default-app-icon.png",
--- a/devtools/client/webide/modules/moz.build
+++ b/devtools/client/webide/modules/moz.build
@@ -7,12 +7,13 @@
 DevToolsModules(
     'addons.js',
     'app-manager.js',
     'app-projects.js',
     'app-validator.js',
     'config-view.js',
     'project-list.js',
     'runtime-list.js',
+    'runtime-types.js',
     'runtimes.js',
     'tab-store.js',
     'utils.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/webide/modules/runtime-types.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// These type strings are used for logging events to Telemetry.
+// You must update Histograms.json if new types are added.
+const RuntimeTypes = {
+  USB: "USB",
+  WIFI: "WIFI",
+  REMOTE: "REMOTE",
+  LOCAL: "LOCAL",
+  OTHER: "OTHER"
+};
+
+exports.RuntimeTypes = RuntimeTypes;
--- a/devtools/client/webide/modules/runtimes.js
+++ b/devtools/client/webide/modules/runtimes.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const Services = require("Services");
 const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
 const {DebuggerServer} = require("devtools/server/main");
 const discovery = require("devtools/shared/discovery/discovery");
 const EventEmitter = require("devtools/shared/event-emitter");
+const {RuntimeTypes} = require("devtools/client/webide/modules/runtime-types");
 const promise = require("promise");
 loader.lazyRequireGetter(this, "AuthenticationResult",
   "devtools/shared/security/auth", true);
 loader.lazyRequireGetter(this, "DevToolsUtils",
   "devtools/shared/DevToolsUtils");
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
@@ -307,27 +308,17 @@ var StaticScanner = {
     }
     return runtimes;
   }
 };
 
 EventEmitter.decorate(StaticScanner);
 RuntimeScanners.add(StaticScanner);
 
-/* RUNTIMES */
-
-// These type strings are used for logging events to Telemetry.
-// You must update Histograms.json if new types are added.
-var RuntimeTypes = exports.RuntimeTypes = {
-  USB: "USB",
-  WIFI: "WIFI",
-  REMOTE: "REMOTE",
-  LOCAL: "LOCAL",
-  OTHER: "OTHER"
-};
+exports.RuntimeTypes = RuntimeTypes;
 
 function WiFiRuntime(deviceName) {
   this.deviceName = deviceName;
 }
 
 WiFiRuntime.prototype = {
   type: RuntimeTypes.WIFI,
   // Mark runtime as taking a long time to connect
--- a/devtools/server/actors/inspector/custom-element-watcher.js
+++ b/devtools/server/actors/inspector/custom-element-watcher.js
@@ -8,17 +8,19 @@ const {Cu} = require("chrome");
 const InspectorUtils = require("InspectorUtils");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 /**
  * The CustomElementWatcher can be used to be notified if a custom element definition
  * is created for a node.
  *
  * When a custom element is defined for a monitored name, an "element-defined" event is
- * fired with the Set of impacted node actors as argument.
+ * fired with the following Object argument:
+ * - {String} name: name of the custom element defined
+ * - {Set} Set of impacted node actors
  */
 class CustomElementWatcher extends EventEmitter {
   constructor(chromeEventHandler) {
     super();
 
     this.chromeEventHandler = chromeEventHandler;
     this._onCustomElementDefined = this._onCustomElementDefined.bind(this);
     this.chromeEventHandler.addEventListener("customelementdefined",
@@ -54,70 +56,77 @@ class CustomElementWatcher extends Event
     if (!this._shouldWatchDefinition(nodeActor)) {
       return;
     }
 
     const registry = nodeActor.rawNode.ownerGlobal.customElements;
     const registryMap = this._getMapForRegistry(registry);
 
     const name = nodeActor.rawNode.localName;
-    if (!registryMap.has(name)) {
-      // Create a new entry in the Map for this name.
-      registryMap.set(name, new Set());
-    }
-    registryMap.get(name).add(nodeActor);
+    const actorsSet = this._getActorsForName(name, registryMap);
+    actorsSet.add(nodeActor);
   }
 
   /**
    * Stop watching the provided NodeActor.
    */
   unmanageNode(nodeActor) {
     if (!this._isValidNode(nodeActor)) {
       return;
     }
 
     const win = nodeActor.rawNode.ownerGlobal;
     const registry = win.customElements;
     const registryMap = this._getMapForRegistry(registry);
     const name = nodeActor.rawNode.localName;
-    if (registryMap && registryMap.has(name)) {
+    if (registryMap.has(name)) {
       registryMap.get(name).delete(nodeActor);
     }
   }
 
   /**
    * Retrieve the map of name->nodeActors for a given CustomElementsRegistry.
    * Will create the map if not created yet.
    */
   _getMapForRegistry(registry) {
     if (!this.watchedRegistries.has(registry)) {
       this.watchedRegistries.set(registry, new Map());
     }
     return this.watchedRegistries.get(registry);
   }
 
+  /**
+   * Retrieve the set of nodeActors for a given name and registry.
+   * Will create the set if not created yet.
+   */
+  _getActorsForName(name, registryMap) {
+    if (!registryMap.has(name)) {
+      registryMap.set(name, new Set());
+    }
+    return registryMap.get(name);
+  }
+
   _shouldWatchDefinition(nodeActor) {
     const doc = nodeActor.rawNode.ownerDocument;
     const namespaceURI = doc.documentElement.namespaceURI;
     const name = nodeActor.rawNode.localName;
     const isValidName = InspectorUtils.isCustomElementName(name, namespaceURI);
 
     const customElements = doc.defaultView.customElements;
     return isValidName && !customElements.get(name);
   }
 
   _onCustomElementDefined(event) {
     const doc = event.target;
     const registry = doc.defaultView.customElements;
     const registryMap = this._getMapForRegistry(registry);
 
     const name = event.detail;
-    const nodeActors = registryMap.get(name);
-
-    this.emit("element-defined", nodeActors);
+    const actors = this._getActorsForName(name, registryMap);
+    this.emit("element-defined", { name, actors });
     registryMap.delete(name);
   }
 
   /**
    * Some nodes (e.g. inside of <template> tags) don't have a documentElement or an
    * ownerGlobal and can't be watched by this helper.
    */
   _isValidNode(nodeActor) {
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -345,20 +345,20 @@ var WalkerActor = protocol.ActorClassWit
     }
 
     this.customElementWatcher.manageNode(actor);
 
     return actor;
   },
 
   /**
-   * When a custom element is defined for one of the names currently watched, send a
-   * customElementDefined mutation for all the NodeActors using this tag name.
+   * When a custom element is defined, send a customElementDefined mutation for all the
+   * NodeActors using this tag name.
    */
-  onCustomElementDefined: function(actors) {
+  onCustomElementDefined: function({name, actors}) {
     actors.forEach(actor => this.queueMutation({
       target: actor.actorID,
       type: "customElementDefined",
       customElementLocation: actor.getCustomElementLocation(),
     }));
   },
 
   _onReflows: function(reflows) {
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb-binary.js
@@ -0,0 +1,145 @@
+/* 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";
+
+loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
+loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");
+loader.lazyRequireGetter(this, "Services");
+loader.lazyRequireGetter(this, "FileUtils",
+                         "resource://gre/modules/FileUtils.jsm", true);
+loader.lazyRequireGetter(this, "NetUtil",
+                         "resource://gre/modules/NetUtil.jsm", true);
+loader.lazyGetter(this, "UNPACKED_ROOT_PATH", () => {
+  return OS.Path.join(OS.Constants.Path.localProfileDir, "adb");
+});
+
+// FIXME: Bug 1481691 - Read the extension ID from a pref.
+const EXTENSION_ID = "adb@mozilla.org";
+
+async function getAdbInfo(adbUri) {
+  return new Promise(resolve => {
+    NetUtil.asyncFetch({
+      uri: adbUri,
+      loadUsingSystemPrincipal: true
+    }, (input) => {
+      try {
+        const string = NetUtil.readInputStreamToString(input, input.available());
+        resolve(JSON.parse(string));
+      } catch (e) {
+        console.log(`Could not read adb.json in the extension: ${e}`);
+        resolve(null);
+      }
+    });
+  });
+}
+
+/**
+ * Unpack file from the extension.
+ * Uses NetUtil to read and write, since it's required for reading.
+ *
+ * @param {string} file
+ *        The path name of the file in the extension, such as "win32/adb.exe".
+ *        This has to have a '/' in the path string.
+ */
+async function unpackFile(file) {
+  const policy = ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
+  if (!policy) {
+    return;
+  }
+
+  // Assumes that destination dir already exists.
+  const filePath = OS.Path.join(UNPACKED_ROOT_PATH, file.split("/")[1]);
+  await new Promise((resolve, reject) => {
+    NetUtil.asyncFetch({
+      uri: policy.getURL(file),
+      loadUsingSystemPrincipal: true
+    }, (input) => {
+      try {
+        // Since we have to use NetUtil to read, probably it's okay to use for
+        // writing, rather than bouncing to OS.File...?
+        const outputFile = new FileUtils.File(filePath);
+        const output = FileUtils.openAtomicFileOutputStream(outputFile);
+        NetUtil.asyncCopy(input, output, resolve);
+      } catch (e) {
+        console.log(`Could not unpack file ${file} in the extension: ${e}`);
+        reject(e);
+      }
+    });
+  });
+  // Mark binaries as executable.
+  await OS.File.setPermissions(filePath, { unixMode: 0o744 });
+}
+
+/**
+ * Extract files in the extension into local profile directory and returns
+ * the path for the adb binary.
+ */
+async function extractBinary() {
+  const policy = ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
+  if (!policy) {
+    return null;
+  }
+  const uri = policy.getURL("adb.json");
+
+  const adbInfo = await getAdbInfo(uri);
+  if (!adbInfo) {
+    return null;
+  }
+
+  let filesForAdb;
+  try {
+    // The adbInfo is an object looks like this;
+    //
+    //  {
+    //    "Linux": {
+    //      "x86": [
+    //        "linux/adb"
+    //      ],
+    //      "x86_64": [
+    //        "linux64/adb"
+    //      ]
+    //    },
+    // ...
+    filesForAdb =
+      // XPCOMABI looks this; x86_64-gcc3, so drop the compiler name.
+      adbInfo[Services.appinfo.OS][Services.appinfo.XPCOMABI.split("-")[0]];
+  } catch (e) {
+    return null;
+  }
+
+  await OS.File.makeDir(UNPACKED_ROOT_PATH);
+
+  for (const file of filesForAdb) {
+    try {
+      await unpackFile(file);
+    } catch (e) {
+      return null;
+    }
+  }
+
+  let adbBinaryPath = OS.Path.join(UNPACKED_ROOT_PATH, "adb");
+
+  if (Services.appinfo.OS === "WINNT") {
+    adbBinaryPath += ".exe";
+  }
+  return adbBinaryPath;
+}
+
+/**
+ * Get a file object for the adb binary from the 'adb@mozilla.org' extension
+ * which has been already installed.
+ *
+ * @return {nsIFile}
+ *        File object for the binary.
+ */
+async function getFileForBinary() {
+  const path = await extractBinary();
+  if (!path) {
+    return null;
+  }
+  console.log(`Binary path: ${path}`);
+  return new FileUtils.File(path);
+}
+exports.getFileForBinary = getFileForBinary;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb-client.js
@@ -0,0 +1,90 @@
+/* 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/. */
+
+/*
+ * A module to track device changes
+ * Adapted from adb.js at
+ * https://github.com/mozilla/adbhelper/tree/f44386c2d8cb7635a7d2c5a51191c89b886f8327
+ */
+
+"use strict";
+
+const { AdbSocket } = require("./adb-socket");
+
+const OKAY = 0x59414b4f;
+const FAIL = 0x4c494146;
+
+const _sockets = [ ];
+
+// Return buffer, which differs between Gecko versions
+function getBuffer(packet) {
+  return packet.buffer ? packet.buffer : packet;
+}
+
+// @param aPacket         The packet to get the length from.
+// @param aIgnoreResponse True if this packet has no OKAY/FAIL.
+// @return                A js object { length:...; data:... }
+function unpackPacket(packet, ignoreResponse) {
+  const buffer = getBuffer(packet);
+  console.debug("Len buffer: " + buffer.byteLength);
+  if (buffer.byteLength === 4 && !ignoreResponse) {
+    console.debug("Packet empty");
+    return { length: 0, data: "" };
+  }
+  const lengthView = new Uint8Array(buffer, ignoreResponse ? 0 : 4, 4);
+  const decoder = new TextDecoder();
+  const length = parseInt(decoder.decode(lengthView), 16);
+  const text = new Uint8Array(buffer, ignoreResponse ? 4 : 8, length);
+  return { length, data: decoder.decode(text) };
+}
+
+// Checks if the response is expected (defaults to OKAY).
+// @return true if response equals expected.
+function checkResponse(packet, expected = OKAY) {
+  const buffer = getBuffer(packet);
+  const view = new Uint32Array(buffer, 0, 1);
+  if (view[0] == FAIL) {
+    console.debug("Response: FAIL");
+  }
+  console.debug("view[0] = " + view[0]);
+  return view[0] == expected;
+}
+
+// @param aCommand A protocol-level command as described in
+//  http://androidxref.com/4.0.4/xref/system/core/adb/OVERVIEW.TXT and
+//  http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
+// @return A 8 bit typed array.
+function createRequest(command) {
+  let length = command.length.toString(16).toUpperCase();
+  while (length.length < 4) {
+    length = "0" + length;
+  }
+
+  const encoder = new TextEncoder();
+  console.debug("Created request: " + length + command);
+  return encoder.encode(length + command);
+}
+
+function close() {
+  _sockets.forEach(function(s) {
+    s.close();
+  });
+}
+
+function connect() {
+  const tmp = new AdbSocket();
+  _sockets.push(tmp);
+  return tmp;
+}
+
+const client = {
+  getBuffer,
+  unpackPacket,
+  checkResponse,
+  createRequest,
+  connect,
+  close
+};
+
+module.exports = client;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb-device.js
@@ -0,0 +1,52 @@
+/* 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";
+
+const { ADB } = require("devtools/shared/adb/adb");
+
+/**
+ * A Device instance is created and registered with the Devices module whenever
+ * ADB notices a new device is connected.
+ */
+function Device(id) {
+  this.id = id;
+}
+
+Device.prototype = {
+  type: "adb",
+
+  shell: ADB.shell.bind(ADB),
+  forwardPort: ADB.forwardPort.bind(ADB),
+  push: ADB.push.bind(ADB),
+  pull: ADB.pull.bind(ADB),
+  reboot: ADB.reboot.bind(ADB),
+  rebootRecovery: ADB.rebootRecovery.bind(ADB),
+  rebootBootloader: ADB.rebootBootloader.bind(ADB),
+
+  isRoot() {
+    return ADB.shell("id").then(stdout => {
+      if (stdout) {
+        const uid = stdout.match(/uid=(\d+)/)[1];
+        return uid == "0";
+      }
+      return false;
+    });
+  },
+
+  summonRoot() {
+    return ADB.root();
+  },
+
+  getModel() {
+    if (this._modelPromise) {
+      return this._modelPromise;
+    }
+    this._modelPromise = this.shell("getprop ro.product.model")
+                             .then(model => model.trim());
+    return this._modelPromise;
+  }
+};
+
+module.exports = Device;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb-running-checker.js
@@ -0,0 +1,90 @@
+/* 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/.
+ */
+
+/*
+ * Uses host:version service to detect if ADB is running
+ * Modified from adb-file-transfer from original ADB
+ */
+
+"use strict";
+
+const client = require("./adb-client");
+
+exports.check = async function check() {
+  let socket;
+  let state;
+  let timerID;
+  const TIMEOUT_TIME = 1000;
+
+  console.debug("Asking for host:version");
+
+  return new Promise(resolve => {
+    // On MacOSX connecting to a port which is not started listening gets
+    // stuck (bug 1481963), to avoid the stuck, we do forcibly fail the
+    // connection after |TIMEOUT_TIME| elapsed.
+    timerID = setTimeout(() => {
+      socket.close();
+      resolve(false);
+    }, TIMEOUT_TIME);
+
+    function finish(returnValue) {
+      clearTimeout(timerID);
+      resolve(returnValue);
+    }
+
+    const runFSM = function runFSM(packetData) {
+      console.debug("runFSM " + state);
+      switch (state) {
+        case "start":
+          const req = client.createRequest("host:version");
+          socket.send(req);
+          state = "wait-version";
+          break;
+        case "wait-version":
+          // TODO: Actually check the version number to make sure the daemon
+          //       supports the commands we want to use
+          const { length, data } = client.unpackPacket(packetData);
+          console.debug("length: ", length, "data: ", data);
+          socket.close();
+          const version = parseInt(data, 16);
+          if (version >= 31) {
+            finish(true);
+          } else {
+            console.log("killing existing adb as we need version >= 31");
+            finish(false);
+          }
+          break;
+        default:
+          console.debug("Unexpected State: " + state);
+          finish(false);
+      }
+    };
+
+    const setupSocket = function() {
+      socket.s.onerror = function(event) {
+        console.debug("running checker onerror");
+        finish(false);
+      };
+
+      socket.s.onopen = function(event) {
+        console.debug("running checker onopen");
+        state = "start";
+        runFSM();
+      };
+
+      socket.s.onclose = function(event) {
+        console.debug("running checker onclose");
+      };
+
+      socket.s.ondata = function(event) {
+        console.debug("running checker ondata");
+        runFSM(event.data);
+      };
+    };
+
+    socket = client.connect();
+    setupSocket();
+  });
+};
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb-scanner.js
@@ -0,0 +1,204 @@
+/* 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";
+
+const EventEmitter = require("devtools/shared/event-emitter");
+const { ConnectionManager } =
+  require("devtools/shared/client/connection-manager");
+const { Devices } =
+  require("devtools/shared/apps/Devices.jsm");
+const { RuntimeTypes } =
+  require("devtools/client/webide/modules/runtime-types");
+
+const ADBScanner = {
+
+  _runtimes: [],
+
+  enable() {
+    this._updateRuntimes = this._updateRuntimes.bind(this);
+    Devices.on("register", this._updateRuntimes);
+    Devices.on("unregister", this._updateRuntimes);
+    Devices.on("addon-status-updated", this._updateRuntimes);
+    this._updateRuntimes();
+  },
+
+  disable() {
+    Devices.off("register", this._updateRuntimes);
+    Devices.off("unregister", this._updateRuntimes);
+    Devices.off("addon-status-updated", this._updateRuntimes);
+  },
+
+  _emitUpdated() {
+    this.emit("runtime-list-updated");
+  },
+
+  _updateRuntimes() {
+    if (this._updatingPromise) {
+      return this._updatingPromise;
+    }
+    this._runtimes = [];
+    const promises = [];
+    for (const id of Devices.available()) {
+      const device = Devices.getByName(id);
+      promises.push(this._detectRuntimes(device));
+    }
+    this._updatingPromise = Promise.all(promises);
+    this._updatingPromise.then(() => {
+      this._emitUpdated();
+      this._updatingPromise = null;
+    }, () => {
+      this._updatingPromise = null;
+    });
+    return this._updatingPromise;
+  },
+
+  _detectRuntimes: async function(device) {
+    const model = await device.getModel();
+    let detectedRuntimes = await FirefoxOSRuntime.detect(device, model);
+    this._runtimes.push(...detectedRuntimes);
+    detectedRuntimes = await FirefoxOnAndroidRuntime.detect(device, model);
+    this._runtimes.push(...detectedRuntimes);
+  },
+
+  scan() {
+    return this._updateRuntimes();
+  },
+
+  listRuntimes() {
+    return this._runtimes;
+  }
+
+};
+
+EventEmitter.decorate(ADBScanner);
+
+function Runtime(device, model, socketPath) {
+  this.device = device;
+  this._model = model;
+  this._socketPath = socketPath;
+}
+
+Runtime.prototype = {
+  type: RuntimeTypes.USB,
+  connect(connection) {
+    const port = ConnectionManager.getFreeTCPPort();
+    const local = "tcp:" + port;
+    let remote;
+    if (this._socketPath.startsWith("@")) {
+      remote = "localabstract:" + this._socketPath.substring(1);
+    } else {
+      remote = "localfilesystem:" + this._socketPath;
+    }
+    return this.device.forwardPort(local, remote).then(() => {
+      connection.host = "localhost";
+      connection.port = port;
+      connection.connect();
+    });
+  },
+  get id() {
+    return this.device.id + "|" + this._socketPath;
+  },
+};
+
+// FIXME: Bug 1481691 - Drop code for support FirefoxOS.
+function FirefoxOSRuntime(device, model) {
+  Runtime.call(this, device, model, "/data/local/debugger-socket");
+}
+
+FirefoxOSRuntime.detect = async function(device, model) {
+  const runtimes = [];
+  const query = "test -f /system/b2g/b2g; echo $?";
+  let b2gExists = await device.shell(query);
+  // XXX: Sometimes we get an empty response back.  Likely a bug in our shell
+  // code in this add-on.
+  // There are also some Android devices that do not have `test` installed.
+  for (let attempts = 3; attempts > 0; attempts--) {
+    b2gExists = await device.shell(query);
+    if (b2gExists.length == 3) {
+      break;
+    }
+  }
+  if (b2gExists === "0\r\n") {
+    const runtime = new FirefoxOSRuntime(device, model);
+    console.log("Found " + runtime.name);
+    runtimes.push(runtime);
+  }
+  return runtimes;
+};
+
+FirefoxOSRuntime.prototype = Object.create(Runtime.prototype);
+
+Object.defineProperty(FirefoxOSRuntime.prototype, "name", {
+  get() {
+    return this._model || this.device.id;
+  }
+});
+
+function FirefoxOnAndroidRuntime(device, model, socketPath) {
+  Runtime.call(this, device, model, socketPath);
+}
+
+// This requires Unix socket support from Firefox for Android (35+)
+FirefoxOnAndroidRuntime.detect = async function(device, model) {
+  const runtimes = [];
+  // A matching entry looks like:
+  // 00000000: 00000002 00000000 00010000 0001 01 6551588
+  //  /data/data/org.mozilla.fennec/firefox-debugger-socket
+  const query = "cat /proc/net/unix";
+  const rawSocketInfo = await device.shell(query);
+  let socketInfos = rawSocketInfo.split(/\r?\n/);
+  // Filter to lines with "firefox-debugger-socket"
+  socketInfos = socketInfos.filter(l => l.includes("firefox-debugger-socket"));
+  // It's possible to have multiple lines with the same path, so de-dupe them
+  const socketPaths = new Set();
+  for (const socketInfo of socketInfos) {
+    const socketPath = socketInfo.split(" ").pop();
+    socketPaths.add(socketPath);
+  }
+  for (const socketPath of socketPaths) {
+    const runtime = new FirefoxOnAndroidRuntime(device, model, socketPath);
+    console.log("Found " + runtime.name);
+    runtimes.push(runtime);
+  }
+  return runtimes;
+};
+
+FirefoxOnAndroidRuntime.prototype = Object.create(Runtime.prototype);
+
+Object.defineProperty(FirefoxOnAndroidRuntime.prototype, "name", {
+  get() {
+    // If using abstract socket address, it is "@org.mozilla.firefox/..."
+    // If using path base socket, it is "/data/data/<package>...""
+    // Until Fennec 62 only supports path based UNIX domain socket, but
+    // Fennec 63+ supports both path based and abstract socket.
+    const packageName = this._socketPath.startsWith("@") ?
+                        this._socketPath.substr(1).split("/")[0] :
+                        this._socketPath.split("/")[3];
+    let channel;
+    switch (packageName) {
+      case "org.mozilla.firefox":
+        channel = "";
+        break;
+      case "org.mozilla.firefox_beta":
+        channel = " Beta";
+        break;
+      case "org.mozilla.fennec_aurora":
+        // This package name is now the one for Firefox Nightly distributed
+        // through the Google Play Store since "dawn project"
+        // cf. https://bugzilla.mozilla.org/show_bug.cgi?id=1357351#c8
+        channel = " Nightly";
+        break;
+      case "org.mozilla.fennec":
+        channel = " Nightly";
+        break;
+      default:
+        channel = " Custom";
+    }
+    return "Firefox" + channel + " on Android (" +
+           (this._model || this.device.id) + ")";
+  }
+});
+
+exports.ADBScanner = ADBScanner;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb-socket.js
@@ -0,0 +1,74 @@
+/* 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";
+
+const { Cu } = require("chrome");
+
+function createTCPSocket(location, port, options) {
+  // Starting with FF57, jsm share the same global and requires some special code
+  const { TCPSocket } =
+    Cu.getGlobalForObject(Cu.import("resource://gre/modules/Services.jsm", {}));
+
+  // Starting with FF43, TCPSocket is now exposed via WebIDL
+  return new TCPSocket(location, port, options);
+}
+
+// Creates a socket connected to the adb instance.
+// This instantiation is sync, and returns before we know if opening the
+// connection succeeds. Callers must attach handlers to the s field.
+class AdbSocket {
+  constructor() {
+    this.s = createTCPSocket("127.0.0.1", 5037, { binaryType: "arraybuffer" });
+  }
+
+  /**
+   * Dump the first few bytes of the given array to the console.
+   *
+   * @param {TypedArray} inputArray
+   *        the array to dump
+   */
+  _hexdump(inputArray) {
+    const decoder = new TextDecoder("windows-1252");
+    const array = new Uint8Array(inputArray.buffer);
+    const s = decoder.decode(array);
+    const len = array.length;
+    let dbg = "len=" + len + " ";
+    const l = len > 20 ? 20 : len;
+
+    for (let i = 0; i < l; i++) {
+      let c = array[i].toString(16);
+      if (c.length == 1) {
+        c = "0" + c;
+      }
+      dbg += c;
+    }
+    dbg += " ";
+    for (let i = 0; i < l; i++) {
+      const c = array[i];
+      if (c < 32 || c > 127) {
+        dbg += ".";
+      } else {
+        dbg += s[i];
+      }
+    }
+    console.debug(dbg);
+  }
+
+  // debugging version of tcpsocket.send()
+  send(array) {
+    this._hexdump(array);
+
+    this.s.send(array.buffer, array.byteOffset, array.byteLength);
+  }
+
+  close() {
+    if (this.s.readyState === "open" ||
+        this.s.readyState === "connecting") {
+      this.s.close();
+    }
+  }
+}
+
+exports.AdbSocket = AdbSocket;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/adb.js
@@ -0,0 +1,969 @@
+/* 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/. */
+
+// Wrapper around the ADB utility.
+
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const EventEmitter = require("devtools/shared/event-emitter");
+const client = require("./adb-client");
+const { getFileForBinary } = require("./adb-binary");
+const { setTimeout } = require("resource://gre/modules/Timer.jsm");
+const { PromiseUtils } = require("resource://gre/modules/PromiseUtils.jsm");
+const { OS } = require("resource://gre/modules/osfile.jsm");
+const { Services } = require("resource://gre/modules/Services.jsm");
+loader.lazyRequireGetter(this, "check",
+                         "devtools/shared/adb/adb-running-checker", true);
+
+let ready = false;
+let didRunInitially = false;
+
+const OKAY = 0x59414b4f;
+// const FAIL = 0x4c494146;
+// const STAT = 0x54415453;
+const DATA = 0x41544144;
+const DONE = 0x454e4f44;
+
+const ADB = {
+  get didRunInitially() {
+    return didRunInitially;
+  },
+  set didRunInitially(newVal) {
+    didRunInitially = newVal;
+  },
+
+  get ready() {
+    return ready;
+  },
+  set ready(newVal) {
+    ready = newVal;
+  },
+
+  get adbFilePromise() {
+    if (this._adbFilePromise) {
+      return this._adbFilePromise;
+    }
+    this._adbFilePromise = getFileForBinary();
+    return this._adbFilePromise;
+  },
+
+  async _runProcess(process, params) {
+    return new Promise((resolve, reject) => {
+      process.runAsync(params, params.length, {
+        observe(subject, topic, data) {
+          switch (topic) {
+            case "process-finished":
+              resolve();
+              break;
+            case "process-failed":
+              reject();
+              break;
+          }
+        }
+      }, false);
+    });
+  },
+
+  // Waits until a predicate returns true or re-tries the predicate calls
+  // |retry| times, we wait for 100ms between each calls.
+  async _waitUntil(predicate, retry = 20) {
+    let count = 0;
+    while (count++ < retry) {
+      if (await predicate()) {
+        return true;
+      }
+      // Wait for 100 milliseconds.
+      await new Promise(resolve => setTimeout(resolve, 100));
+    }
+    // Timed out after trying too many times.
+    return false;
+  },
+
+  // We startup by launching adb in server mode, and setting
+  // the tcp socket preference to |true|
+  async start() {
+    return new Promise(async (resolve, reject) => {
+      const onSuccessfulStart = () => {
+        Services.obs.notifyObservers(null, "adb-ready");
+        this.ready = true;
+        resolve();
+      };
+
+      const isAdbRunning = await check();
+      if (isAdbRunning) {
+        this.didRunInitially = false;
+        console.log("Found ADB process running, not restarting");
+        onSuccessfulStart();
+        return;
+      }
+      console.log("Didn't find ADB process running, restarting");
+
+      this.didRunInitially = true;
+      const process = Cc["@mozilla.org/process/util;1"]
+                      .createInstance(Ci.nsIProcess);
+      // FIXME: Bug 1481691 - We should avoid extracting files every time.
+      const adbFile = await this.adbFilePromise;
+      process.init(adbFile);
+      // Hide command prompt window on Windows
+      try {
+        // startHidden is 55+
+        process.startHidden = true;
+        // noShell attribute is 58+
+        process.noShell = true;
+      } catch (e) {
+      }
+      const params = ["start-server"];
+      let isStarted = false;
+      try {
+        await this._runProcess(process, params);
+        isStarted = await this._waitUntil(check);
+      } catch (e) {
+      }
+
+      if (isStarted) {
+        onSuccessfulStart();
+      } else {
+        this.ready = false;
+        reject();
+      }
+    });
+  },
+
+  /**
+   * Stop the ADB server, but only if we started it.  If it was started before
+   * us, we return immediately.
+   *
+   * @param boolean sync
+   *        In case, we do need to kill the server, this param is passed through
+   *        to kill to determine whether it's a sync operation.
+   */
+  async stop(sync) {
+    if (!this.didRunInitially) {
+      return; // We didn't start the server, nothing to do
+    }
+    await this.kill(sync);
+    // Make sure the ADB server stops listening because kill() above doesn't
+    // mean that the ADB server stops, it means that 'adb kill-server' command
+    // just finished, so that it's possible that the ADB server is still alive.
+    await this._waitUntil(async () => {
+      return !await check();
+    });
+  },
+
+  /**
+   * Kill the ADB server.  We do this by running ADB again, passing it
+   * the "kill-server" argument.
+   *
+   * @param {Boolean} sync
+   *        Whether or not to kill the server synchronously.  In general,
+   *        this should be false.  But on Windows, an add-on may fail to update
+   *        if its copy of ADB is running when Firefox tries to update it.
+   *        So add-ons who observe their own updates and kill the ADB server
+   *        beforehand should do so synchronously on Windows to make sure
+   *        the update doesn't race the killing.
+   */
+  async kill(sync) {
+    const process = Cc["@mozilla.org/process/util;1"]
+                    .createInstance(Ci.nsIProcess);
+    const adbFile = await this.adbFilePromise;
+    process.init(adbFile);
+    // Hide command prompt window on Windows
+    try {
+      // startHidden is 55+
+      process.startHidden = true;
+      // noShell attribute is 58+
+      process.noShell = true;
+    } catch (e) {
+    }
+    const params = ["kill-server"];
+
+    if (sync) {
+      process.run(true, params, params.length);
+      console.log("adb kill-server: " + process.exitValue);
+      this.ready = false;
+      this.didRunInitially = false;
+    } else {
+      const self = this;
+      process.runAsync(params, params.length, {
+        observe(subject, topic, data) {
+          switch (topic) {
+            case "process-finished":
+              console.log("adb kill-server: " + process.exitValue);
+              Services.obs.notifyObservers(null, "adb-killed");
+              self.ready = false;
+              self.didRunInitially = false;
+              break;
+            case "process-failed":
+              console.log("adb kill-server failure: " + process.exitValue);
+              // It's hard to say whether or not ADB is ready at this point,
+              // but it seems safer to assume that it isn't, so code that wants
+              // to use it later will try to restart it.
+              Services.obs.notifyObservers(null, "adb-killed");
+              self.ready = false;
+              self.didRunInitially = false;
+              break;
+          }
+        }
+      }, false);
+    }
+  },
+
+  // Start tracking devices connecting and disconnecting from the host.
+  // We can't reuse runCommand here because we keep the socket alive.
+  // @return The socket used.
+  trackDevices() {
+    console.log("trackDevices");
+    const socket = client.connect();
+    let waitForFirst = true;
+    const devices = {};
+
+    socket.s.onopen = function() {
+      console.log("trackDevices onopen");
+      Services.obs.notifyObservers(null, "adb-track-devices-start");
+      const req = client.createRequest("host:track-devices");
+      socket.send(req);
+    };
+
+    socket.s.onerror = function(event) {
+      console.log("trackDevices onerror: " + event);
+      Services.obs.notifyObservers(null, "adb-track-devices-stop");
+    };
+
+    socket.s.onclose = function() {
+      console.log("trackDevices onclose");
+
+      // Report all devices as disconnected
+      for (const dev in devices) {
+        devices[dev] = false;
+        EventEmitter.emit(ADB, "device-disconnected", dev);
+      }
+
+      Services.obs.notifyObservers(null, "adb-track-devices-stop");
+
+      // When we lose connection to the server,
+      // and the adb is still on, we most likely got our server killed
+      // by local adb. So we do try to reconnect to it.
+      setTimeout(function() { // Give some time to the new adb to start
+        if (ADB.ready) { // Only try to reconnect/restart if the add-on is still enabled
+          ADB.start().then(function() { // try to connect to the new local adb server
+                                         // or, spawn a new one
+            ADB.trackDevices(); // Re-track devices
+          });
+        }
+      }, 2000);
+    };
+
+    socket.s.ondata = function(event) {
+      console.log("trackDevices ondata");
+      const data = event.data;
+      console.log("length=" + data.byteLength);
+      const dec = new TextDecoder();
+      console.log(dec.decode(new Uint8Array(data)).trim());
+
+      // check the OKAY or FAIL on first packet.
+      if (waitForFirst) {
+        if (!client.checkResponse(data, OKAY)) {
+          socket.close();
+          return;
+        }
+      }
+
+      const packet = client.unpackPacket(data, !waitForFirst);
+      waitForFirst = false;
+
+      if (packet.data == "") {
+        // All devices got disconnected.
+        for (const dev in devices) {
+          devices[dev] = false;
+          EventEmitter.emit(ADB, "device-disconnected", dev);
+        }
+      } else {
+        // One line per device, each line being $DEVICE\t(offline|device)
+        const lines = packet.data.split("\n");
+        const newDev = {};
+        lines.forEach(function(line) {
+          if (line.length == 0) {
+            return;
+          }
+
+          const [dev, status] = line.split("\t");
+          newDev[dev] = status !== "offline";
+        });
+        // Check which device changed state.
+        for (const dev in newDev) {
+          if (devices[dev] != newDev[dev]) {
+            if (dev in devices || newDev[dev]) {
+              const topic = newDev[dev] ? "device-connected"
+                                        : "device-disconnected";
+              EventEmitter.emit(ADB, topic, dev);
+            }
+            devices[dev] = newDev[dev];
+          }
+        }
+      }
+    };
+  },
+
+  // Sends back an array of device names.
+  listDevices() {
+    console.log("listDevices");
+
+    return this.runCommand("host:devices").then(
+      function onSuccess(data) {
+        const lines = data.split("\n");
+        const res = [];
+        lines.forEach(function(line) {
+          if (line.length == 0) {
+            return;
+          }
+          const [ device ] = line.split("\t");
+          res.push(device);
+        });
+        return res;
+      }
+    );
+  },
+
+  // sends adb forward localPort devicePort
+  forwardPort(localPort, devicePort) {
+    console.log("forwardPort " + localPort + " -- " + devicePort);
+    // <host-prefix>:forward:<local>;<remote>
+
+    return this.runCommand("host:forward:" + localPort + ";" + devicePort)
+               .then(function onSuccess(data) {
+                 return data;
+               });
+  },
+
+  // pulls a file from the device.
+  // send "host:transport-any" why??
+  // if !OKAY, return
+  // send "sync:"
+  // if !OKAY, return
+  // send STAT + hex4(path.length) + path
+  // recv STAT + 12 bytes (3 x 32 bits: mode, size, time)
+  // send RECV + hex4(path.length) + path
+  // while(needs data):
+  //   recv DATA + hex4 + data
+  // recv DONE + hex4(0)
+  // send QUIT + hex4(0)
+  pull(from, dest) {
+    const deferred = PromiseUtils.defer();
+    let state;
+    let fileData = null;
+    let currentPos = 0;
+    let chunkSize = 0;
+    let pkgData;
+    const headerArray = new Uint32Array(2);
+    let currentHeaderLength = 0;
+
+    const encoder = new TextEncoder();
+    let infoLengthPacket;
+
+    console.log("pulling " + from + " -> " + dest);
+
+    const shutdown = function() {
+      console.log("pull shutdown");
+      socket.close();
+      deferred.reject("BAD_RESPONSE");
+    };
+
+    // extract chunk data header info. to headerArray.
+    const extractChunkDataHeader = function(data) {
+      const tmpArray = new Uint8Array(headerArray.buffer);
+      for (let i = 0; i < 8 - currentHeaderLength; i++) {
+        tmpArray[currentHeaderLength + i] = data[i];
+      }
+    };
+
+    // chunk data header is 8 bytes length,
+    // the first 4 bytes: hex4("DATA"), and
+    // the second 4 bytes: hex4(chunk size)
+    const checkChunkDataHeader = function(data) {
+      if (data.length + currentHeaderLength >= 8) {
+        extractChunkDataHeader(data);
+
+        if (headerArray[0] != DATA) {
+          shutdown();
+          return false;
+        }
+        // remove header info. from socket package data
+        pkgData = data.subarray(8 - currentHeaderLength, data.length);
+        chunkSize = headerArray[1];
+        currentHeaderLength = 0;
+        return true;
+      }
+
+      // If chunk data header info. is separated into more than one
+      // socket package, keep partial header info. in headerArray.
+      const tmpArray = new Uint8Array(headerArray.buffer);
+      for (let i = 0; i < data.length; i++) {
+        tmpArray[currentHeaderLength + i] = data[i];
+      }
+      currentHeaderLength += data.length;
+      return true;
+    };
+
+    // The last remaining package data contains 8 bytes,
+    // they are "DONE(0x454e4f44)" and 0x0000.
+    const checkDone = function(data) {
+      if (data.length != 8) {
+        return false;
+      }
+
+      const doneFlagArray = new Uint32Array(1);
+      const tmpArray = new Uint8Array(doneFlagArray.buffer);
+      for (let i = 0; i < 4; i++) {
+        tmpArray[i] = data[i];
+      }
+      // Check DONE flag
+      if (doneFlagArray[0] == DONE) {
+        return true;
+      }
+      return false;
+    };
+
+    const runFSM = function runFSM(data) {
+      console.log("runFSM " + state);
+      let req;
+      switch (state) {
+        case "start":
+          state = "send-transport";
+          runFSM();
+          break;
+        case "send-transport":
+          req = client.createRequest("host:transport-any");
+          socket.send(req);
+          state = "wait-transport";
+          break;
+        case "wait-transport":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          console.log("transport: OK");
+          state = "send-sync";
+          runFSM();
+          break;
+        case "send-sync":
+          req = client.createRequest("sync:");
+          socket.send(req);
+          state = "wait-sync";
+          break;
+        case "wait-sync":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          console.log("sync: OK");
+          state = "send-recv";
+          runFSM();
+          break;
+        case "send-recv":
+          infoLengthPacket = new Uint32Array(1);
+          infoLengthPacket[0] = from.length;
+          socket.send(encoder.encode("RECV"));
+          socket.send(infoLengthPacket);
+          socket.send(encoder.encode(from));
+
+          state = "wait-recv";
+          break;
+        case "wait-recv":
+          // After sending "RECV" command, adb server will send chunks data back,
+          // Handle every single socket package here.
+          // Note: One socket package maybe contain many chunks, and often
+          // partial chunk at the end.
+          pkgData = new Uint8Array(client.getBuffer(data));
+
+          // Handle all data in a single socket package.
+          while (pkgData.length > 0) {
+            if (chunkSize == 0 && checkDone(pkgData)) {
+              OS.File.writeAtomic(dest, fileData, {}).then(
+                function onSuccess(number) {
+                  console.log(number);
+                  deferred.resolve("SUCCESS");
+                },
+                function onFailure(reason) {
+                  console.log(reason);
+                  deferred.reject("CANT_ACCESS_FILE");
+                }
+              );
+
+              state = "send-quit";
+              runFSM();
+              return;
+            }
+            if (chunkSize == 0 && !checkChunkDataHeader(pkgData)) {
+              shutdown();
+              return;
+            }
+            // handle full chunk
+            if (chunkSize > 0 && pkgData.length >= chunkSize) {
+              const chunkData = pkgData.subarray(0, chunkSize);
+              const tmpData = new Uint8Array(currentPos + chunkSize);
+              if (fileData) {
+                tmpData.set(fileData, 0);
+              }
+              tmpData.set(chunkData, currentPos);
+              fileData = tmpData;
+              pkgData = pkgData.subarray(chunkSize, pkgData.length);
+              currentPos += chunkSize;
+              chunkSize = 0;
+            }
+            // handle partial chunk at the end of socket package
+            if (chunkSize > 0 && pkgData.length > 0 && pkgData.length < chunkSize) {
+              const tmpData = new Uint8Array(currentPos + pkgData.length);
+              if (fileData) {
+                tmpData.set(fileData, 0);
+              }
+              tmpData.set(pkgData, currentPos);
+              fileData = tmpData;
+              currentPos += pkgData.length;
+              chunkSize -= pkgData.length;
+              break; // Break while loop.
+            }
+          }
+
+          break;
+        case "send-quit":
+          infoLengthPacket = new Uint32Array(1);
+          infoLengthPacket[0] = 0;
+          socket.send(encoder.encode("QUIT"));
+          socket.send(infoLengthPacket);
+
+          state = "end";
+          runFSM();
+          break;
+        case "end":
+          socket.close();
+          break;
+        default:
+          console.log("pull Unexpected State: " + state);
+          deferred.reject("UNEXPECTED_STATE");
+      }
+    };
+
+    const setupSocket = function() {
+      socket.s.onerror = function(event) {
+        console.log("pull onerror");
+        deferred.reject("SOCKET_ERROR");
+      };
+
+      socket.s.onopen = function(event) {
+        console.log("pull onopen");
+        state = "start";
+        runFSM();
+      };
+
+      socket.s.onclose = function(event) {
+        console.log("pull onclose");
+      };
+
+      socket.s.ondata = function(event) {
+        console.log("pull ondata:");
+        runFSM(event.data);
+      };
+    };
+
+    const socket = client.connect();
+    setupSocket();
+
+    return deferred.promise;
+  },
+
+  // pushes a file to the device.
+  // from and dest are full paths.
+  // XXX we should STAT the remote path before sending.
+  push(from, dest) {
+    const deferred = PromiseUtils.defer();
+    let socket;
+    let state;
+    let fileSize;
+    let fileData;
+    let remaining;
+    let currentPos = 0;
+    let fileTime;
+
+    console.log("pushing " + from + " -> " + dest);
+
+    const shutdown = function() {
+      console.log("push shutdown");
+      socket.close();
+      deferred.reject("BAD_RESPONSE");
+    };
+
+    const runFSM = function runFSM(data) {
+      console.log("runFSM " + state);
+      let req;
+      switch (state) {
+        case "start":
+          state = "send-transport";
+          runFSM();
+          break;
+        case "send-transport":
+          req = client.createRequest("host:transport-any");
+          socket.send(req);
+          state = "wait-transport";
+          break;
+        case "wait-transport":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          console.log("transport: OK");
+          state = "send-sync";
+          runFSM();
+          break;
+        case "send-sync":
+          req = client.createRequest("sync:");
+          socket.send(req);
+          state = "wait-sync";
+          break;
+        case "wait-sync":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          console.log("sync: OK");
+          state = "send-send";
+          runFSM();
+          break;
+        case "send-send":
+          // need to send SEND + length($dest,$fileMode)
+          // $fileMode is not the octal one there.
+          const encoder = new TextEncoder();
+
+          const infoLengthPacket = new Uint32Array(1), info = dest + ",33204";
+          infoLengthPacket[0] = info.length;
+          socket.send(encoder.encode("SEND"));
+          socket.send(infoLengthPacket);
+          socket.send(encoder.encode(info));
+
+          // now sending file data.
+          while (remaining > 0) {
+            const toSend = remaining > 65536 ? 65536 : remaining;
+            console.log("Sending " + toSend + " bytes");
+
+            const dataLengthPacket = new Uint32Array(1);
+            // We have to create a new ArrayBuffer for the fileData slice
+            // because nsIDOMTCPSocket (or ArrayBufferInputStream) chokes on
+            // reused buffers, even when we don't modify their contents.
+            const dataPacket = new Uint8Array(new ArrayBuffer(toSend));
+            dataPacket.set(new Uint8Array(fileData.buffer, currentPos, toSend));
+            dataLengthPacket[0] = toSend;
+            socket.send(encoder.encode("DATA"));
+            socket.send(dataLengthPacket);
+            socket.send(dataPacket);
+
+            currentPos += toSend;
+            remaining -= toSend;
+          }
+
+          // Ending up with DONE + mtime (wtf???)
+          const fileTimePacket = new Uint32Array(1);
+          fileTimePacket[0] = fileTime;
+          socket.send(encoder.encode("DONE"));
+          socket.send(fileTimePacket);
+
+          state = "wait-done";
+          break;
+        case "wait-done":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          console.log("DONE: OK");
+          state = "end";
+          runFSM();
+          break;
+        case "end":
+          socket.close();
+          deferred.resolve("SUCCESS");
+          break;
+        default:
+          console.log("push Unexpected State: " + state);
+          deferred.reject("UNEXPECTED_STATE");
+      }
+    };
+
+    const setupSocket = function() {
+      socket.s.onerror = function(event) {
+        console.log("push onerror");
+        deferred.reject("SOCKET_ERROR");
+      };
+
+      socket.s.onopen = function(event) {
+        console.log("push onopen");
+        state = "start";
+        runFSM();
+      };
+
+      socket.s.onclose = function(event) {
+        console.log("push onclose");
+      };
+
+      socket.s.ondata = function(event) {
+        console.log("push ondata");
+        runFSM(event.data);
+      };
+    };
+    // Stat the file, get its size.
+    OS.File.stat(from).then(
+      function onSuccess(stat) {
+        if (stat.isDir) {
+          // The path represents a directory
+          deferred.reject("CANT_PUSH_DIR");
+        } else {
+          // The path represents a file, not a directory
+          fileSize = stat.size;
+          // We want seconds since epoch
+          fileTime = stat.lastModificationDate.getTime() / 1000;
+          remaining = fileSize;
+          console.log(from + " size is " + fileSize);
+          const readPromise = OS.File.read(from);
+          readPromise.then(
+            function readSuccess(data) {
+              fileData = data;
+              socket = client.connect();
+              setupSocket();
+            },
+            function readError() {
+              deferred.reject("READ_FAILED");
+            }
+          );
+        }
+      },
+      function onFailure(reason) {
+        console.log(reason);
+        deferred.reject("CANT_ACCESS_FILE");
+      }
+    );
+
+    return deferred.promise;
+  },
+
+  // Run a shell command
+  shell(command) {
+    const deferred = PromiseUtils.defer();
+    let state;
+    let stdout = "";
+
+    console.log("shell " + command);
+
+    const shutdown = function() {
+      console.log("shell shutdown");
+      socket.close();
+      deferred.reject("BAD_RESPONSE");
+    };
+
+    const runFSM = function runFSM(data) {
+      console.log("runFSM " + state);
+      let req;
+      let ignoreResponseCode = false;
+      switch (state) {
+        case "start":
+          state = "send-transport";
+          runFSM();
+          break;
+        case "send-transport":
+          req = client.createRequest("host:transport-any");
+          socket.send(req);
+          state = "wait-transport";
+          break;
+        case "wait-transport":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          state = "send-shell";
+          runFSM();
+          break;
+        case "send-shell":
+          req = client.createRequest("shell:" + command);
+          socket.send(req);
+          state = "rec-shell";
+          break;
+        case "rec-shell":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          state = "decode-shell";
+          if (client.getBuffer(data).byteLength == 4) {
+            break;
+          }
+          ignoreResponseCode = true;
+          // eslint-disable-next-lined no-fallthrough
+        case "decode-shell":
+          const decoder = new TextDecoder();
+          const text = new Uint8Array(client.getBuffer(data),
+                                      ignoreResponseCode ? 4 : 0);
+          stdout += decoder.decode(text);
+          break;
+        default:
+          console.log("shell Unexpected State: " + state);
+          deferred.reject("UNEXPECTED_STATE");
+      }
+    };
+
+    const socket = client.connect();
+    socket.s.onerror = function(event) {
+      console.log("shell onerror");
+      deferred.reject("SOCKET_ERROR");
+    };
+
+    socket.s.onopen = function(event) {
+      console.log("shell onopen");
+      state = "start";
+      runFSM();
+    };
+
+    socket.s.onclose = function(event) {
+      deferred.resolve(stdout);
+      console.log("shell onclose");
+    };
+
+    socket.s.ondata = function(event) {
+      console.log("shell ondata");
+      runFSM(event.data);
+    };
+
+    return deferred.promise;
+  },
+
+  reboot() {
+    return this.shell("reboot");
+  },
+
+  rebootRecovery() {
+    return this.shell("reboot recovery");
+  },
+
+  rebootBootloader() {
+    return this.shell("reboot bootloader");
+  },
+
+  root() {
+    const deferred = PromiseUtils.defer();
+    let state;
+
+    console.log("root");
+
+    const shutdown = function() {
+      console.log("root shutdown");
+      socket.close();
+      deferred.reject("BAD_RESPONSE");
+    };
+
+    const runFSM = function runFSM(data) {
+      console.log("runFSM " + state);
+      let req;
+      switch (state) {
+        case "start":
+          state = "send-transport";
+          runFSM();
+          break;
+        case "send-transport":
+          req = client.createRequest("host:transport-any");
+          socket.send(req);
+          state = "wait-transport";
+          break;
+        case "wait-transport":
+          if (!client.checkResponse(data, OKAY)) {
+            shutdown();
+            return;
+          }
+          state = "send-root";
+          runFSM();
+          break;
+        case "send-root":
+          req = client.createRequest("root:");
+          socket.send(req);
+          state = "rec-root";
+          break;
+        case "rec-root":
+          // Nothing to do
+          break;
+        default:
+          console.log("root Unexpected State: " + state);
+          deferred.reject("UNEXPECTED_STATE");
+      }
+    };
+
+    const socket = client.connect();
+    socket.s.onerror = function(event) {
+      console.log("root onerror");
+      deferred.reject("SOCKET_ERROR");
+    };
+
+    socket.s.onopen = function(event) {
+      console.log("root onopen");
+      state = "start";
+      runFSM();
+    };
+
+    socket.s.onclose = function(event) {
+      deferred.resolve();
+      console.log("root onclose");
+    };
+
+    socket.s.ondata = function(event) {
+      console.log("root ondata");
+      runFSM(event.data);
+    };
+
+    return deferred.promise;
+  },
+
+  // Asynchronously runs an adb command.
+  // @param command The command as documented in
+  // http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
+  runCommand(command) {
+    console.log("runCommand " + command);
+    const deferred = PromiseUtils.defer();
+    if (!this.ready) {
+      setTimeout(function() {
+        deferred.reject("ADB_NOT_READY");
+      });
+      return deferred.promise;
+    }
+
+    const socket = client.connect();
+
+    socket.s.onopen = function() {
+      console.log("runCommand onopen");
+      const req = client.createRequest(command);
+      socket.send(req);
+    };
+
+    socket.s.onerror = function() {
+      console.log("runCommand onerror");
+      deferred.reject("NETWORK_ERROR");
+    };
+
+    socket.s.onclose = function() {
+      console.log("runCommand onclose");
+    };
+
+    socket.s.ondata = function(event) {
+      console.log("runCommand ondata");
+      const data = event.data;
+
+      const packet = client.unpackPacket(data, false);
+      if (!client.checkResponse(data, OKAY)) {
+        socket.close();
+        console.log("Error: " + packet.data);
+        deferred.reject("PROTOCOL_ERROR");
+        return;
+      }
+
+      deferred.resolve(packet.data);
+    };
+
+    return deferred.promise;
+  }
+};
+
+exports.ADB = ADB;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/moz.build
@@ -0,0 +1,18 @@
+# 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/.
+
+DevToolsModules(
+    'adb-binary.js',
+    'adb-client.js',
+    'adb-device.js',
+    'adb-running-checker.js',
+    'adb-scanner.js',
+    'adb-socket.js',
+    'adb.js',
+)
+
+with Files('**'):
+    BUG_COMPONENT = ('DevTools', 'about:debugging')
+
+XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/test/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+  "extends": "../../../.eslintrc.xpcshell.js",
+};
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/test/adb.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env 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/.
+
+"""
+A fake ADB binary
+"""
+
+from __future__ import absolute_import
+
+import os
+import socket
+import SocketServer
+import sys
+import thread
+
+HOST = '127.0.0.1'
+PORT = 5037
+
+class ADBServer(SocketServer.BaseRequestHandler):
+    def sendData(self, data):
+        header = 'OKAY%04x' % len(data)
+        all_data = header + data
+        total_length = len(all_data)
+        sent_length = 0
+        # Make sure send all data to the client.
+        # Though the data length here is pretty small but sometimes when the
+        # client is on heavy load (e.g. MOZ_CHAOSMODE) we can't send the whole
+        # data at once.
+        while sent_length < total_length:
+            sent = self.request.send(all_data[sent_length:])
+            sent_length = sent_length + sent
+
+    def handle(self):
+        while True:
+            data = self.request.recv(4096)
+            if 'kill-server' in data:
+                def shutdown(server):
+                    server.shutdown()
+                    thread.exit()
+                thread.start_new_thread(shutdown, (server, ))
+                self.request.close()
+                break
+            elif 'host:version' in data:
+                self.sendData('001F')
+                self.request.close()
+                break
+            elif 'host:track-devices' in data:
+                self.sendData('1234567890\tdevice')
+                break
+
+if len(sys.argv) == 2:
+    if sys.argv[1] == 'start-server':
+        # daemonize
+        if os.fork() > 0:
+            sys.exit(0)
+        os.setsid()
+        if os.fork() > 0:
+            sys.exit(0)
+
+        # Create a SocketServer with 'False' for bind_and_activate to set
+        # allow_reuse_address before binding.
+        server = SocketServer.TCPServer((HOST, PORT), ADBServer, False)
+        server.allow_reuse_address = True
+        server.server_bind()
+        server.server_activate()
+        server.serve_forever()
+    elif sys.argv[1] == 'kill-server':
+        sock = socket.socket()
+        sock.connect((HOST, PORT))
+        sock.send('kill-server')
+        sock.shutdown(socket.SHUT_RDWR)
+        sock.close()
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/test/test_adb.js
@@ -0,0 +1,229 @@
+"use strict";
+
+const EventEmitter = require("devtools/shared/event-emitter");
+const { ExtensionTestUtils } = ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm", {});
+const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
+const { getFileForBinary } = require("devtools/shared/adb/adb-binary");
+const { check } = require("devtools/shared/adb/adb-running-checker");
+const { ADB } = require("devtools/shared/adb/adb");
+
+const ADB_JSON = {
+  "Linux": {
+    "x86": [
+      "linux/adb"
+    ],
+    "x86_64": [
+      "linux64/adb"
+    ]
+  },
+  "Darwin": {
+    "x86_64": [
+      "mac64/adb"
+    ]
+  },
+  "WINNT": {
+    "x86": [
+      "win32/adb.exe",
+      "win32/AdbWinApi.dll",
+      "win32/AdbWinUsbApi.dll"
+    ],
+    "x86_64": [
+      "win32/adb.exe",
+      "win32/AdbWinApi.dll",
+      "win32/AdbWinUsbApi.dll"
+    ]
+  }
+};
+
+ExtensionTestUtils.init(this);
+
+function readAdbMockContent() {
+  const adbMockFile = do_get_file("adb.py", false);
+  const s = Cc["@mozilla.org/network/file-input-stream;1"]
+    .createInstance(Ci.nsIFileInputStream);
+  s.init(adbMockFile, -1, -1, false);
+  try {
+    return NetUtil.readInputStreamToString(s, s.available());
+  } finally {
+    s.close();
+  }
+}
+
+const adbMock = readAdbMockContent();
+
+add_task(async function setup() {
+  // Prepare the profile directory where the adb extension will be installed.
+  do_get_profile();
+});
+
+add_task(async function testAdbIsNotRunningInitially() {
+  const isAdbRunning = await check();
+  // Assume that no adb server running.
+  ok(!isAdbRunning, "adb is not running initially");
+});
+
+add_task(async function testNoAdbExtension() {
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      version: "1.0",
+      applications: {
+        gecko: { id: "not-adb@mozilla.org" }
+      }
+    },
+  });
+
+  await extension.startup();
+
+  const adbBinary = await getFileForBinary();
+  equal(adbBinary, null);
+
+  await extension.unload();
+});
+
+add_task(async function testNoAdbJSON() {
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      version: "1.0",
+      applications: {
+        // The extension id here and in later test cases should match the
+        // corresponding prefrece value.
+        gecko: { id: "adb@mozilla.org" }
+      }
+    },
+  });
+
+  await extension.startup();
+
+  const adbBinary = await getFileForBinary();
+  equal(adbBinary, null);
+
+  await extension.unload();
+});
+
+add_task({
+  skip_if: () => mozinfo.os == "win" // bug 1482008
+}, async function testNoTargetBinaries() {
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      version: "1.0",
+      applications: {
+        gecko: { id: "adb@mozilla.org" }
+      }
+    },
+    files: {
+      "adb.json": JSON.stringify(ADB_JSON),
+    },
+  });
+
+  await extension.startup();
+
+  const adbBinary = await getFileForBinary();
+  equal(adbBinary, null);
+
+  await extension.unload();
+});
+
+add_task(async function testExtract() {
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      version: "1.0",
+      applications: {
+        gecko: { id: "adb@mozilla.org" }
+      }
+    },
+    files: {
+      "adb.json": JSON.stringify(ADB_JSON),
+      "linux/adb": "adb",
+      "linux64/adb": "adb",
+      "mac64/adb": "adb",
+      "win32/adb.exe": "adb.exe",
+      "win32/AdbWinApi.dll": "AdbWinApi.dll",
+      "win32/AdbWinUsbApi.dll": "AdbWinUsbApi.dll"
+    },
+  });
+
+  await extension.startup();
+
+  const adbBinary = await getFileForBinary();
+  ok(await adbBinary.exists);
+
+  await extension.unload();
+});
+
+add_task({
+  skip_if: () => mozinfo.os == "win" // bug 1482008
+}, async function testStartAndStop() {
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      version: "1.0",
+      applications: {
+        gecko: { id: "adb@mozilla.org" }
+      }
+    },
+    files: {
+      "adb.json": JSON.stringify(ADB_JSON),
+      "linux/adb": adbMock,
+      "linux64/adb": adbMock,
+      "mac64/adb": adbMock,
+      "win32/adb.exe": adbMock,
+      "win32/AdbWinApi.dll": "dummy",
+      "win32/AdbWinUsbApi.dll": "dummy"
+    },
+  });
+
+  await extension.startup();
+
+  await ADB.start();
+  ok(ADB.ready);
+
+  ok(await check(), "adb is now running");
+
+  await ADB.stop(true /* sync */);
+  ok(!ADB.ready);
+
+  await extension.unload();
+});
+
+add_task({
+  skip_if: () => mozinfo.os == "win" // bug 1482008
+}, async function testTrackDevices() {
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      version: "1.0",
+      applications: {
+        gecko: { id: "adb@mozilla.org" }
+      }
+    },
+    files: {
+      "adb.json": JSON.stringify(ADB_JSON),
+      "linux/adb": adbMock,
+      "linux64/adb": adbMock,
+      "mac64/adb": adbMock,
+      "win32/adb.exe": adbMock,
+      "win32/AdbWinApi.dll": "dummy",
+      "win32/AdbWinUsbApi.dll": "dummy"
+    },
+  });
+
+  await extension.startup();
+
+  await ADB.start();
+  ok(ADB.ready);
+
+  ok(await check(), "adb is now running");
+
+  const receivedDeviceId = await new Promise(resolve => {
+    EventEmitter.on(ADB, "device-connected", deviceId => {
+      resolve(deviceId);
+    });
+    ADB.trackDevices();
+  });
+
+  equal(receivedDeviceId, "1234567890");
+
+  await ADB.stop(true /* sync */);
+  ok(!ADB.ready);
+
+  await extension.unload();
+});
+
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/test/xpcshell-head.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
new file mode 100644
--- /dev/null
+++ b/devtools/shared/adb/test/xpcshell.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+tags = devtools
+head = xpcshell-head.js
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+support-files =
+  adb.py
+
+[test_adb.js]
+run-sequentially = An extension having the same id is installed/uninstalled in different tests
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include('../templates.mozbuild')
 
 DIRS += [
     'acorn',
+    'adb',
     'apps',
     'client',
     'css',
     'discovery',
     'fronts',
     'gcli',
     'heapsnapshot',
     'inspector',
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1469,18 +1469,22 @@ MediaFormatReader::ResolveSetCDMPromiseI
   MOZ_ASSERT(mCDMProxy);
   if (mSetCDMForTracks.contains(aTrack)) {
     mSetCDMForTracks -= aTrack;
   }
 
   if (mSetCDMForTracks.isEmpty()) {
     LOGV("%s : Done ", __func__);
     mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__);
-    ScheduleUpdate(TrackInfo::kAudioTrack);
-    ScheduleUpdate(TrackInfo::kVideoTrack);
+    if (HasAudio()) {
+      ScheduleUpdate(TrackInfo::kAudioTrack);
+    }
+    if (HasVideo()) {
+      ScheduleUpdate(TrackInfo::kVideoTrack);
+    }
     return true;
   }
   LOGV("%s : %s track is ready.", __func__, TrackTypeToStr(aTrack));
   return false;
 }
 
 void
 MediaFormatReader::PrepareToSetCDMForTrack(TrackType aTrack)
@@ -2216,16 +2220,19 @@ MediaFormatReader::NeedInput(DecoderData
 void
 MediaFormatReader::ScheduleUpdate(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   if (mShutdown) {
     return;
   }
   auto& decoder = GetDecoderData(aTrack);
+  MOZ_RELEASE_ASSERT(decoder.GetCurrentInfo(),
+                     "Can only schedule update when track exists");
+
   if (decoder.mUpdateScheduled) {
     return;
   }
   LOGV("SchedulingUpdate(%s)", TrackTypeToStr(aTrack));
   decoder.mUpdateScheduled = true;
   RefPtr<nsIRunnable> task(NewRunnableMethod<TrackType>(
     "MediaFormatReader::Update", this, &MediaFormatReader::Update, aTrack));
   nsresult rv = OwnerThread()->Dispatch(task.forget());
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -872,37 +872,47 @@ EditorBase::CanRedo(bool* aIsEnabled, bo
   *aCanRedo = CanRedo();
   *aIsEnabled = IsUndoRedoEnabled();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::BeginTransaction()
 {
+  BeginTransactionInternal();
+  return NS_OK;
+}
+
+void
+EditorBase::BeginTransactionInternal()
+{
   BeginUpdateViewBatch();
 
   if (mTransactionManager) {
     RefPtr<TransactionManager> transactionManager(mTransactionManager);
     transactionManager->BeginBatch(nullptr);
   }
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::EndTransaction()
 {
+  EndTransactionInternal();
+  return NS_OK;
+}
+
+void
+EditorBase::EndTransactionInternal()
+{
   if (mTransactionManager) {
     RefPtr<TransactionManager> transactionManager(mTransactionManager);
     transactionManager->EndBatch(false);
   }
 
   EndUpdateViewBatch();
-
-  return NS_OK;
 }
 
 void
 EditorBase::BeginPlaceholderTransaction(nsAtom* aTransactionName)
 {
   MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!");
   if (!mPlaceholderBatch) {
     NotifyEditorObservers(eNotifyEditorObserversOfBefore);
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -48,16 +48,17 @@ class nsISupports;
 class nsITransaction;
 class nsITransactionListener;
 class nsIWidget;
 class nsRange;
 
 namespace mozilla {
 class AutoSelectionRestorer;
 class AutoTopLevelEditSubActionNotifier;
+class AutoTransactionBatch;
 class AutoTransactionsConserveSelection;
 class AutoUpdateViewBatch;
 class ChangeAttributeTransaction;
 class CompositionTransaction;
 class CreateElementTransaction;
 class CSSEditUtils;
 class DeleteNodeTransaction;
 class DeleteRangeTransaction;
@@ -1662,16 +1663,26 @@ protected: // Called by helper classes.
    * manager batches.
    */
   void BeginPlaceholderTransaction(nsAtom* aTransactionName);
   void EndPlaceholderTransaction();
 
   void BeginUpdateViewBatch();
   void EndUpdateViewBatch();
 
+  /**
+   * Used by AutoTransactionBatch.  After calling BeginTransactionInternal(),
+   * all transactions will be treated as an atomic transaction.  I.e.,
+   * two or more transactions are undid once.
+   * XXX What's the difference with PlaceholderTransaction? Should we always
+   *     use it instead?
+   */
+  void BeginTransactionInternal();
+  void EndTransactionInternal();
+
 protected: // Shouldn't be used by friend classes
   /**
    * The default destructor. This should suffice. Should this be pure virtual
    * for someone to derive from the EditorBase later? I don't believe so.
    */
   virtual ~EditorBase();
 
   /**
@@ -1985,16 +1996,17 @@ protected:
   // Whether spellchecker dictionary is initialized after focused.
   bool mSpellCheckerDictionaryUpdated;
   // Whether we are an HTML editor class.
   bool mIsHTMLEditorClass;
 
   friend class AutoPlaceholderBatch;
   friend class AutoSelectionRestorer;
   friend class AutoTopLevelEditSubActionNotifier;
+  friend class AutoTransactionBatch;
   friend class AutoTransactionsConserveSelection;
   friend class AutoUpdateViewBatch;
   friend class CompositionTransaction;
   friend class CreateElementTransaction;
   friend class CSSEditUtils;
   friend class DeleteNodeTransaction;
   friend class DeleteRangeTransaction;
   friend class DeleteTextTransaction;
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -447,16 +447,41 @@ private:
   nsCOMPtr<nsIContent> mRightContent;
 
   nsresult mRv;
 
   SplitRangeOffFromNodeResult() = delete;
 };
 
 /***************************************************************************
+ * stack based helper class for calling EditorBase::EndTransaction() after
+ * EditorBase::BeginTransaction().
+ ***************************************************************************/
+class MOZ_RAII AutoTransactionBatch final
+{
+private:
+  OwningNonNull<EditorBase> mEditorBase;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+public:
+  explicit AutoTransactionBatch(EditorBase& aEditorBase
+                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    : mEditorBase(aEditorBase)
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    mEditorBase->BeginTransactionInternal();
+  }
+
+  ~AutoTransactionBatch()
+  {
+    mEditorBase->EndTransactionInternal();
+  }
+};
+
+/***************************************************************************
  * stack based helper class for batching a collection of transactions inside a
  * placeholder transaction.
  */
 class MOZ_RAII AutoPlaceholderBatch final
 {
 private:
   RefPtr<EditorBase> mEditorBase;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -783,16 +783,26 @@ protected: // Shouldn't be used by frien
 
   /**
    * InsertBrElementAtSelectionWithTransaction() inserts a new <br> element at
    * selection.  If there is non-collapsed selection ranges, the selected
    * ranges is deleted first.
    */
   nsresult InsertBrElementAtSelectionWithTransaction();
 
+  /**
+   * InsertTextWithQuotationsInternal() replaces selection with new content.
+   * First, this method splits aStringToInsert to multiple chunks which start
+   * with non-linebreaker except first chunk and end with a linebreaker except
+   * last chunk.  Then, each chunk starting with ">" is inserted after wrapping
+   * with <span _moz_quote="true">, and each chunk not starting with ">" is
+   * inserted as normal text.
+   */
+  nsresult InsertTextWithQuotationsInternal(const nsAString& aStringToInsert);
+
   nsresult LoadHTML(const nsAString& aInputString);
 
   /**
    * ReplaceHeadContentsWithSourceWithTransaction() replaces all children of
    * <head> element with given source code.  This is undoable.
    *
    * @param aSourceToInsert     HTML source fragment to replace the children
    *                            of <head> element.
--- a/editor/libeditor/HTMLEditorCommands.cpp
+++ b/editor/libeditor/HTMLEditorCommands.cpp
@@ -240,16 +240,19 @@ StyleUpdatingCommand::ToggleState(HTMLEd
     doTagRemoval = params->GetBool(STATE_ALL, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
   if (doTagRemoval) {
     // Also remove equivalent properties (bug 317093)
+    // XXX Why don't we make the following two transactions as an atomic
+    //     transaction?  If the element is <b>, <i> or <strike>, user
+    //     needs to undo twice.
     if (mTagName == nsGkAtoms::b) {
       nsresult rv =
         aHTMLEditor->RemoveInlineProperty(nsGkAtoms::strong, nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else if (mTagName == nsGkAtoms::i) {
       nsresult rv =
@@ -267,28 +270,31 @@ StyleUpdatingCommand::ToggleState(HTMLEd
 
     nsresult rv = aHTMLEditor->RemoveInlineProperty(mTagName, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  // Superscript and Subscript styles are mutually exclusive
-  aHTMLEditor->BeginTransaction();
+  // Superscript and Subscript styles are mutually exclusive.
+  AutoTransactionBatch bundleAllTransactions(*aHTMLEditor);
 
-  nsresult rv = NS_OK;
   if (mTagName == nsGkAtoms::sub || mTagName == nsGkAtoms::sup) {
-    rv = aHTMLEditor->RemoveInlineProperty(mTagName, nullptr);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    rv = aHTMLEditor->SetInlineProperty(mTagName, nullptr, EmptyString());
+    nsresult rv = aHTMLEditor->RemoveInlineProperty(mTagName, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
-  aHTMLEditor->EndTransaction();
+  nsresult rv =
+    aHTMLEditor->SetInlineProperty(mTagName, nullptr, EmptyString());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   return rv;
 }
 
 ListCommand::ListCommand(nsAtom* aTagName)
   : StateUpdatingCommandBase(aTagName)
 {
 }
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -1675,19 +1675,30 @@ HTMLEditor::PasteAsPlaintextQuotation(in
 
   return rv;
 }
 
 NS_IMETHODIMP
 HTMLEditor::InsertTextWithQuotations(const nsAString& aStringToInsert)
 {
   // The whole operation should be undoable in one transaction:
-  BeginTransaction();
+  // XXX Why isn't enough to use only AutoPlaceholderBatch here?
+  AutoTransactionBatch bundleAllTransactions(*this);
   AutoPlaceholderBatch beginBatching(this);
 
+  nsresult rv = InsertTextWithQuotationsInternal(aStringToInsert);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+HTMLEditor::InsertTextWithQuotationsInternal(const nsAString& aStringToInsert)
+{
   // We're going to loop over the string, collecting up a "hunk"
   // that's all the same type (quoted or not),
   // Whenever the quotedness changes (or we reach the string's end)
   // we will insert the hunk all at once, quoted or non.
 
   static const char16_t cite('>');
   bool curHunkIsQuoted = (aStringToInsert.First() == cite);
 
@@ -1762,18 +1773,16 @@ HTMLEditor::InsertTextWithQuotations(con
     }
     if (!found) {
       break;
     }
     curHunkIsQuoted = quoted;
     hunkStart = lineStart;
   }
 
-  EndTransaction();
-
   return rv;
 }
 
 NS_IMETHODIMP
 HTMLEditor::InsertAsQuotation(const nsAString& aQuotedText,
                               nsINode** aNodeInserted)
 {
   AutoPlaceholderBatch beginBatching(this);
@@ -1914,17 +1923,26 @@ HTMLEditor::Rewrap(bool aRespectNewlines
     return rv;
   }
 
   if (isCollapsed) {
     DebugOnly<nsresult> rv = SelectAllInternal();
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),  "Failed to select all text");
   }
 
-  return InsertTextWithQuotations(wrapped);
+  // The whole operation in InsertTextWithQuotationsInternal() should be
+  // undoable in one transaction.
+  // XXX Why isn't enough to use only AutoPlaceholderBatch here?
+  AutoTransactionBatch bundleAllTransactions(*this);
+  AutoPlaceholderBatch beginBatching(this);
+  rv = InsertTextWithQuotationsInternal(wrapped);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
                                    const nsAString& aCitation,
                                    bool aInsertHTML,
                                    nsINode** aNodeInserted)
 {
--- a/editor/spellchecker/TextServicesDocument.cpp
+++ b/editor/spellchecker/TextServicesDocument.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "TextServicesDocument.h"
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
+#include "mozilla/EditorUtils.h"        // for AutoTransactionBatch
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/TextEditor.h"         // for TextEditor
 #include "nsAString.h"                  // for nsAString::Length, etc
 #include "nsContentUtils.h"             // for nsContentUtils
 #include "nsComposeTxtSrvFilter.h"
 #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
@@ -18,28 +19,24 @@
 #include "nsError.h"                    // for NS_OK, NS_ERROR_FAILURE, etc
 #include "nsFilteredContentIterator.h"  // for nsFilteredContentIterator
 #include "nsGenericHTMLElement.h"       // for nsGenericHTMLElement
 #include "nsIContent.h"                 // for nsIContent, etc
 #include "nsIContentIterator.h"         // for nsIContentIterator
 #include "nsID.h"                       // for NS_GET_IID
 #include "nsIEditor.h"                  // for nsIEditor, etc
 #include "nsINode.h"                    // for nsINode
-#include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor
 #include "nsISelectionController.h"     // for nsISelectionController, etc
 #include "nsISupportsBase.h"            // for nsISupports
 #include "nsISupportsUtils.h"           // for NS_IF_ADDREF, NS_ADDREF, etc
 #include "mozilla/intl/WordBreaker.h"   // for WordRange, WordBreaker
 #include "nsRange.h"                    // for nsRange
 #include "nsString.h"                   // for nsString, nsAutoString
 #include "nscore.h"                     // for nsresult, NS_IMETHODIMP, etc
 
-#define LOCK_DOC(doc)
-#define UNLOCK_DOC(doc)
-
 namespace mozilla {
 
 using namespace dom;
 
 class OffsetEntry final
 {
 public:
   OffsetEntry(nsINode* aNode,
@@ -107,106 +104,92 @@ NS_IMPL_CYCLE_COLLECTION(TextServicesDoc
 
 nsresult
 TextServicesDocument::InitWithEditor(nsIEditor* aEditor)
 {
   nsCOMPtr<nsISelectionController> selCon;
 
   NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
 
-  LOCK_DOC(this);
-
   // Check to see if we already have an mSelCon. If we do, it
   // better be the same one the editor uses!
 
   nsresult rv = aEditor->GetSelectionController(getter_AddRefs(selCon));
 
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
     return rv;
   }
 
   if (!selCon || (mSelCon && selCon != mSelCon)) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   if (!mSelCon) {
     mSelCon = selCon;
   }
 
   // Check to see if we already have an mDocument. If we do, it
   // better be the same one the editor uses!
 
   nsCOMPtr<nsIDocument> doc = aEditor->AsEditorBase()->GetDocument();
   if (!doc || (mDocument && doc != mDocument)) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   if (!mDocument) {
     mDocument = doc;
 
     rv = CreateDocumentContentIterator(getter_AddRefs(mIterator));
 
     if (NS_FAILED(rv)) {
-      UNLOCK_DOC(this);
       return rv;
     }
 
     mIteratorStatus = IteratorStatus::eDone;
 
     rv = FirstBlock();
 
     if (NS_FAILED(rv)) {
-      UNLOCK_DOC(this);
       return rv;
     }
   }
 
   mTextEditor = aEditor->AsTextEditor();
 
   rv = aEditor->AddEditActionListener(this);
 
-  UNLOCK_DOC(this);
-
   return rv;
 }
 
 nsresult
 TextServicesDocument::SetExtent(nsRange* aRange)
 {
   NS_ENSURE_ARG_POINTER(aRange);
   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   // We need to store a copy of aDOMRange since we don't
   // know where it came from.
 
   mExtent = aRange->CloneRange();
 
   // Create a new iterator based on our new extent range.
 
   nsresult rv = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
 
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
     return rv;
   }
 
   // Now position the iterator at the start of the first block
   // in the range.
 
   mIteratorStatus = IteratorStatus::eDone;
 
   rv = FirstBlock();
 
-  UNLOCK_DOC(this);
-
   return rv;
 }
 
 nsresult
 TextServicesDocument::ExpandRangeToWordBoundaries(nsRange* aRange)
 {
   NS_ENSURE_ARG_POINTER(aRange);
 
@@ -371,430 +354,378 @@ nsresult
 TextServicesDocument::GetCurrentTextBlock(nsString *aStr)
 {
   NS_ENSURE_TRUE(aStr, NS_ERROR_NULL_POINTER);
 
   aStr->Truncate();
 
   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   nsresult rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
                                   mExtent, aStr);
-
-  UNLOCK_DOC(this);
-
-  return rv;
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 nsresult
 TextServicesDocument::FirstBlock()
 {
   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   nsresult rv = FirstTextNode(mIterator, &mIteratorStatus);
 
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
     return rv;
   }
 
   // Keep track of prev and next blocks, just in case
   // the text service blows away the current block.
 
   if (mIteratorStatus == IteratorStatus::eValid) {
     mPrevTextBlock  = nullptr;
     rv = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
   } else {
     // There's no text block in the document!
 
     mPrevTextBlock  = nullptr;
     mNextTextBlock  = nullptr;
   }
 
-  UNLOCK_DOC(this);
-
   // XXX Result of FirstTextNode() or GetFirstTextNodeInNextBlock().
   return rv;
 }
 
 nsresult
 TextServicesDocument::LastSelectedBlock(BlockSelectionStatus* aSelStatus,
                                         int32_t* aSelOffset,
                                         int32_t* aSelLength)
 {
   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
 
-  LOCK_DOC(this);
-
   mIteratorStatus = IteratorStatus::eDone;
 
   *aSelStatus = BlockSelectionStatus::eBlockNotFound;
   *aSelOffset = *aSelLength = -1;
 
   if (!mSelCon || !mIterator) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<Selection> selection =
     mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
   if (NS_WARN_IF(!selection)) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIContentIterator> iter;
   RefPtr<nsRange> range;
   nsCOMPtr<nsINode> parent;
 
   if (selection->IsCollapsed()) {
     // We have a caret. Check if the caret is in a text node.
     // If it is, make the text node's block the current block.
     // If the caret isn't in a text node, search forwards in
     // the document, till we find a text node.
 
     range = selection->GetRangeAt(0);
 
     if (!range) {
-      UNLOCK_DOC(this);
       return NS_ERROR_FAILURE;
     }
 
     parent = range->GetStartContainer();
     if (!parent) {
-      UNLOCK_DOC(this);
       return NS_ERROR_FAILURE;
     }
 
     nsresult rv;
     if (parent->IsText()) {
       // The caret is in a text node. Find the beginning
       // of the text block containing this text node and
       // return.
 
       rv = mIterator->PositionAt(parent);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       rv = FirstTextNodeInCurrentBlock(mIterator);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       mIteratorStatus = IteratorStatus::eValid;
 
       rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
                              mExtent, nullptr);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       if (*aSelStatus == BlockSelectionStatus::eBlockContains) {
         rv = SetSelectionInternal(*aSelOffset, *aSelLength, false);
       }
     } else {
       // The caret isn't in a text node. Create an iterator
       // based on a range that extends from the current caret
       // position to the end of the document, then walk forwards
       // till you find a text node, then find the beginning of it's block.
 
       range = CreateDocumentContentRootToNodeOffsetRange(
                 parent, range->StartOffset(), false);
 
       if (NS_WARN_IF(!range)) {
-        UNLOCK_DOC(this);
         return NS_ERROR_FAILURE;
       }
 
       if (range->Collapsed()) {
         // If we get here, the range is collapsed because there is nothing after
         // the caret! Just return NS_OK;
-
-        UNLOCK_DOC(this);
         return NS_OK;
       }
 
       rv = CreateContentIterator(range, getter_AddRefs(iter));
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       iter->First();
 
       nsIContent* content = nullptr;
       while (!iter->IsDone()) {
         nsINode* currentNode = iter->GetCurrentNode();
         if (currentNode->IsText()) {
           content = currentNode->AsContent();
           break;
         }
         iter->Next();
       }
 
       if (!content) {
-        UNLOCK_DOC(this);
         return NS_OK;
       }
 
       rv = mIterator->PositionAt(content);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       rv = FirstTextNodeInCurrentBlock(mIterator);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       mIteratorStatus = IteratorStatus::eValid;
 
       rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
                              mExtent, nullptr);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
     }
 
-    UNLOCK_DOC(this);
-
     // Result of SetSelectionInternal() in the |if| block or NS_OK.
     return rv;
   }
 
   // If we get here, we have an uncollapsed selection!
   // Look backwards through each range in the selection till you
   // find the first text node. If you find one, find the
   // beginning of its text block, and make it the current
   // block.
 
   int32_t rangeCount = static_cast<int32_t>(selection->RangeCount());
   NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
 
   if (rangeCount <= 0) {
-    UNLOCK_DOC(this);
     return NS_OK;
   }
 
   // XXX: We may need to add some code here to make sure
   //      the ranges are sorted in document appearance order!
 
   for (int32_t i = rangeCount - 1; i >= 0; i--) {
     // Get the i'th range from the selection.
 
     range = selection->GetRangeAt(i);
 
     if (!range) {
-      UNLOCK_DOC(this);
       return NS_OK; // XXX Really?
     }
 
     // Create an iterator for the range.
 
     nsresult rv = CreateContentIterator(range, getter_AddRefs(iter));
 
     if (NS_FAILED(rv)) {
-      UNLOCK_DOC(this);
       return rv;
     }
 
     iter->Last();
 
     // Now walk through the range till we find a text node.
 
     while (!iter->IsDone()) {
       if (iter->GetCurrentNode()->NodeType() == nsINode::TEXT_NODE) {
         // We found a text node, so position the document's
         // iterator at the beginning of the block, then get
         // the selection in terms of the string offset.
 
         rv = mIterator->PositionAt(iter->GetCurrentNode());
 
         if (NS_FAILED(rv)) {
-          UNLOCK_DOC(this);
           return rv;
         }
 
         rv = FirstTextNodeInCurrentBlock(mIterator);
 
         if (NS_FAILED(rv)) {
-          UNLOCK_DOC(this);
           return rv;
         }
 
         mIteratorStatus = IteratorStatus::eValid;
 
         rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
                                mExtent, nullptr);
 
         if (NS_FAILED(rv)) {
-          UNLOCK_DOC(this);
           return rv;
         }
 
         rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
 
-        UNLOCK_DOC(this);
-
         return rv;
 
       }
 
       iter->Prev();
     }
   }
 
   // If we get here, we didn't find any text node in the selection!
   // Create a range that extends from the end of the selection,
   // to the end of the document, then iterate forwards through
   // it till you find a text node!
 
   range = selection->GetRangeAt(rangeCount - 1);
 
   if (!range) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   parent = range->GetEndContainer();
   if (!parent) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   range = CreateDocumentContentRootToNodeOffsetRange(
             parent, range->EndOffset(), false);
 
   if (NS_WARN_IF(!range)) {
-    UNLOCK_DOC(this);
     return NS_ERROR_FAILURE;
   }
 
   if (range->Collapsed()) {
     // If we get here, the range is collapsed because there is nothing after
     // the current selection! Just return NS_OK;
-
-    UNLOCK_DOC(this);
     return NS_OK;
   }
 
   nsresult rv = CreateContentIterator(range, getter_AddRefs(iter));
 
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
     return rv;
   }
 
   iter->First();
 
   while (!iter->IsDone()) {
     if (iter->GetCurrentNode()->NodeType() == nsINode::TEXT_NODE) {
       // We found a text node! Adjust the document's iterator to point
       // to the beginning of its text block, then get the current selection.
       rv = mIterator->PositionAt(iter->GetCurrentNode());
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       rv = FirstTextNodeInCurrentBlock(mIterator);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
 
       mIteratorStatus = IteratorStatus::eValid;
 
       rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
                              mExtent, nullptr);
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
-
-      UNLOCK_DOC(this);
-
-      return rv;
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      return NS_OK;
     }
 
     iter->Next();
   }
 
   // If we get here, we didn't find any block before or inside
   // the selection! Just return OK.
-
-  UNLOCK_DOC(this);
-
   return NS_OK;
 }
 
 nsresult
 TextServicesDocument::PrevBlock()
 {
   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   if (mIteratorStatus == IteratorStatus::eDone) {
     return NS_OK;
   }
 
   switch (mIteratorStatus) {
     case IteratorStatus::eValid:
     case IteratorStatus::eNext: {
 
       nsresult rv = FirstTextNodeInPrevBlock(mIterator);
 
       if (NS_FAILED(rv)) {
         mIteratorStatus = IteratorStatus::eDone;
-        UNLOCK_DOC(this);
         return rv;
       }
 
       if (mIterator->IsDone()) {
         mIteratorStatus = IteratorStatus::eDone;
-        UNLOCK_DOC(this);
         return NS_OK;
       }
 
       mIteratorStatus = IteratorStatus::eValid;
       break;
     }
     case IteratorStatus::ePrev:
 
@@ -817,49 +748,43 @@ TextServicesDocument::PrevBlock()
     GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
     rv = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
   } else {
     // We must be done!
     mPrevTextBlock = nullptr;
     mNextTextBlock = nullptr;
   }
 
-  UNLOCK_DOC(this);
-
   // XXX The result of GetFirstTextNodeInNextBlock() or NS_OK.
   return rv;
 }
 
 nsresult
 TextServicesDocument::NextBlock()
 {
   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   if (mIteratorStatus == IteratorStatus::eDone) {
     return NS_OK;
   }
 
   switch (mIteratorStatus) {
     case IteratorStatus::eValid: {
 
       // Advance the iterator to the next text block.
 
       nsresult rv = FirstTextNodeInNextBlock(mIterator);
 
       if (NS_FAILED(rv)) {
         mIteratorStatus = IteratorStatus::eDone;
-        UNLOCK_DOC(this);
         return rv;
       }
 
       if (mIterator->IsDone()) {
         mIteratorStatus = IteratorStatus::eDone;
-        UNLOCK_DOC(this);
         return NS_OK;
       }
 
       mIteratorStatus = IteratorStatus::eValid;
       break;
     }
     case IteratorStatus::eNext:
 
@@ -888,107 +813,90 @@ TextServicesDocument::NextBlock()
     GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
     rv = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
   } else {
     // We must be done.
     mPrevTextBlock = nullptr;
     mNextTextBlock = nullptr;
   }
 
-  UNLOCK_DOC(this);
-
   // The result of GetFirstTextNodeInNextBlock() or NS_OK.
   return rv;
 }
 
 nsresult
 TextServicesDocument::IsDone(bool* aIsDone)
 {
   NS_ENSURE_TRUE(aIsDone, NS_ERROR_NULL_POINTER);
 
   *aIsDone = false;
 
   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   *aIsDone = mIteratorStatus == IteratorStatus::eDone;
 
-  UNLOCK_DOC(this);
-
   return NS_OK;
 }
 
 nsresult
 TextServicesDocument::SetSelection(int32_t aOffset,
                                    int32_t aLength)
 {
   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   nsresult rv = SetSelectionInternal(aOffset, aLength, true);
 
-  UNLOCK_DOC(this);
-
   //**** KDEBUG ****
   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
   //**** KDEBUG ****
 
   return rv;
 }
 
 nsresult
 TextServicesDocument::ScrollSelectionIntoView()
 {
   NS_ENSURE_TRUE(mSelCon, NS_ERROR_FAILURE);
 
-  LOCK_DOC(this);
-
   // After ScrollSelectionIntoView(), the pending notifications might be flushed
   // and PresShell/PresContext/Frames may be dead. See bug 418470.
   nsresult rv =
     mSelCon->ScrollSelectionIntoView(
       nsISelectionController::SELECTION_NORMAL,
       nsISelectionController::SELECTION_FOCUS_REGION,
       nsISelectionController::SCROLL_SYNCHRONOUS);
 
-  UNLOCK_DOC(this);
-
   return rv;
 }
 
 nsresult
 TextServicesDocument::DeleteSelection()
 {
   if (NS_WARN_IF(!mTextEditor) || NS_WARN_IF(!SelectionIsValid())) {
     return NS_ERROR_FAILURE;
   }
 
   if (SelectionIsCollapsed()) {
     return NS_OK;
   }
 
-  LOCK_DOC(this);
-
   // If we have an mExtent, save off its current set of
   // end points so we can compare them against mExtent's
   // set after the deletion of the content.
 
   nsCOMPtr<nsINode> origStartNode, origEndNode;
   int32_t origStartOffset = 0, origEndOffset = 0;
 
   if (mExtent) {
     nsresult rv =
       GetRangeEndPoints(mExtent,
                         getter_AddRefs(origStartNode), &origStartOffset,
                         getter_AddRefs(origEndNode), &origEndOffset);
 
     if (NS_FAILED(rv)) {
-      UNLOCK_DOC(this);
       return rv;
     }
   }
 
   int32_t selLength;
   OffsetEntry *entry, *newEntry;
 
   for (int32_t i = mSelStartIndex; i <= mSelEndIndex; i++) {
@@ -1014,17 +922,16 @@ TextServicesDocument::DeleteSelection()
         // Selection doesn't start at the beginning of the
         // text node entry. We need to split this entry into
         // two pieces, the piece before the selection, and
         // the piece inside the selection.
 
         nsresult rv = SplitOffsetEntry(i, selLength);
 
         if (NS_FAILED(rv)) {
-          UNLOCK_DOC(this);
           return rv;
         }
 
         // Adjust selection indexes to account for new entry:
 
         ++mSelStartIndex;
         ++mSelEndIndex;
         ++i;
@@ -1058,17 +965,16 @@ TextServicesDocument::DeleteSelection()
         if (selLength > 0 &&
             mSelEndOffset < entry->mStrOffset + entry->mLength) {
           // mStrOffset is guaranteed to be inside the selection, even
           // when mSelStartIndex == mSelEndIndex.
 
           nsresult rv = SplitOffsetEntry(i, entry->mLength - selLength);
 
           if (NS_FAILED(rv)) {
-            UNLOCK_DOC(this);
             return rv;
           }
 
           // Update the entry fields:
 
           newEntry = mOffsetTable[i+1];
           newEntry->mNodeOffset = entry->mNodeOffset;
         }
@@ -1095,34 +1001,32 @@ TextServicesDocument::DeleteSelection()
   AdjustContentIterator();
 
   // Now delete the actual content!
   RefPtr<TextEditor> textEditor = mTextEditor;
   nsresult rv =
     textEditor->DeleteSelectionAsAction(nsIEditor::ePrevious,
                                         nsIEditor::eStrip);
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
     return rv;
   }
 
   // Now that we've actually deleted the selected content,
   // check to see if our mExtent has changed, if so, then
   // we have to create a new content iterator!
 
   if (origStartNode && origEndNode) {
     nsCOMPtr<nsINode> curStartNode, curEndNode;
     int32_t curStartOffset = 0, curEndOffset = 0;
 
     rv = GetRangeEndPoints(mExtent,
                            getter_AddRefs(curStartNode), &curStartOffset,
                            getter_AddRefs(curEndNode), &curEndOffset);
 
     if (NS_FAILED(rv)) {
-      UNLOCK_DOC(this);
       return rv;
     }
 
     if (origStartNode != curStartNode || origEndNode != curEndNode) {
       // The range has changed, so we need to create a new content
       // iterator based on the new range.
 
       nsCOMPtr<nsIContent> curContent;
@@ -1137,17 +1041,16 @@ TextServicesDocument::DeleteSelection()
                      : nullptr;
       }
 
       // Create the new iterator.
 
       rv = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
 
       if (NS_FAILED(rv)) {
-        UNLOCK_DOC(this);
         return rv;
       }
 
       // Now make the new iterator point to the content node
       // the old one was pointing at.
 
       if (curContent) {
         rv = mIterator->PositionAt(curContent);
@@ -1206,18 +1109,16 @@ TextServicesDocument::DeleteSelection()
   rv = RemoveInvalidOffsetEntries();
 
   //**** KDEBUG ****
   // printf("\n---- After Delete\n");
   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
   // PrintOffsetTable();
   //**** KDEBUG ****
 
-  UNLOCK_DOC(this);
-
   return rv;
 }
 
 nsresult
 TextServicesDocument::InsertText(const nsString* aText)
 {
   if (NS_WARN_IF(!aText)) {
     return NS_ERROR_INVALID_ARG;
@@ -1242,110 +1143,80 @@ TextServicesDocument::InsertText(const n
     // Collapse to the start of the current selection
     // for the insert!
 
     nsresult rv = SetSelection(mSelStartOffset, 0);
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-
-  LOCK_DOC(this);
-
-  RefPtr<TextEditor> textEditor = mTextEditor;
-  nsresult rv = textEditor->BeginTransaction();
+  // AutoTransactionBatch grabs mTextEditor, so, we don't need to grab the
+  // instance with local variable here.
+  // XXX Well, do we really need to create AutoTransactionBatch here?
+  //     Looks like that after InsertTextAsAction(), this does nothing
+  //     from a point of view of editor.
+  AutoTransactionBatch bundleAllTransactions(*mTextEditor);
+
+  nsresult rv = mTextEditor->InsertTextAsAction(*aText);
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
-    return rv;
-  }
-
-  rv = textEditor->InsertTextAsAction(*aText);
-  if (NS_FAILED(rv)) {
-    textEditor->EndTransaction();
-    UNLOCK_DOC(this);
     return rv;
   }
 
   int32_t strLength = aText->Length();
 
   OffsetEntry *itEntry;
   OffsetEntry *entry = mOffsetTable[mSelStartIndex];
   void *node         = entry->mNode;
 
   NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
 
   if (entry->mStrOffset == mSelStartOffset) {
     if (entry->mIsInsertedText) {
       // If the caret is in an inserted text offset entry,
       // we simply insert the text at the end of the entry.
-
       entry->mLength += strLength;
     } else {
       // Insert an inserted text offset entry before the current
       // entry!
-
       itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
-
-      if (!itEntry) {
-        textEditor->EndTransaction();
-        UNLOCK_DOC(this);
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-
       itEntry->mIsInsertedText = true;
       itEntry->mNodeOffset = entry->mNodeOffset;
-
       if (!mOffsetTable.InsertElementAt(mSelStartIndex, itEntry)) {
-        textEditor->EndTransaction();
-        UNLOCK_DOC(this);
         return NS_ERROR_FAILURE;
       }
     }
   } else if (entry->mStrOffset + entry->mLength == mSelStartOffset) {
     // We are inserting text at the end of the current offset entry.
     // Look at the next valid entry in the table. If it's an inserted
     // text entry, add to its length and adjust its node offset. If
     // it isn't, add a new inserted text entry.
 
     // XXX Rename this!
     uint32_t i = mSelStartIndex + 1;
     itEntry = 0;
 
     if (mOffsetTable.Length() > i) {
       itEntry = mOffsetTable[i];
-
       if (!itEntry) {
-        textEditor->EndTransaction();
-        UNLOCK_DOC(this);
         return NS_ERROR_FAILURE;
       }
 
       // Check if the entry is a match. If it isn't, set
       // iEntry to zero.
-
       if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset) {
         itEntry = 0;
       }
     }
 
     if (!itEntry) {
       // We didn't find an inserted text offset entry, so
       // create one.
-
       itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
-
-      if (!itEntry) {
-        textEditor->EndTransaction();
-        UNLOCK_DOC(this);
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-
       itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
       itEntry->mIsInsertedText = true;
-
       if (!mOffsetTable.InsertElementAt(i, itEntry)) {
         delete itEntry;
         return NS_ERROR_FAILURE;
       }
     }
 
     // We have a valid inserted text offset entry. Update its
     // length, adjust the selection indexes, and make sure the
@@ -1353,59 +1224,42 @@ TextServicesDocument::InsertText(const n
 
     itEntry->mLength += strLength;
 
     mSelStartIndex = mSelEndIndex = i;
 
     RefPtr<Selection> selection =
       mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
     if (NS_WARN_IF(!selection)) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
       return rv;
     }
 
     rv = selection->Collapse(itEntry->mNode,
                              itEntry->mNodeOffset + itEntry->mLength);
 
     if (NS_FAILED(rv)) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
       return rv;
     }
   } else if (entry->mStrOffset + entry->mLength > mSelStartOffset) {
     // We are inserting text into the middle of the current offset entry.
     // split the current entry into two parts, then insert an inserted text
     // entry between them!
 
     // XXX Rename this!
     uint32_t i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
 
     rv = SplitOffsetEntry(mSelStartIndex, i);
-
     if (NS_FAILED(rv)) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
       return rv;
     }
 
     itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
-
-    if (!itEntry) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
     itEntry->mIsInsertedText = true;
     itEntry->mNodeOffset     = entry->mNodeOffset + entry->mLength;
-
     if (!mOffsetTable.InsertElementAt(mSelStartIndex + 1, itEntry)) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
       return NS_ERROR_FAILURE;
     }
 
     mSelEndIndex = ++mSelStartIndex;
   }
 
   // We've just finished inserting an inserted text offset entry.
   // update all entries with the same mNode pointer that follow
@@ -1424,63 +1278,49 @@ TextServicesDocument::InsertText(const n
   //**** KDEBUG ****
   // printf("\n---- After Insert\n");
   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
   // PrintOffsetTable();
   //**** KDEBUG ****
 
   if (!collapsedSelection) {
     rv = SetSelection(savedSelOffset, savedSelLength);
-
     if (NS_FAILED(rv)) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
       return rv;
     }
 
     rv = DeleteSelection();
-
     if (NS_FAILED(rv)) {
-      textEditor->EndTransaction();
-      UNLOCK_DOC(this);
       return rv;
     }
   }
 
-  rv = textEditor->EndTransaction();
-
-  UNLOCK_DOC(this);
-
-  return rv;
+  return NS_OK;
 }
 
 void
 TextServicesDocument::DidDeleteNode(nsINode* aChild)
 {
   if (NS_WARN_IF(!mIterator)) {
     return;
   }
 
-  LOCK_DOC(this);
-
   int32_t nodeIndex = 0;
   bool hasEntry = false;
   OffsetEntry *entry;
 
   nsresult rv =
     NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
   if (NS_FAILED(rv)) {
-    UNLOCK_DOC(this);
     return;
   }
 
   if (!hasEntry) {
     // It's okay if the node isn't in the offset table, the
     // editor could be cleaning house.
-    UNLOCK_DOC(this);
     return;
   }
 
   nsINode* node = mIterator->GetCurrentNode();
   if (node && node == aChild &&
       mIteratorStatus != IteratorStatus::eDone) {
     // XXX: This should never really happen because
     // AdjustContentIterator() should have been called prior
@@ -1489,30 +1329,26 @@ TextServicesDocument::DidDeleteNode(nsIN
     // wasn't a next, it would've set mIteratorStatus to eIsDone.
 
     NS_ERROR("DeleteNode called for current iterator node.");
   }
 
   int32_t tcount = mOffsetTable.Length();
   while (nodeIndex < tcount) {
     entry = mOffsetTable[nodeIndex];
-
     if (!entry) {
-      UNLOCK_DOC(this);
       return;
     }
 
     if (entry->mNode == aChild) {
       entry->mIsValid = false;
     }
 
     nodeIndex++;
   }
-
-  UNLOCK_DOC(this);
 }
 
 void
 TextServicesDocument::DidJoinNodes(nsINode& aLeftNode,
                                    nsINode& aRightNode)
 {
   // Make sure that both nodes are text nodes -- otherwise we don't care.
   if (!aLeftNode.IsText() || !aRightNode.IsText()) {
@@ -1553,18 +1389,16 @@ TextServicesDocument::DidJoinNodes(nsINo
 
   NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
 
   if (leftIndex > rightIndex) {
     // Don't know how to handle this situation.
     return;
   }
 
-  LOCK_DOC(this);
-
   OffsetEntry *entry = mOffsetTable[rightIndex];
   NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
 
   // Run through the table and change all entries referring to
   // the left node so that they now refer to the right node:
   uint32_t nodeLength = aLeftNode.Length();
   for (int32_t i = leftIndex; i < rightIndex; i++) {
     entry = mOffsetTable[i];
@@ -1590,18 +1424,16 @@ TextServicesDocument::DidJoinNodes(nsINo
   }
 
   // Now check to see if the iterator is pointing to the
   // left node. If it is, make it point to the right node!
 
   if (mIterator->GetCurrentNode() == &aLeftNode) {
     mIterator->PositionAt(&aRightNode);
   }
-
-  UNLOCK_DOC(this);
 }
 
 nsresult
 TextServicesDocument::CreateContentIterator(nsRange* aRange,
                                             nsIContentIterator** aIterator)
 {
   NS_ENSURE_TRUE(aRange && aIterator, NS_ERROR_NULL_POINTER);
 
@@ -2060,30 +1892,23 @@ TextServicesDocument::GetSelection(Block
   if (mIteratorStatus == IteratorStatus::eDone) {
     return NS_OK;
   }
 
   RefPtr<Selection> selection =
     mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
 
-  // XXX: If we expose this method publicly, we need to
-  //      add LOCK_DOC/UNLOCK_DOC calls!
-
-  // LOCK_DOC(this);
-
   nsresult rv;
   if (selection->IsCollapsed()) {
     rv = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
   } else {
     rv = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
   }
 
-  // UNLOCK_DOC(this);
-
   // XXX The result of GetCollapsedSelection() or GetUncollapsedSelection().
   return rv;
 }
 
 nsresult
 TextServicesDocument::GetCollapsedSelection(BlockSelectionStatus* aSelStatus,
                                             int32_t* aSelOffset,
                                             int32_t* aSelLength)
--- a/editor/txmgr/TransactionManager.cpp
+++ b/editor/txmgr/TransactionManager.cpp
@@ -192,16 +192,26 @@ NS_IMETHODIMP
 TransactionManager::Clear()
 {
   return ClearUndoRedo() ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 TransactionManager::BeginBatch(nsISupports* aData)
 {
+  nsresult rv = BeginBatchInternal(aData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+TransactionManager::BeginBatchInternal(nsISupports* aData)
+{
   // We can batch independent transactions together by simply pushing
   // a dummy transaction item on the do stack. This dummy transaction item
   // will be popped off the do stack, and then pushed on the undo stack
   // in EndBatch().
   bool doInterrupt = false;
   nsresult rv = WillBeginBatchNotify(&doInterrupt);
   if (NS_FAILED(rv)) {
     return rv;
@@ -220,16 +230,26 @@ TransactionManager::BeginBatch(nsISuppor
   // XXX The result of BeginTransaction() or DidBeginBatchNotify() if
   //     BeginTransaction() succeeded.
   return rv;
 }
 
 NS_IMETHODIMP
 TransactionManager::EndBatch(bool aAllowEmpty)
 {
+  nsresult rv = EndBatchInternal(aAllowEmpty);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+TransactionManager::EndBatchInternal(bool aAllowEmpty)
+{
   // XXX: Need to add some mechanism to detect the case where the transaction
   //      at the top of the do stack isn't the dummy transaction, so we can
   //      throw an error!! This can happen if someone calls EndBatch() within
   //      the DoTransaction() method of a transaction.
   //
   //      For now, we can detect this case by checking the value of the
   //      dummy transaction's mTransaction field. If it is our dummy
   //      transaction, it should be nullptr. This may not be true in the
--- a/editor/txmgr/TransactionManager.h
+++ b/editor/txmgr/TransactionManager.h
@@ -88,16 +88,22 @@ public:
   nsresult WillMergeNotify(nsITransaction* aTop,
                            nsITransaction* aTransaction,
                            bool* aInterrupt);
   nsresult DidMergeNotify(nsITransaction* aTop,
                           nsITransaction* aTransaction,
                           bool aDidMerge,
                           nsresult aMergeResult);
 
+  /**
+   * Exposing non-virtual methods of nsITransactionManager methods.
+   */
+  nsresult BeginBatchInternal(nsISupports* aData);
+  nsresult EndBatchInternal(bool aAllowEmpty);
+
 private:
   virtual ~TransactionManager() = default;
 
   nsresult BeginTransaction(nsITransaction* aTransaction,
                             nsISupports* aData);
   nsresult EndTransaction(bool aAllowEmpty);
 
   int32_t                mMaxTransactionCount;
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1007,38 +1007,48 @@ GLContextGLX::FindFBConfigForWindow(Disp
     const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
 #ifdef DEBUG
     printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
 #endif
 
     for (int i = 0; i < numConfigs; i++) {
         int visid = X11None;
         sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
-        if (!visid) {
-            continue;
+        if (visid) {
+            // WebRender compatible GLX visual is configured
+            // at nsWindow::Create() by GLContextGLX::FindVisual(),
+            // just reuse it here.
+            if (windowVisualID == static_cast<VisualID>(visid)) {
+                *out_config = cfgs[i];
+                *out_visid = visid;
+                return true;
+            }
         }
-        if (aWebRender || sGLXLibrary.IsATI()) {
+    }
+
+    // We don't have a frame buffer visual which matches the GLX visual
+    // from GLContextGLX::FindVisual(). Let's try to find a near one and hope
+    // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
+    for (int i = 0; i < numConfigs; i++) {
+        int visid = X11None;
+        sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
+        if (visid) {
             int depth;
             Visual* visual;
             FindVisualAndDepth(display, visid, &visual, &depth);
             if (depth == windowAttrs.depth &&
                 AreCompatibleVisuals(windowAttrs.visual, visual)) {
                 *out_config = cfgs[i];
                 *out_visid = visid;
                 return true;
             }
-        } else {
-            if (windowVisualID == static_cast<VisualID>(visid)) {
-                *out_config = cfgs[i];
-                *out_visid = visid;
-                return true;
-            }
         }
     }
 
+
     NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
     return false;
 }
 
 static already_AddRefed<GLContextGLX>
 CreateOffscreenPixmapContext(CreateContextFlags flags, const IntSize& size,
                              const SurfaceCaps& minCaps, nsACString* const out_failureId)
 {
--- a/gfx/layers/apz/src/AndroidFlingPhysics.cpp
+++ b/gfx/layers/apz/src/AndroidFlingPhysics.cpp
@@ -146,16 +146,18 @@ StaticAutoPtr<SplineConstants> gSplineCo
   gSplineConstants = new SplineConstants();
   ClearOnShutdown(&gSplineConstants);
 }
 
 void AndroidFlingPhysics::Init(const ParentLayerPoint& aStartingVelocity,
                                float aPLPPI)
 {
   mVelocity = aStartingVelocity.Length();
+  // We should not have created a fling animation if there is no velocity.
+  MOZ_ASSERT(mVelocity != 0.0f);
   const double tuningCoeff = ComputeDeceleration(aPLPPI);
   mTargetDuration = ComputeFlingDuration(mVelocity, tuningCoeff);
   MOZ_ASSERT(!mTargetDuration.IsZero());
   mDurationSoFar = TimeDuration();
   mLastPos = ParentLayerPoint();
   mCurrentPos = ParentLayerPoint();
   float coeffX = mVelocity == 0 ? 1.0f : aStartingVelocity.x / mVelocity;
   float coeffY = mVelocity == 0 ? 1.0f : aStartingVelocity.y / mVelocity;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3059,16 +3059,25 @@ ParentLayerPoint AsyncPanZoomController:
     mX.SetVelocity(mX.GetVelocity() + aHandoffState.mVelocity.x);
     residualVelocity.x = 0;
   }
   if (mY.CanScroll()) {
     mY.SetVelocity(mY.GetVelocity() + aHandoffState.mVelocity.y);
     residualVelocity.y = 0;
   }
 
+  // If we're not scrollable in at least one of the directions in which we
+  // were handed velocity, don't start a fling animation.
+  if (GetVelocityVector().Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
+    // Relieve overscroll now if needed, since we will not transition to a fling
+    // animation and then an overscroll animation, and relieve it then.
+    aHandoffState.mChain->SnapBackOverscrolledApzc(this);
+    return residualVelocity;
+  }
+
   // If there's a scroll snap point near the predicted fling destination,
   // scroll there using a smooth scroll animation. Otherwise, start a
   // fling animation.
   ScrollSnapToDestination();
   if (mState != SMOOTH_SCROLL) {
     SetState(FLING);
     AsyncPanZoomAnimation* fling = GetPlatformSpecificState()->CreateFlingAnimation(
         *this, aHandoffState, PLPPI);
--- a/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
+++ b/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
@@ -407,16 +407,17 @@ TEST_F(APZScrollHandoffTester, OpposingC
 }
 
 // Test that flinging in a direction where one component of the fling goes into
 // overscroll but the other doesn't, results in just the one component being
 // handed off to the parent, while the original APZC continues flinging in the
 // other direction.
 TEST_F(APZScrollHandoffTester, PartialFlingHandoff) {
   SCOPED_GFX_VAR(UseWebRender, bool, false);
+  SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
 
   CreateScrollHandoffLayerTree1();
 
   // Fling up and to the left. The child APZC has room to scroll up, but not
   // to the left, so the horizontal component of the fling should be handed
   // off to the parent APZC.
   Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55));
 
--- a/netwerk/dns/TRR.cpp
+++ b/netwerk/dns/TRR.cpp
@@ -174,17 +174,17 @@ TRR::SendHTTPRequest()
     /* For GET requests, the outgoing packet needs to be Base64url-encoded and
        then appended to the end of the URI. */
     rv = Base64URLEncode(tmp.Length(), reinterpret_cast<const unsigned char *>(tmp.get()),
                          Base64URLEncodePaddingPolicy::Omit, body);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString uri;
     gTRRService->GetURI(uri);
-    uri.Append(NS_LITERAL_CSTRING("?ct&dns="));
+    uri.Append(NS_LITERAL_CSTRING("?dns="));
     uri.Append(body);
     rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
   } else {
     rv = DohEncode(body);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString uri;
     gTRRService->GetURI(uri);
@@ -211,17 +211,17 @@ TRR::SendHTTPRequest()
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   if (!httpChannel) {
     return NS_ERROR_UNEXPECTED;
   }
 
   rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
-                                     NS_LITERAL_CSTRING("application/dns-udpwireformat"),
+                                     NS_LITERAL_CSTRING("application/dns-message"),
                                      false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString cred;
   gTRRService->GetCredentials(cred);
   if (!cred.IsEmpty()){
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Authorization"), cred, false);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -253,24 +253,24 @@ TRR::SendHTTPRequest()
       return NS_ERROR_UNEXPECTED;
     }
     uint32_t streamLength = body.Length();
     nsCOMPtr<nsIInputStream> uploadStream;
     rv = NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = uploadChannel->ExplicitSetUploadStream(uploadStream,
-                                                NS_LITERAL_CSTRING("application/dns-udpwireformat"),
+                                                NS_LITERAL_CSTRING("application/dns-message"),
                                                 streamLength,
                                                 NS_LITERAL_CSTRING("POST"), false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // set the *default* response content type
-  if (NS_FAILED(httpChannel->SetContentType(NS_LITERAL_CSTRING("application/dns-udpwireformat")))) {
+  if (NS_FAILED(httpChannel->SetContentType(NS_LITERAL_CSTRING("application/dns-message")))) {
     LOG(("TRR::SendHTTPRequest: couldn't set content-type!\n"));
   }
   if (NS_SUCCEEDED(httpChannel->AsyncOpen2(this))) {
     NS_NewTimerWithCallback(getter_AddRefs(mTimeout),
                             this, gTRRService->GetRequestTimeout(),
                             nsITimer::TYPE_ONE_SHOT);
     return NS_OK;
   }
@@ -924,19 +924,18 @@ TRR::OnStopRequest(nsIRequest *aRequest,
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
     if (!httpChannel) {
       return NS_ERROR_UNEXPECTED;
     }
     nsresult rv = NS_OK;
     nsAutoCString contentType;
     httpChannel->GetContentType(contentType);
     if (contentType.Length() &&
-        !contentType.LowerCaseEqualsLiteral("application/dns-udpwireformat")) {
-      // try and parse missing content-types, but otherwise require udpwireformat
-      LOG(("TRR:OnStopRequest %p %s %d should fail due to content type %s\n",
+        !contentType.LowerCaseEqualsLiteral("application/dns-message")) {
+      LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n",
            this, mHost.get(), mType, contentType.get()));
       FailData(NS_ERROR_UNEXPECTED);
       return NS_OK;
     }
 
     uint32_t httpStatus;
     rv = httpChannel->GetResponseStatus(&httpStatus);
     if (NS_SUCCEEDED(rv) && httpStatus == 200) {
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,45 +4,38 @@
   "lockfileVersion": 1,
   "dependencies": {
     "acorn": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
       "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="
     },
     "acorn-jsx": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
-      "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz",
+      "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==",
       "requires": {
-        "acorn": "3.3.0"
-      },
-      "dependencies": {
-        "acorn": {
-          "version": "3.3.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
-        }
+        "acorn": "5.7.1"
       }
     },
     "ajv": {
-      "version": "5.5.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
+      "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
       "requires": {
-        "co": "4.6.0",
-        "fast-deep-equal": "1.1.0",
+        "fast-deep-equal": "2.0.1",
         "fast-json-stable-stringify": "2.0.0",
-        "json-schema-traverse": "0.3.1"
+        "json-schema-traverse": "0.4.1",
+        "uri-js": "4.2.2"
       }
     },
     "ajv-keywords": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
-      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I="
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
+      "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo="
     },
     "ansi-escapes": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
       "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw=="
     },
     "ansi-regex": {
       "version": "2.1.1",
@@ -130,21 +123,16 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "requires": {
         "balanced-match": "1.0.0",
         "concat-map": "0.0.1"
       }
     },
-    "buffer-from": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
-      "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ=="
-    },
     "caller-path": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
       "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
       "requires": {
         "callsites": "0.2.0"
       }
     },
@@ -199,21 +187,16 @@
         "restore-cursor": "2.0.0"
       }
     },
     "cli-width": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
       "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
     },
-    "co": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
-    },
     "color-convert": {
       "version": "1.9.2",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
       "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
       "requires": {
         "color-name": "1.1.1"
       }
     },
@@ -222,38 +205,29 @@
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
       "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok="
     },
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "requires": {
-        "buffer-from": "1.1.0",
-        "inherits": "2.0.3",
-        "readable-stream": "2.3.6",
-        "typedarray": "0.0.6"
-      }
-    },
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     "cross-spawn": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-      "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
       "requires": {
-        "lru-cache": "4.1.3",
+        "nice-try": "1.0.4",
+        "path-key": "2.0.1",
+        "semver": "5.5.0",
         "shebang-command": "1.2.0",
         "which": "1.3.1"
       }
     },
     "debug": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
       "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
@@ -363,64 +337,65 @@
       }
     },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
     "eslint": {
-      "version": "4.19.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
-      "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz",
+      "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==",
       "requires": {
-        "ajv": "5.5.2",
+        "ajv": "6.5.2",
         "babel-code-frame": "6.26.0",
         "chalk": "2.4.1",
-        "concat-stream": "1.6.2",
-        "cross-spawn": "5.1.0",
+        "cross-spawn": "6.0.5",
         "debug": "3.1.0",
         "doctrine": "2.1.0",
-        "eslint-scope": "3.7.3",
+        "eslint-scope": "4.0.0",
+        "eslint-utils": "1.3.1",
         "eslint-visitor-keys": "1.0.0",
-        "espree": "3.5.4",
+        "espree": "4.0.0",
         "esquery": "1.0.1",
         "esutils": "2.0.2",
         "file-entry-cache": "2.0.0",
         "functional-red-black-tree": "1.0.1",
         "glob": "7.1.2",
         "globals": "11.7.0",
-        "ignore": "3.3.10",
+        "ignore": "4.0.3",
         "imurmurhash": "0.1.4",
-        "inquirer": "3.3.0",
+        "inquirer": "5.2.0",
         "is-resolvable": "1.1.0",
         "js-yaml": "3.12.0",
         "json-stable-stringify-without-jsonify": "1.0.1",
         "levn": "0.3.0",
         "lodash": "4.17.10",
         "minimatch": "3.0.4",
         "mkdirp": "0.5.1",
         "natural-compare": "1.4.0",
         "optionator": "0.8.2",
         "path-is-inside": "1.0.2",
         "pluralize": "7.0.0",
         "progress": "2.0.0",
-        "regexpp": "1.1.0",
+        "regexpp": "2.0.0",
         "require-uncached": "1.0.3",
         "semver": "5.5.0",
+        "string.prototype.matchall": "2.0.0",
         "strip-ansi": "4.0.0",
         "strip-json-comments": "2.0.1",
-        "table": "4.0.2",
+        "table": "4.0.3",
         "text-table": "0.2.0"
       }
     },
     "eslint-plugin-html": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.3.tgz",
-      "integrity": "sha512-ArFnlfQxwYSz/CP0zvk8Cy3MUhcDpT3o6jgO8eKD/b8ezcLVBrgkYzmMv+7S/ya+Yl9pN+Cz2tsgYp/zElkQzA==",
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.5.tgz",
+      "integrity": "sha512-yULqYldzhYXTwZEaJXM30HhfgJdtTzuVH3LeoANybESHZ5+2ztLD72BsB2wR124/kk/PvQqZofDFSdNIk+kykw==",
       "requires": {
         "htmlparser2": "3.9.2"
       }
     },
     "eslint-plugin-mozilla": {
       "version": "file:tools/lint/eslint/eslint-plugin-mozilla",
       "requires": {
         "htmlparser2": "3.9.2",
@@ -443,36 +418,41 @@
         "jsx-ast-utils": "2.0.1",
         "prop-types": "15.6.2"
       }
     },
     "eslint-plugin-spidermonkey-js": {
       "version": "file:tools/lint/eslint/eslint-plugin-spidermonkey-js"
     },
     "eslint-scope": {
-      "version": "3.7.3",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
-      "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+      "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
       "requires": {
         "esrecurse": "4.2.1",
         "estraverse": "4.2.0"
       }
     },
+    "eslint-utils": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+      "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q=="
+    },
     "eslint-visitor-keys": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
       "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ=="
     },
     "espree": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
-      "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz",
+      "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==",
       "requires": {
         "acorn": "5.7.1",
-        "acorn-jsx": "3.0.1"
+        "acorn-jsx": "4.1.1"
       }
     },
     "esprima": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
     },
     "esquery": {
@@ -507,19 +487,19 @@
       "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
       "requires": {
         "chardet": "0.4.2",
         "iconv-lite": "0.4.23",
         "tmp": "0.0.33"
       }
     },
     "fast-deep-equal": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
-      "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
     },
     "fast-json-stable-stringify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
       "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
     },
     "fast-levenshtein": {
       "version": "2.0.6",
@@ -626,16 +606,21 @@
         "ansi-regex": "2.1.1"
       }
     },
     "has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
     },
+    "has-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
+    },
     "htmlparser2": {
       "version": "3.9.2",
       "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
       "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=",
       "requires": {
         "domelementtype": "1.3.0",
         "domhandler": "2.4.2",
         "domutils": "1.7.0",
@@ -648,19 +633,19 @@
       "version": "0.4.23",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
       "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
       "requires": {
         "safer-buffer": "2.1.2"
       }
     },
     "ignore": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
-      "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz",
+      "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A=="
     },
     "imurmurhash": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
     },
     "inflight": {
       "version": "1.0.6",
@@ -677,31 +662,30 @@
       "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
     },
     "ini-parser": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/ini-parser/-/ini-parser-0.0.2.tgz",
       "integrity": "sha1-+kF4flZ3Y7P/Zdel2alO23QHh+8="
     },
     "inquirer": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
-      "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz",
+      "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==",
       "requires": {
         "ansi-escapes": "3.1.0",
         "chalk": "2.4.1",
         "cli-cursor": "2.1.0",
         "cli-width": "2.2.0",
         "external-editor": "2.2.0",
         "figures": "2.0.0",
         "lodash": "4.17.10",
         "mute-stream": "0.0.7",
         "run-async": "2.3.0",
-        "rx-lite": "4.0.8",
-        "rx-lite-aggregates": "4.0.8",
+        "rxjs": "5.5.11",
         "string-width": "2.1.1",
         "strip-ansi": "4.0.0",
         "through": "2.3.8"
       }
     },
     "is-callable": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
@@ -781,19 +765,19 @@
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
       "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
       "requires": {
         "argparse": "1.0.10",
         "esprima": "4.0.1"
       }
     },
     "json-schema-traverse": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
     },
     "json-stable-stringify-without-jsonify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
       "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
     },
     "jsx-ast-utils": {
       "version": "2.0.1",
@@ -820,25 +804,16 @@
     "loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
       "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
       "requires": {
         "js-tokens": "3.0.2"
       }
     },
-    "lru-cache": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz",
-      "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==",
-      "requires": {
-        "pseudomap": "1.0.2",
-        "yallist": "2.1.2"
-      }
-    },
     "mimic-fn": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
       "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
     },
     "minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -870,16 +845,21 @@
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
       "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
     },
     "natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="
     },
+    "nice-try": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz",
+      "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA=="
+    },
     "object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
       "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
     },
     "object-keys": {
       "version": "1.0.12",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
@@ -924,16 +904,21 @@
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "path-is-inside": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
       "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
     },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+    },
     "pify": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
       "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
     },
     "pinkie": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
@@ -971,39 +956,47 @@
       "version": "15.6.2",
       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
       "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
       "requires": {
         "loose-envify": "1.4.0",
         "object-assign": "4.1.1"
       }
     },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
     },
     "readable-stream": {
       "version": "2.3.6",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
       "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "requires": {
         "core-util-is": "1.0.2",
         "inherits": "2.0.3",
         "isarray": "1.0.0",
         "process-nextick-args": "2.0.0",
         "safe-buffer": "5.1.2",
         "string_decoder": "1.1.1",
         "util-deprecate": "1.0.2"
       }
     },
+    "regexp.prototype.flags": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz",
+      "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==",
+      "requires": {
+        "define-properties": "1.1.2"
+      }
+    },
     "regexpp": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
-      "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw=="
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz",
+      "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA=="
     },
     "require-uncached": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
       "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
       "requires": {
         "caller-path": "0.1.0",
         "resolve-from": "1.0.1"
@@ -1034,27 +1027,22 @@
     "run-async": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
       "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
       "requires": {
         "is-promise": "2.1.0"
       }
     },
-    "rx-lite": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
-      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ="
-    },
-    "rx-lite-aggregates": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
-      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+    "rxjs": {
+      "version": "5.5.11",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
+      "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
       "requires": {
-        "rx-lite": "4.0.8"
+        "symbol-observable": "1.0.1"
       }
     },
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
       "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
     "safer-buffer": {
@@ -1107,16 +1095,28 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
       "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
       "requires": {
         "is-fullwidth-code-point": "2.0.0",
         "strip-ansi": "4.0.0"
       }
     },
+    "string.prototype.matchall": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz",
+      "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==",
+      "requires": {
+        "define-properties": "1.1.2",
+        "es-abstract": "1.12.0",
+        "function-bind": "1.1.1",
+        "has-symbols": "1.0.0",
+        "regexp.prototype.flags": "1.2.0"
+      }
+    },
     "string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "requires": {
         "safe-buffer": "5.1.2"
       }
     },
@@ -1140,23 +1140,28 @@
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
       "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
     },
     "supports-color": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
       "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
     },
+    "symbol-observable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+      "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
+    },
     "table": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
-      "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz",
+      "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==",
       "requires": {
-        "ajv": "5.5.2",
-        "ajv-keywords": "2.1.1",
+        "ajv": "6.5.2",
+        "ajv-keywords": "3.2.0",
         "chalk": "2.4.1",
         "lodash": "4.17.10",
         "slice-ansi": "1.0.0",
         "string-width": "2.1.1"
       }
     },
     "text-table": {
       "version": "0.2.0",
@@ -1179,20 +1184,23 @@
     "type-check": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
       "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
       "requires": {
         "prelude-ls": "1.1.2"
       }
     },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+      "requires": {
+        "punycode": "2.1.1"
+      }
     },
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
     "which": {
       "version": "1.3.1",
@@ -1214,16 +1222,11 @@
     },
     "write": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
       "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
       "requires": {
         "mkdirp": "0.5.1"
       }
-    },
-    "yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
     }
   }
 }
--- a/package.json
+++ b/package.json
@@ -1,15 +1,15 @@
 {
   "name": "mozillaeslintsetup",
   "description": "This package file is for setup of ESLint only for editor integration.",
   "repository": {},
   "license": "MPL-2.0",
   "dependencies": {
-    "eslint": "4.19.1",
-    "eslint-plugin-html": "4.0.3",
+    "eslint": "5.3.0",
+    "eslint-plugin-html": "4.0.5",
     "eslint-plugin-mozilla": "file:tools/lint/eslint/eslint-plugin-mozilla",
     "eslint-plugin-no-unsanitized": "3.0.2",
     "eslint-plugin-react": "7.10.0",
     "eslint-plugin-spidermonkey-js": "file:tools/lint/eslint/eslint-plugin-spidermonkey-js"
   },
   "devDependencies": {}
 }
--- a/python/mozbuild/mozpack/packager/__init__.py
+++ b/python/mozbuild/mozpack/packager/__init__.py
@@ -13,16 +13,17 @@ from mozpack.chrome.manifest import (
     ManifestBinaryComponent,
     ManifestChrome,
     ManifestInterfaces,
     is_manifest,
     parse_manifest,
 )
 import mozpack.path as mozpath
 from collections import deque
+import json
 
 
 class Component(object):
     '''
     Class that represents a component in a package manifest.
     '''
     def __init__(self, name, destdir=''):
         if name.find(' ') > 0:
@@ -264,17 +265,40 @@ class SimplePackager(object):
             self._queue.append(self.formatter.add_interfaces, path, file)
         else:
             self._file_queue.append(self.formatter.add, path, file)
             if mozpath.basename(path) == 'install.rdf':
                 addon = True
                 install_rdf = file.open().read()
                 if self.UNPACK_ADDON_RE.search(install_rdf):
                     addon = 'unpacked'
-                self._addons[mozpath.dirname(path)] = addon
+                self._add_addon(mozpath.dirname(path), addon)
+            elif mozpath.basename(path) == 'manifest.json':
+                manifest = file.open().read()
+                try:
+                    parsed = json.loads(manifest)
+                except ValueError:
+                    pass
+                if isinstance(parsed, dict) and parsed.has_key('manifest_version'):
+                    self._add_addon(mozpath.dirname(path), True)
+
+    def _add_addon(self, path, addon_type):
+        '''
+        Add the given BaseFile to the collection of addons if a parent
+        directory is not already in the collection.
+        '''
+        if mozpath.basedir(path, self._addons) != None:
+            return
+
+        for dir in self._addons:
+            if mozpath.basedir(dir, [path]) != None:
+                del self._addons[dir]
+                break
+
+        self._addons[path] = addon_type
 
     def _add_manifest_file(self, path, file):
         '''
         Add the given BaseFile with manifest file contents with the given path.
         '''
         self._manifests.add(path)
         base = ''
         if hasattr(file, 'path'):
--- a/python/mozbuild/mozpack/test/test_packager.py
+++ b/python/mozbuild/mozpack/test/test_packager.py
@@ -232,16 +232,39 @@ class TestSimplePackager(unittest.TestCa
                 '<RDF>\n<... em:unpack=\'true\'>\n<...>\n</RDF>')
             packager.add('addon10/install.rdf', install_rdf_addon10)
 
         with errors.context('manifest', 19):
             install_rdf_addon11 = GeneratedFile(
                 '<RDF>\n<... em:unpack=\'false\'>\n<...>\n</RDF>')
             packager.add('addon11/install.rdf', install_rdf_addon11)
 
+        we_manifest = GeneratedFile('{"manifest_version": 2, "name": "Test WebExtension", "version": "1.0"}')
+        # hybrid and hybrid2 are both bootstrapped extensions with
+        # embedded webextensions, they differ in the order in which
+        # the manifests are added to the packager.
+        with errors.context('manifest', 20):
+            packager.add('hybrid/install.rdf', install_rdf)
+
+        with errors.context('manifest', 21):
+            packager.add('hybrid/webextension/manifest.json', we_manifest)
+
+        with errors.context('manifest', 22):
+            packager.add('hybrid2/webextension/manifest.json', we_manifest)
+
+        with errors.context('manifest', 23):
+            packager.add('hybrid2/install.rdf', install_rdf)
+
+        with errors.context('manifest', 24):
+            packager.add('webextension/manifest.json', we_manifest)
+
+        non_we_manifest = GeneratedFile('{"not a webextension": true}')
+        with errors.context('manifest', 25):
+            packager.add('nonwebextension/manifest.json', non_we_manifest)
+
         self.assertEqual(formatter.log, [])
 
         with errors.context('dummy', 1):
             packager.close()
         self.maxDiff = None
         # The formatter is expected to reorder the manifest entries so that
         # chrome entries appear before the others.
         self.assertEqual(formatter.log, [
@@ -252,17 +275,20 @@ class TestSimplePackager(unittest.TestCa
             (('dummy', 1), 'add_base', 'addon2', 'unpacked'),
             (('dummy', 1), 'add_base', 'addon3', 'unpacked'),
             (('dummy', 1), 'add_base', 'addon4', 'unpacked'),
             (('dummy', 1), 'add_base', 'addon5', True),
             (('dummy', 1), 'add_base', 'addon6', 'unpacked'),
             (('dummy', 1), 'add_base', 'addon7', True),
             (('dummy', 1), 'add_base', 'addon8', 'unpacked'),
             (('dummy', 1), 'add_base', 'addon9', True),
+            (('dummy', 1), 'add_base', 'hybrid', True),
+            (('dummy', 1), 'add_base', 'hybrid2', True),
             (('dummy', 1), 'add_base', 'qux', False),
+            (('dummy', 1), 'add_base', 'webextension', True),
             ((os.path.join(curdir, 'foo', 'bar.manifest'), 2),
              'add_manifest', ManifestContent('foo', 'bar', 'bar/')),
             ((os.path.join(curdir, 'foo', 'bar.manifest'), 1),
              'add_manifest', ManifestResource('foo', 'bar', 'bar/')),
             (('bar/baz.manifest', 1),
              'add_manifest', ManifestResource('bar', 'baz', 'baz/')),
             (('qux/qux.manifest', 1),
              'add_manifest', ManifestResource('qux', 'qux', 'qux/')),
@@ -292,22 +318,33 @@ class TestSimplePackager(unittest.TestCa
             (('manifest', 16), 'add', 'addon8/install.rdf',
              install_rdf_addon8),
             (('manifest', 17), 'add', 'addon9/install.rdf',
              install_rdf_addon9),
             (('manifest', 18), 'add', 'addon10/install.rdf',
              install_rdf_addon10),
             (('manifest', 19), 'add', 'addon11/install.rdf',
              install_rdf_addon11),
+            (('manifest', 20), 'add', 'hybrid/install.rdf', install_rdf),
+            (('manifest', 21),
+             'add', 'hybrid/webextension/manifest.json', we_manifest),
+            (('manifest', 22),
+             'add', 'hybrid2/webextension/manifest.json', we_manifest),
+            (('manifest', 23), 'add', 'hybrid2/install.rdf', install_rdf),
+            (('manifest', 24),
+             'add', 'webextension/manifest.json', we_manifest),
+            (('manifest', 25),
+             'add', 'nonwebextension/manifest.json', non_we_manifest),
         ])
 
         self.assertEqual(packager.get_bases(),
                          set(['', 'addon', 'addon2', 'addon3', 'addon4',
                               'addon5', 'addon6', 'addon7', 'addon8',
-                              'addon9', 'addon10', 'addon11', 'qux']))
+                              'addon9', 'addon10', 'addon11', 'qux',
+                              'hybrid', 'hybrid2', 'webextension']))
         self.assertEqual(packager.get_bases(addons=False), set(['', 'qux']))
 
     def test_simple_packager_manifest_consistency(self):
         formatter = MockFormatter()
         # bar/ is detected as an addon because of install.rdf, but top-level
         # includes a manifest inside bar/.
         packager = SimplePackager(formatter)
         packager.add('base.manifest', GeneratedFile(
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -138,28 +138,36 @@ static const char kGOOGLE_PIN_DigiCertSH
 /* GOOGLE_PIN_Entrust_SSL */
 static const char kGOOGLE_PIN_Entrust_SSLFingerprint[] =
   "nsxRNo6G40YPZsKV5JQt1TCA8nseQQr/LRqp1Oa8fnw=";
 
 /* GOOGLE_PIN_GTECyberTrustGlobalRoot */
 static const char kGOOGLE_PIN_GTECyberTrustGlobalRootFingerprint[] =
   "EGn6R6CqT4z3ERscrqNl7q7RC//zJmDe9uBhS/rnCHU=";
 
+/* GOOGLE_PIN_GTSCA1O1 */
+static const char kGOOGLE_PIN_GTSCA1O1Fingerprint[] =
+  "YZPgTZ+woNCCCIW3LH2CxQeLzB/1m42QcCTBSdgayjs=";
+
 /* GOOGLE_PIN_GeoTrustGlobal2 */
 static const char kGOOGLE_PIN_GeoTrustGlobal2Fingerprint[] =
   "F3VaXClfPS1y5vAxofB/QAxYi55YKyLxfq4xoVkNEYU=";
 
 /* GOOGLE_PIN_GoDaddySecure */
 static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
   "MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
 
 /* GOOGLE_PIN_GoogleG2 */
 static const char kGOOGLE_PIN_GoogleG2Fingerprint[] =
   "7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=";
 
+/* GOOGLE_PIN_GoogleG3 */
+static const char kGOOGLE_PIN_GoogleG3Fingerprint[] =
+  "f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=";
+
 /* GOOGLE_PIN_RapidSSL */
 static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
   "lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
 
 /* GOOGLE_PIN_SecureCertificateServices */
 static const char kGOOGLE_PIN_SecureCertificateServicesFingerprint[] =
   "RpHL/ehKa2BS3b4VK7DCFq4lqG5XR4E9vA8UfzOFcL4=";
 
@@ -456,17 +464,18 @@ static const char* const kPinset_test_Da
 static const StaticFingerprints kPinset_test = {
   sizeof(kPinset_test_Data) / sizeof(const char*),
   kPinset_test_Data
 };
 
 static const char* const kPinset_google_Data[] = {
   kGOOGLE_PIN_GoogleG2Fingerprint,
   kGoogleBackup2048Fingerprint,
-  kGeoTrust_Global_CAFingerprint,
+  kGOOGLE_PIN_GTSCA1O1Fingerprint,
+  kGOOGLE_PIN_GoogleG3Fingerprint,
   kGlobalSign_Root_CA___R2Fingerprint,
 };
 static const StaticFingerprints kPinset_google = {
   sizeof(kPinset_google_Data) / sizeof(const char*),
   kPinset_google_Data
 };
 
 static const char* const kPinset_tor_Data[] = {
@@ -1160,9 +1169,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 487;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1542017029312000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1542276268102000);
--- a/security/manager/ssl/nsSTSPreloadList.inc
+++ b/security/manager/ssl/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.                */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1544436205842000);
+const PRTime gPreloadListExpirationTime = INT64_C(1544695448460000);
 %%
 0-1.party, 1
 0.me.uk, 1
 0005pay.com, 1
 00100010.net, 1
 0010100.net, 1
 00120012.net, 1
 00130013.net, 1
--- a/services/settings/dumps/blocklists/addons.json
+++ b/services/settings/dumps/blocklists/addons.json
@@ -1,1 +1,1 @@
-{"data":[{"guid":"{5834f62d-6164-4cdd-a0a3-c00c66ec9d13}","prefs":[],"schema":1532704368947,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1479002","why":"This add-on violates our security and user-choice/no surprises policies.","name":"Youtube Dark Mode (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"d0a401cb-0c70-4784-8288-b06a88b2ae8a","last_modified":1532705151926},{"guid":"/^((@asdfjhsdfuhw)|(@asdfsdfwe)|(@asdieieuss)|(@dghfghfgh)|(@difherk)|(@dsfgtftgjhrdf4)|(@fidfueir)|(@fsgergsdqtyy)|(@hjconsnfes)|(@isdifvdkf)|(@iweruewir)|(@oiboijdjfj)|(@safesearchavs)|(@safesearchavsext)|(@safesearchincognito)|(@safesearchscoutee)|(@sdfykhhhfg)|(@sdiosuff)|(@sdklsajd)|(@sduixcjksd)|(@sicognitores)|(@simtabtest)|(@sodiasudi)|(@test13)|(@test131)|(@test131ver)|(@test132)|(@test13s)|(@testmptys)|(\\{ac4e5b0c-13c4-4bfd-a0c3-1e73c81e8bac\\})|(\\{e78785c3-ec49-44d2-8aac-9ec7293f4a8f\\})|(general@filecheckerapp\\.com)|(general@safesearch\\.net))$/","prefs":[],"schema":1532703832328,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1475330","why":"These Add-ons violate our data collection, no surprises and user-choice policies.","name":"Safesearch (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"d664412d-ed08-4892-b247-b007a70856ff","last_modified":1532704364007},{"guid":"{dd3d7613-0246-469d-bc65-2a3cc1668adc}","prefs":[],"schema":1532684052432,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1478731","why":"This add-on violates data practices outlined in the review policy.","name":"BlockSite"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"4.0.3","minVersion":"0"}],"id":"e04f98b5-4480-43a3-881d-e509e4e28cdc","last_modified":1532684085999},{"guid":"{bee8b1f2-823a-424c-959c-f8f76c8b2306}","prefs":[],"schema":1532547689407,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1478731","why":"This add-on violates data practices outlined in the review policy.","name":"Popup blocker for FireFox"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"4.0.7.3","minVersion":"0"}],"id":"f0713a5e-7208-484e-b3a0-4e6dc6a195be","last_modified":1532684052426},{"guid":"/^((\\{39bd8607-0af4-4d6b-bd69-9a63c1825d3c\\})|(\\{273f0bce-33f4-45f6-ae03-df67df3864c2\\})|(\\{a77fc9b9-6ebb-418d-b0b6-86311c191158\\})|(\\{c6c4a718-cf91-4648-aa9b-170d66163cf2\\})|(\\{d371abec-84bb-481b-acbf-235639451127\\})|(\\{e63b262a-f9b8-4496-9c4b-9d3cbd6aea90\\}))$/","prefs":[],"schema":1532386339902,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1477950","why":"Add-ons that contain malicious functionality like search engine redirect.","name":"Smash (Malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"c37c7c24-e738-4d06-888c-108b4d63b428","last_modified":1532424286908},{"guid":"/^((\\{ac296b47-7c03-486f-a1d6-c48b24419749\\})|(\\{1cab8ccf-deff-4743-925d-a47cbd0a6b56\\})|(\\{5da81d3d-5db1-432a-affc-4a2fe9a70749\\})|(\\{071b9878-a7d3-4ae3-8ef0-2eaee1923403\\})|(\\{261476ea-bd0e-477c-abd7-33cdf626f81f\\})|(\\{224e66d0-6b11-4c4b-9bcf-41180889898a\\})|(\\{1e90cf52-c67c-4bd9-80c3-a2bf521fc981\\})|(\\{09c4799c-00f1-439e-9e60-3827c589b372\\})|(\\{d3d2095a-9faa-466f-82ae-3114179b34d6\\})|(\\{70389ea5-7e4d-4515-835c-fbd047f229dd\\})|(\\{2e8083a5-cd88-4aaa-bb8b-e54e9753f280\\})|(\\{fbf2480b-5c19-478e-bfd0-192ad9f84dc9\\})|(\\{6c7dc694-89f8-477e-88d5-c55af4d6a846\\})|(\\{915c12c6-901a-490d-9bfc-20f00d1ad31d\\})|(\\{d3a4aa3e-f74c-4382-876d-825f592f2976\\})|(\\{0ad91ec1-f7c4-4a39-9244-3310e9fdd169\\})|(\\{9c17aa27-63c5-470a-a678-dc899ab67ed3\\})|(\\{c65efef2-9988-48db-9e0a-9ff8164182b6\\})|(\\{d54c5d25-2d51-446d-8d14-18d859e3e89a\\})|(\\{e458f1f1-a331-4486-b157-81cba19f0993\\})|(\\{d2de7e1f-6e51-41d6-ba8a-937f8a5c92ff\\})|(\\{2b08a649-9bea-4dd4-91c8-f53a84d38e19\\})|(\\{312dd57e-a590-4e19-9b26-90e308cfb103\\})|(\\{82ce595a-f9b6-4db8-9c97-b1f1c933418b\\})|(\\{0a2e64f0-ea5a-4fff-902d-530732308d8e\\})|(\\{5fbdc975-17ab-4b4e-90d7-9a64fd832a08\\})|(\\{28820707-54d8-41f0-93e9-a36ffb2a1da6\\})|(\\{64a2aed1-5dcf-4f2b-aad6-9717d23779ec\\})|(\\{ee54794f-cd16-4f7d-a7dd-515a36086f60\\})|(\\{4d381160-b2d5-4718-9a05-fc54d4b307e7\\})|(\\{60393e0e-f039-4b80-bad4-10189053c2ab\\})|(\\{0997b7b2-52d7-4d14-9aa6-d820b2e26310\\})|(\\{8214cbd6-d008-4d16-9381-3ef1e1415665\\})|(\\{6dec3d8d-0527-49a3-8f12-b05f2a8b95b2\\})|(\\{0c0d8d8f-3ae0-4c98-81ac-06453a316d16\\})|(\\{84d5ef02-a283-484a-80da-7087836c74aa\\})|(\\{24413756-2c44-47c5-8bbf-160cb37776d8\\})|(\\{cf6ac458-06e8-45d0-9cbf-ec7fc0eb1710\\})|(\\{263a5792-933a-4de1-820a-d04198e17120\\})|(\\{b5fd7f37-190d-4c0a-b8dd-8b4850c986ac\\})|(\\{cb5ef07b-c2e7-47a6-be81-2ceff8df4dd5\\})|(\\{311b20bc-b498-493c-a5e1-22ec32b0e83c\\})|(\\{b308aead-8bc1-4f37-9324-834b49903df7\\})|(\\{3a26e767-b781-4e21-aaf8-ac813d9edc9f\\}))$/","prefs":[],"schema":1532361925873,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1476553","why":"Third-party websites try to trick users into installing add-ons that inject remote scripts.","name":"Various malicious add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"52842139-3d11-41ac-9d7f-8e51122a3141","last_modified":1532372344457},{"guid":"/^((@FirefoxUpdate)|(@googledashboard)|(@smash_mov)|(@smash_tv)|(@smashdashboard)|(@smashmovs)|(@smashtvs)|(\\{0be01832-7cce-4457-b8ad-73b743914085\\})|(\\{0e1c683e-9f34-45f1-b365-a283befb471a\\})|(\\{0c72a72d-6b2e-4a0e-8a31-16581176052d\\})|(\\{0ccfc208-8441-4c27-b1cb-799accb04908\\})|(\\{0ede8d39-26f2-49c4-8014-dfc484f54a65\\})|(\\{1fc1f8e6-3575-4a6f-a4d1-c4ca1c36bd2a\\})|(\\{3a1d6607-e6a8-4012-9506-f14cd157c171\\})|(\\{03b3ac4d-59a3-4cc6-aa4d-9b39dd8b3196\\})|(\\{3bb6e889-ac7a-46ca-8eed-45ba4fbe75b5\\})|(\\{3c841114-da8c-44ea-8303-78264edfe60b\\})|(\\{3f3bcb3e-dd73-4410-b102-60a87fcb8323\\})|(\\{3f951165-fd85-42ae-96ef-6ff589a1fe72\\})|(\\{04c86cb3-5f52-4083-9e9a-e322dd02181a\\})|(\\{4d8b44ef-9b8b-4d82-b668-a49648d2749d\\})|(\\{4d25d2b4-6ae7-4a66-abc0-c3fca4cdddf6\\})|(\\{5c9a2eca-2126-4a84-82c0-efbf3d989371\\})|(\\{6ecb9f49-90f0-43a1-8f8a-e809ea4f732b\\})|(\\{6fb8289d-c6c8-4fe5-9a92-7dc6cbf35349\\})|(\\{7fea697d-327c-4d20-80d5-813a6fb26d86\\})|(\\{08a3e913-0bbc-42ba-96d7-3fa16aceccbf\\})|(\\{8b04086b-94a5-4161-910b-59e3e31e4364\\})|(\\{08c28c16-9fb6-4b32-9868-db37c1668f94\\})|(\\{8cd69708-2f5e-4282-a94f-3feebc4bce35\\})|(\\{8dc21e24-3883-4d01-b486-ef1d1106fa3d\\})|(\\{8f8cc21a-2097-488f-a213-f5786a2ccbbf\\})|(\\{9c8b93f7-3bf8-4762-b221-40c912268f96\\})|(\\{9ce66491-ef06-4da6-b602-98c2451f6395\\})|(\\{1e1acc1c-8daa-4c2e-ad05-5ef01ae65f1e\\})|(\\{10b0f607-1efa-4762-82a0-e0d9bbae4e48\\})|(\\{24f338d7-b539-49f1-b276-c9edc367a32d\\})|(\\{40c9030f-7a2f-4a58-9d0a-edccd8063218\\})|(\\{41f97b71-c7c6-40b8-83b1-a4dbff76f73d\\})|(\\{42f3034a-0c4a-4f68-a8fd-8a2440e3f011\\})|(\\{52d456e5-245a-4319-b8d2-c14fbc9755f0\\})|(\\{57ea692b-f9fe-42df-bf5e-af6953fba05a\\})|(\\{060c61d8-b48f-465d-aa4b-23325ea757c3\\})|(\\{65c1967c-6a5c-44dd-9637-0d4d8b4c339b\\})|(\\{65d40b64-b52a-46d8-b146-580ff91889cb\\})|(\\{75b7af0d-b4ed-4320-95c8-7ffd8dd2cb7c\\})|(\\{77fe9731-b683-4599-9b06-a5dcea63d432\\})|(\\{84b20d0c-9c87-4340-b4f8-1912df2ae70d\\})|(\\{92b9e511-ac81-4d47-9b8f-f92dc872447e\\})|(\\{95afafef-b580-4f66-a0fe-7f3e74be7507\\})|(\\{116a0754-20eb-4fe5-bd35-575867a0b89e\\})|(\\{118bf5f6-98b1-4543-b133-42fdaf3cbade\\})|(\\{248eacc4-195f-43b2-956c-b9ad1ae67529\\})|(\\{328f931d-83c1-4876-953c-ddc9f63fe3b4\\})|(\\{447fa5d3-1c27-4502-9e13-84452d833b89\\})|(\\{476a1fa9-bce8-4cb4-beff-cb31980cc521\\})|(\\{507a5b13-a8a3-4653-a4a7-9a03099acf48\\})|(\\{531bf931-a8c6-407b-a48f-8a53f43cd461\\})|(\\{544c7f83-ef54-4d17-aa91-274fa27514ef\\})|(\\{546ea388-2839-4215-af49-d7289514a7b1\\})|(\\{635cb424-0cd5-4446-afaf-6265c4b711b5\\})|(\\{654b21c7-6a70-446c-b9ac-8cac9592f4a9\\})|(\\{0668b0a7-7578-4fb3-a4bd-39344222daa3\\})|(\\{944ed336-d750-48f1-b0b5-3c516bfb551c\\})|(\\{1882a9ce-c0e3-4476-8185-f387fe269852\\})|(\\{5571a054-225d-4b65-97f7-3511936b3429\\})|(\\{5921be85-cddd-4aff-9b83-0b317db03fa3\\})|(\\{7082ba5c-f55e-4cd8-88d6-8bc479d3749e\\})|(\\{7322a4cb-641c-4ca2-9d83-8701a639e17a\\})|(\\{90741f13-ab72-443f-a558-167721f64883\\})|(\\{198627a5-4a7b-4857-b074-3040bc8effb8\\})|(\\{5e5b9f44-2416-4669-8362-42a0b3f97868\\})|(\\{824985b9-df2a-401c-9168-749960596007\\})|(\\{4853541f-c9d7-42c5-880f-fd460dbb5d5f\\})|(\\{6e6ff0fd-4ae4-49ae-ac0c-e2527e12359b\\})|(\\{90e8aa72-a7eb-4337-81d4-538b0b09c653\\})|(\\{02e3137a-96a4-433d-bfb2-0aa1cd4aed08\\})|(\\{9e734c09-fcb1-4e3f-acab-04d03625301c\\})|(\\{a6ad792c-69a8-4608-90f0-ff7c958ce508\\})|(\\{a512297e-4d3a-468c-bd1a-f77bd093f925\\})|(\\{a71b10ae-b044-4bf0-877e-c8aa9ad47b42\\})|(\\{a33358ad-a3fa-4ca1-9a49-612d99539263\\})|(\\{a7775382-4399-49bf-9287-11dbdff8f85f\\})|(\\{afa64d19-ddba-4bd5-9d2a-c0ba4b912173\\})|(\\{b4ab1a1d-e137-4c59-94d5-4f509358a81d\\})|(\\{b4ec2f8e-57fd-4607-bf4f-bc159ca87b26\\})|(\\{b06bfc96-c042-4b34-944c-8eb67f35630a\\})|(\\{b9dcdfb0-3420-4616-a4cb-d41b5192ba0c\\})|(\\{b8467ec4-ff65-45f4-b7c5-f58763bf9c94\\})|(\\{b48e4a17-0655-4e8e-a5e2-3040a3d87e55\\})|(\\{b6166509-5fe0-4efd-906e-1e412ff07a04\\})|(\\{bd1f666e-d473-4d13-bc4d-10dde895717e\\})|(\\{be572ad4-5dd7-4b6b-8204-5d655efaf3b3\\})|(\\{bf2a3e58-2536-44d4-b87f-62633256cf65\\})|(\\{bfc5ac5f-80bd-43e5-9acb-f6d447e0d2ce\\})|(\\{bfe3f6c1-c5fe-44af-93b3-576812cb6f1b\\})|(\\{c0b8009b-57dc-45bc-9239-74721640881d\\})|(\\{c1cf1f13-b257-4271-b922-4c57c6b6e047\\})|(\\{c3d61029-c52f-45df-8ec5-a654b228cd48\\})|(\\{c39e7c0b-79d5-4137-bef0-57cdf85c920f\\})|(\\{ce043eac-df8a-48d0-a739-ef7ed9bdf2b5\\})|(\\{cf62e95a-8ded-4c74-b3ac-f5c037880027\\})|(\\{cff02c70-7f07-4592-986f-7748a2abd9e1\\})|(\\{d1b87087-09c5-4e58-b01d-a49d714da2a2\\})|(\\{d14adc78-36bf-4cf0-9679-439e8371d090\\})|(\\{d64c923e-8819-488c-947f-716473d381b2\\})|(\\{d734e7e3-1b8e-42a7-a9b3-11b16c362790\\})|(\\{d147e8c6-c36e-46b1-b567-63a492390f07\\})|(\\{db1a103d-d1bb-4224-a5e1-8d0ec37cff70\\})|(\\{dec15b3e-1d12-4442-930e-3364e206c3c2\\})|(\\{dfa4b2e3-9e07-45a4-a152-cde1e790511d\\})|(\\{dfcda377-b965-4622-a89b-1a243c1cbcaf\\})|(\\{e4c5d262-8ee4-47d3-b096-42b8b04f590d\\})|(\\{e82c0f73-e42c-41dd-a686-0eb4b65b411c\\})|(\\{e60616a9-9b50-49d8-b1e9-cecc10a8f927\\})|(\\{e517649a-ffd7-4b49-81e0-872431898712\\})|(\\{e771e094-3b67-4c33-8647-7b20c87c2183\\})|(\\{eff5951b-b6d4-48f5-94c3-1b0e178dcca5\\})|(\\{f26a8da3-8634-4086-872e-e589cbf03375\\})|(\\{f992ac88-79d3-4960-870e-92c342ed3491\\})|(\\{f4e4fc03-be50-4257-ae99-5cd0bd4ce6d5\\})|(\\{f73636fb-c322-40e1-82fb-e3d7d06d9606\\})|(\\{f5128739-78d5-4ad7-bac7-bd1af1cfb6d1\\})|(\\{fc11e7f0-1c31-4214-a88f-6497c27b6be9\\})|(\\{feedf4f8-08c1-451f-a717-f08233a64ec9\\}))$/","prefs":[],"schema":1532097654002,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1476369","why":"These add-ons contain unwanted features and try to prevent the user from uninstalling themselves.","name":"Smash/Upater (malware) and similar"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"c7d7515d-563f-459f-821c-27d4cf825dbf","last_modified":1532101113096},{"guid":"^((@mixclouddownloader)|(all-down@james\\.burrow)|(d\\.lehr@chello\\.at)|(easy-video-downloader@addonsmash)|(easy-youtube-mp3@james\\.burrow)|(gid@addonsmash)|(gmail_panel@addon_clone)|(guid-reused-by-pk-907175)|(idm@addonsmash)|(image-picka@addonsmash)|(instant-idm@addon\\.host)|(jdm@awesome\\.addons)|(open-in-idm@addonsmash)|(open-in-idm@james\\.burrow)|(open-in-vlc@awesome\\.addons)|(saveimage@addonsmash)|(thundercross@addonsmash)|(vk-download@addon\\.host)|(vk-music-downloader@addonsmash)|(whatsapp_popup@addons\\.clone)|(ytb-down@james\\.burrow)|(ytb-mp3-downloader@james\\.burrow)|(\\{0df8d631-7d88-401e-ba7e-af1425dded8a\\})|(\\{3c74e141-1993-4c04-b755-a66dd491bb47\\})|(\\{5cdd95c7-5d92-40c5-8e2a-8c52c90191d9\\})|(\\{40efedc0-8e48-404a-a779-f4016b25c0e6\\})|(\\{53d605ce-599b-4352-8a06-5e594b3d1822\\})|(\\{3697c1e8-27d7-4c63-a27e-ac16191a1545\\})|(\\{170503FA-3349-4F17-BC86-001888A5C8E2\\})|(\\{649558df-9461-4824-ad18-f2d4d4845ac8\\})|(\\{27875553-afd5-4365-86dc-019bcd60594c\\})|(\\{27875553-afd5-4365-86dc-019bcd60594c\\})|(\\{6e7624fa-7f70-4417-93db-1ec29c023275\\})|(\\{b1aea1f1-6bed-41ef-9679-1dfbd7b2554f\\})|(\\{b9acc029-d62b-4d23-b921-8e7aea34266a\\})|(\\{b9b59e13-4ac5-4eff-8dbe-c345b7619b3c\\})|(\\{b0186d2d-3126-4537-9186-a6f198547901\\})|(\\{b3e8fde8-6d97-4ac3-95e0-57b797f4c56b\\})|(\\{e6a9a96e-4a08-4719-b9bd-0e91c35aaabc\\})|(\\{e69a36e6-ee12-4fe6-87ca-66b77fc0ffbf\\})|(\\{ee3601f1-78ab-48bf-89ae-0cfe4aed1f2e\\})|(\\{f4ce48b3-ad14-4900-86cb-4604474c5b08\\})|(\\{f5c1262d-b1e8-44a4-b820-a834f0f6d605\\}))$","prefs":[],"schema":1531762485603,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1476020","why":"Add-ons repeatedly violated several of review policies.","name":"Several youtube downloading add-ons and others"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0"}],"id":"ae8ae617-590d-430b-86d4-16364372b67f","last_modified":1531762863373},{"guid":"{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}","prefs":[],"schema":1530711142817,"blockID":"i1900","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1472948","why":"This add-on violates data practices outlined in the review policy.","name":"Stylish"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"3.1.1","minVersion":"3.0.0"}],"id":"c635229f-7aa0-44c5-914f-80c590949071","last_modified":1530716488758},{"guid":"/^(contactus@unzipper.com|{72dcff4e-48ce-41d8-a807-823adadbe0c9}|{dc7d2ecc-9cc3-40d7-93ed-ef6f3219bd6f}|{994db3d3-ccfe-449a-81e4-f95e2da76843}|{25aef460-43d5-4bd0-aa3d-0a46a41400e6}|{178e750c-ae27-4868-a229-04951dac57f7})$/","prefs":[],"schema":1528400492025,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1460331","why":"Add-ons change search settings against our policies, affecting core Firefox features. Add-on is also reportedly installed without user consent.","name":"SearchWeb"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"5afea853-d029-43f3-a387-64ce9980742a","last_modified":1528408770328},{"guid":"{38363d75-6591-4e8b-bf01-0270623d1b6c}","prefs":[],"schema":1526326889114,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1461625","why":"This add-on contains abusive functionality.","name":"Photobucket Hotlink Fix"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"0f0764d5-a290-428b-a5b2-3767e1d72c71","last_modified":1526381862851},{"guid":"@vkmad","prefs":[],"schema":1526154098016,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1461410","why":"This add-on includes malicious functionality.","name":"VK Universal Downloader (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"cbfa5303-c1bf-49c8-87d8-259738a20064","last_modified":1526322954850},{"guid":"/^(({41c14ab8-9958-44bf-b74e-af54c1f169a6})|({78054cb2-e3e8-4070-a8ad-3fd69c8e4707})|({0089b179-8f3d-44d9-bb18-582843b0757a})|({f44ddcb4-4cc0-4866-92fa-eefda60c6720})|({1893d673-7953-4870-8069-baac49ce3335})|({fb28cac0-c2aa-4e0c-a614-cf3641196237})|({d7dee150-da14-45ba-afca-02c7a79ad805})|(RandomNameTest@RandomNameTest\\.com )|(corpsearchengine@mail\\.ru)|(support@work\\.org))$/","prefs":[],"schema":1525377099963,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1458330","why":"These are malicious add-ons that inject remote scripts and use deceptive names.","name":"\"Table\" add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"3a123214-b4b6-410c-a061-bbaf0d168d31","last_modified":1525377135149},{"guid":"/((@extcorp\\.[a-z]+)|(@brcorporation\\.com)|(@brmodcorp\\.com)|(@teset\\.com)|(@modext\\.tech)|(@ext?mod\\.net)|(@browcorporation\\.org)|(@omegacorporation\\.org)|(@browmodule\\.com)|(@corpext\\.net)|({6b50ddac-f5e0-4d9e-945b-e4165bfea5d6})|({fab6484f-b8a7-4ba9-a041-0f948518b80c})|({b797035a-7f29-4ff5-bd19-77f1b5e464b1})|({0f612416-5c5a-4ec8-b482-eb546af9cac4}))$/","prefs":[],"schema":1525290095999,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1458330","why":"These are malicious add-ons that inject remote scripts and use deceptive names.","name":"\"Table\" add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"3ab9f100-e253-4080-b3e5-652f842ddb7a","last_modified":1525377099954},{"guid":"/^({b99ae7b1-aabb-4674-ba8f-14ed32d04e76})|({dfa77d38-f67b-4c41-80d5-96470d804d09})$/","prefs":[],"schema":1524146566650,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1455291","why":"These add-ons claim to be the flash plugin.","name":"Flash Plugin (Malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"96b137e6-8cb5-44d6-9a34-4a4a76fb5e38","last_modified":1524147337556},{"guid":"/^({6ecb9f49-90f0-43a1-8f8a-e809ea4f732b})|(@googledashboard)|(@smashdashboard)|(@smash_tv)|(@smash_mov)|(@smashmovs)|(@smashtvs)|(@FirefoxUpdate)|({92b9e511-ac81-4d47-9b8f-f92dc872447e})|({3c841114-da8c-44ea-8303-78264edfe60b})|({116a0754-20eb-4fe5-bd35-575867a0b89e})|({6e6ff0fd-4ae4-49ae-ac0c-e2527e12359b})|({f992ac88-79d3-4960-870e-92c342ed3491})|({6ecb9f49-90f0-43a1-8f8a-e809ea4f732b})|({a512297e-4d3a-468c-bd1a-f77bd093f925})|({08c28c16-9fb6-4b32-9868-db37c1668f94})|({b4ab1a1d-e137-4c59-94d5-4f509358a81d})|({feedf4f8-08c1-451f-a717-f08233a64ec9})$/","prefs":[],"schema":1524139371832,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1454691","why":"This malware prevents itself from getting uninstalled ","name":"Malware"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"feb2d0d7-1b76-4dba-bf84-42873a92af5f","last_modified":1524141477640},{"guid":"{872f20ea-196e-4d11-8835-1cc4c877b1b8}","prefs":[],"schema":1523734896380,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1454413","why":"Extension claims to be Flash Player","name":"Flash Player (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"1e5f5cb2-346c-422a-9aaa-29d8760949d2","last_modified":1523897202689},{"guid":"/(__TEMPLATE__APPLICATION__@ruta-mapa\\.com)|(application-3@findizer\\.fr)|(application2@allo-pages\\.fr)|(application2@bilan-imc\\.fr)|(application2@lettres\\.net)|(application2@search-maps-finder\\.com)|(application-imcpeso@imc-peso\\.com)|(application-meuimc@meu-imc\\.com)|(application-us2@factorlove)|(application-us@misterdirections)|(application-us@yummmi\\.es)|(application@amiouze\\.fr)|(application@astrolignes\\.com)|(application@blotyn\\.com)|(application@bmi-result\\.com)|(application@bmi-tw\\.com)|(application@calcolo-bmi\\.com)|(application@cartes-itineraires\\.com)|(application@convertisseur\\.pro)|(application@de-findizer\\.fr)|(application@de-super-rezepte\\.com)|(application@dermabeauty\\.fr)|(application@dev\\.squel\\.v2)|(application@eu-my-drivingdirections\\.com)|(application@fr-allo-pages\\.fr)|(application@fr-catizz\\.com)|(application@fr-mr-traduction\\.com)|(application@good-recettes\\.com)|(application@horaires\\.voyage)|(application@imc-calcular\\.com)|(application@imc-peso\\.com)|(application@it-mio-percorso\\.com)|(application@iti-maps\\.fr)|(application@itineraire\\.info)|(application@lbc-search\\.com)|(application@les-pages\\.com)|(application@lovincalculator\\.com)|(application@lovintest\\.com)|(application@masowe\\.com)|(application@matchs\\.direct)|(application@mein-bmi\\.com)|(application@mes-resultats\\.com)|(application@mestaf\\.com)|(application@meu-imc\\.com)|(application@mon-calcul-imc\\.fr)|(application@mon-juste-poids\\.com)|(application@mon-trajet\\.com)|(application@my-drivingdirections\\.com)|(application@people-show\\.com)|(application@plans-reduc\\.fr)|(application@point-meteo\\.fr)|(application@poulixo\\.com)|(application@quipage\\.fr)|(application@quizdeamor\\.com)|(application@quizdoamor\\.com)|(application@quotient-retraite\\.fr)|(application@recettes\\.net)|(application@routenplaner-karten\\.com)|(application@ruta-mapa\\.com)|(application@satellite\\.dev\\.squel\\.v2)|(application@search-bilan-imc\\.fr)|(application@search-maps-finder\\.com)|(application@slimness\\.fr)|(application@start-bmi\\.com)|(application@tests-moi\\.com)|(application@tousmesjeux\\.fr)|(application@toutlannuaire\\.fr)|(application@tuto-diy\\.com)|(application@ubersetzung-app\\.com)|(application@uk-cookyummy\\.com)|(application@uk-howlogin\\.me)|(application@uk-myloap\\.com)|(application@voyagevoyage\\.co)|(application@wikimot\\.fr)|(application@www\\.plans-reduc\\.fr)|(application@yummmi\\.es)|(application@yummmies\\.be)|(application@yummmies\\.ch)|(application@yummmies\\.fr)|(application@yummmies\\.lu)|(application@zikplay\\.fr)|(applicationY@search-maps-finder\\.com)|(cmesapps@findizer\\.fr)|(findizer-shopping@jetpack)|(\\{8aaebb36-1488-4022-b7ec-29b790d12c17\\})/","prefs":[],"schema":1523216496621,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1452648","why":"Those add-ons do not provide a real functionality for users, other than silently tracking browsing behavior.","name":"Tracking Add-ons (harmful)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"36f97298-8bef-4372-a548-eb829413bee9","last_modified":1523286321447},{"guid":"adbeaver@adbeaver.org","prefs":[],"schema":1521630548030,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1445031","why":"This add-on generates numerous errors when loading Facebook, caused by ad injection included in it. Users who want to continue using this add-on can enable it in the Add-ons Manager.","name":"AdBeaver"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0"}],"id":"baf7f735-d6b6-410a-8cc8-25c60f7c57e2","last_modified":1522103097333},{"guid":"{44685ba6-68b3-4895-879e-4efa29dfb578}","prefs":[],"schema":1521565140013,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1447042","why":"This add-on impersonates a Flash tool and runs remote code on users' systems.","name":"FF Flash Manager"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"547037f2-97ae-435a-863c-efd7532668cd","last_modified":1521630548023},{"guid":"/^.*extension.*@asdf\\.pl$/","prefs":[],"schema":1520451695869,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1444037","why":"These add-ons are using deceptive names and taking over Facebook accounts to post spam content.","name":"Facebook spammers"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"3d55fab0-ec1a-4bca-84c9-3b74f5d01509","last_modified":1520527480321},{"guid":"/^(addon@fasterweb\\.com|\\{5f398d3f-25db-47f5-b422-aa2364ff6c0b\\}|addon@fasterp\\.com|addon@calculator)$/","prefs":[],"schema":1520338910918,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1443478","why":"These are malicious add-ons that use deceptive names and run remote scripts.","name":"FasterWeb add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"f58729ec-f93c-41d9-870d-dd9c9fd811b6","last_modified":1520358450708},{"guid":"{42baa93e-0cff-4289-b79e-6ae88df668c4}","prefs":[],"schema":1520336325565,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1443196","why":"The add-on claims to be \"Adobe Shockwave Flash Player\"","name":"Adobe Shockwave Flash Player (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"0cd723fe-d33d-43a0-b84f-7a3cad253212","last_modified":1520338780397},{"guid":"/{0c9970a2-6874-483b-a486-2296cfe251c2}|{01c9a4a4-06dd-426b-9500-2ea6fe841b88}|{1c981c7c-30e0-4ed2-955d-6b370e0a9d19}|{2aa275f8-fabc-4766-95b2-ecfc73db310b}|{2cac0be1-10a2-4a0d-b8c5-787837ea5955}|{2eb66f6c-94b3-44f5-9de2-22371236ec99}|{2f8aade6-8717-4277-b8b1-55172d364903}|{3c27c34f-8775-491a-a1c9-fcb15beb26d3}|{3f4dea3e-dbfc-428f-a88b-36908c459e20}|{3f4191fa-8f16-47d2-9414-36bfc9e0c2bf}|{4c140bc5-c2ad-41c3-a407-749473530904}|{05a21129-af2a-464c-809f-f2df4addf209}|{5da81d3d-5db1-432a-affc-4a2fe9a70749}|{5f4e63e4-351f-4a21-a8e5-e50dc72b5566}|{7c1df23b-1fd8-42b9-8752-71fff2b979de}|{7d5e24a1-7bef-4d09-a952-b9519ec00d20}|{7d932012-b4dd-42cc-8a78-b15ca82d0e61}|{7f8bc48d-1c7c-41a0-8534-54adc079338f}|{8a61507d-dc2f-4507-a9b7-7e33b8cbc31b}|{09c8fa16-4eec-4f78-b19d-9b24b1b57e1e}|{9ce2a636-0e49-4b8e-ad17-d0c156c963b0}|{11df9391-dba5-4fe2-bd48-37a9182b796d}|{23c65153-c21e-430a-a2dc-0793410a870d}|{36a4269e-4eef-4538-baea-9dafbf6a8e2f}|{37f8e483-c782-40ed-82e9-36f101b9e41f}|{63df223d-51cf-4f76-aad8-bbc94c895ed2}|{72c1ca96-c05d-46a7-bce1-c507ec3db4ea}|{76ce213c-8e57-4a14-b60a-67a5519bd7a7}|{79db6c96-d65a-4a64-a892-3d26bd02d2d9}|{81ac42f3-3d17-4cff-85af-8b7f89c8826b}|{83d38ac3-121b-4f28-bf9c-1220bd3c643b}|{86d98522-5d42-41d5-83c2-fc57f260a3d9}|{0111c475-01e6-42ea-a9b4-27bed9eb6092}|{214cb48a-ce31-4e48-82cf-a55061f1b766}|{216e0bcc-8a23-4069-8b63-d9528b437258}|{226b0fe6-f80f-48f1-9d8d-0b7a1a04e537}|{302ef84b-2feb-460e-85ca-f5397a77aa6a}|{408a506b-2336-4671-a490-83a1094b4097}|{419be4e9-c981-478e-baa0-937cf1eea1e8}|{0432b92a-bfcf-41b9-b5f0-df9629feece1}|{449e185a-dd91-4f7b-a23a-bbf6c1ca9435}|{591d1b73-5eae-47f4-a41f-8081d58d49bf}|{869b5825-e344-4375-839b-085d3c09ab9f}|{919fed43-3961-48d9-b0ef-893054f4f6f1}|{01166e60-d740-440c-b640-6bf964504b3c}|{2134e327-8060-441c-ba68-b167b82ff5bc}|{02328ee7-a82b-4983-a5f7-d0fc353698f0}|{6072a2a8-f1bc-4c9c-b836-7ac53e3f51e4}|{28044ca8-8e90-435e-bc63-a757af2fb6be}|{28092fa3-9c52-4a41-996d-c43e249c5f08}|{31680d42-c80d-4f8a-86d3-cd4930620369}|{92111c8d-0850-4606-904a-783d273a2059}|{446122cd-cd92-4d0c-9426-4ee0d28f6dca}|{829827cd-03be-4fed-af96-dd5997806fb4}|{4479446e-40f3-48af-ab85-7e3bb4468227}|{9263519f-ca57-4178-b743-2553a40a4bf1}|{71639610-9cc3-47e0-86ed-d5b99eaa41d5}|{84406197-6d37-437c-8d82-ae624b857355}|{93017064-dfd4-425e-a700-353f332ede37}|{a0ab16af-3384-4dbe-8722-476ce3947873}|{a0c54bd8-7817-4a40-b657-6dc7d59bd961}|{a2de96bc-e77f-4805-92c0-95c9a2023c6a}|{a3fbc8be-dac2-4971-b76a-908464cfa0e0}|{a42e5d48-6175-49e3-9e40-0188cde9c5c6}|{a893296e-5f54-43f9-a849-f12dcdee2c98}|{ac296b47-7c03-486f-a1d6-c48b24419749}|{b26bf964-7aa6-44f4-a2a9-d55af4b4eec0}|{be981b5e-1d9d-40dc-bd4f-47a7a027611c}|{be37931c-af60-4337-8708-63889f36445d}|{bfd92dfd-b293-4828-90c1-66af2ac688e6}|{c5cf4d08-0a33-4aa3-a40d-d4911bcc1da7}|{c488a8f5-ea3d-408d-809e-44e82c06ad9d}|{c661c2dc-00f9-4dc1-a9f6-bb2b7e1a4f8d}|{cd28aa38-d2f1-45a3-96c3-6cfd4702ef51}|{cd89045b-2e06-46bb-9e34-48e8799e5ef2}|{cf9d96ff-5997-439a-b32b-98214c621eee}|{d14acee6-f32b-4aa3-a802-6616003fc6a8}|{d97223b8-44e5-46c7-8ab5-e1d8986daf44}|{ddae89bd-6793-45d8-8ec9-7f4fb7212378}|{de3b1909-d4da-45e9-8da5-7d36a30e2fc6}|{df09f268-3c92-49db-8c31-6a25a6643896}|{e5bc3951-c837-4c98-9643-3c113fc8cf5e}|{e9ccb1f2-a8ba-4346-b43b-0d5582bce414}|{e341ed12-a703-47fe-b8dd-5948c38070e4}|{e2139287-2b0d-4f54-b3b1-c9a06c597223}|{ed352072-ddf0-4cb4-9cb6-d8aa3741c2de}|{f0b809eb-be22-432f-b26f-b1cadd1755b9}|{f1bce8e4-9936-495b-bf48-52850c7250ab}|{f01c3add-dc6d-4f35-a498-6b4279aa2ffa}|{f9e1ad25-5961-4cc5-8d66-5496c438a125}|{f4262989-6de0-4604-918f-663b85fad605}|{fc0d55bd-3c50-4139-9409-7df7c1114a9d}/","prefs":[],"schema":1519766961483,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1439702","why":"This malicious add-on claims to be a Firefox \"helper\" or \"updater\" or similar.","name":"FF updater (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"48b14881-5f6b-4e48-afc5-3d9a7fae26a3","last_modified":1519826648080},{"guid":"{44e4b2cf-77ba-4f76-aca7-f3fcbc2dda2f} ","prefs":[],"schema":1519414957616,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1440821","why":"This is a malicious add-on that uses a deceptive name and runs remote code.","name":"AntiVirus for Firefox"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"2447476f-043b-4d0b-9d3c-8e859c97d950","last_modified":1519429178266},{"guid":"{f3c31b34-862c-4bc8-a98f-910cc6314a86}","prefs":[],"schema":1519242096699,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1440736","why":"This is a malicious add-on that is masked as an official Adobe Updater and runs malicious code.","name":"Adobe Updater (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"adfd98ef-cebc-406b-b1e0-61bd4c71e4b1","last_modified":1519409417397},{"guid":"/^(\\{fd0c36fa-6a29-4246-810b-0bb4800019cb\\}|\\{b9c1e5bf-6585-4766-93fc-26313ac59999\\}|\\{3de25fff-25e8-40e9-9ad9-fdb3b38bb2f4\\})$/","prefs":[],"schema":1519069296530,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1439432","why":"These are malicious add-ons that are masked as an official Adobe Updater and run malicious code.","name":"Adobe Updater"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"4e28ba5c-af62-4e53-a7a1-d33334571cf8","last_modified":1519078890592},{"guid":"/^(\\{01c9a4a4-06dd-426b-9500-2ea6fe841b88\\}|{5e024309-042c-4b9d-a634-5d92cf9c7514\\}|{f4262989-6de0-4604-918f-663b85fad605\\}|{e341ed12-a703-47fe-b8dd-5948c38070e4\\}|{cd89045b-2e06-46bb-9e34-48e8799e5ef2\\}|{ac296b47-7c03-486f-a1d6-c48b24419749\\}|{5da81d3d-5db1-432a-affc-4a2fe9a70749\\}|{df09f268-3c92-49db-8c31-6a25a6643896\\}|{81ac42f3-3d17-4cff-85af-8b7f89c8826b\\}|{09c8fa16-4eec-4f78-b19d-9b24b1b57e1e\\}|{71639610-9cc3-47e0-86ed-d5b99eaa41d5\\}|{83d38ac3-121b-4f28-bf9c-1220bd3c643b\\}|{7f8bc48d-1c7c-41a0-8534-54adc079338f\\})$/","prefs":[],"schema":1518550894975,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1438028","why":"These are malicious add-ons that inject remote scripts into popular websites.","name":"Page Marker add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"cc5848e8-23d5-4655-b45c-dc239839b74e","last_modified":1518640450735},{"guid":"/^(https|youtube)@vietbacsecurity\\.com$/","prefs":[],"schema":1517909997354,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1435974","why":"These add-ons contain malicious functionality, violating the users privacy and security.","name":"HTTPS and Youtube downloader (Malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"646e2384-f894-41bf-b7fc-8879e0095109","last_modified":1517910100624},{"guid":"{ed352072-ddf0-4cb4-9cb6-d8aa3741c2de}","prefs":[],"schema":1517514097126,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1434893","why":"This is a malicious add-on that injects remote scripts into popular pages while pretending to do something else.","name":"Image previewer"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"2104a522-bb2f-4b04-ad0d-b0c571644552","last_modified":1517577111194},{"guid":"/^(\\{0b24cf69-02b8-407d-83db-e7af04fc1f3e\\})|(\\{6feed48d-41d4-49b8-b7d6-ef78cc7a7cd7\\})| (\\{8a0699a0-09c3-4cf1-b38d-fec25441650c\\})$/","prefs":[],"schema":1517341295286,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1434759","why":"These add-ons use remote scripts to alter popular sites like Google or Amazon.","name":"Malicious remote script add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"32ffc62d-40c4-43ac-aa3f-7240978d0ad0","last_modified":1517439279474},{"guid":"/^({be5d0c88-571b-4d01-a27a-cc2d2b75868c})|({3908d078-e1db-40bf-9567-5845aa77b833})|({5b620343-cd69-49b8-a7ba-f9d499ee5d3d})|({6eee2d17-f932-4a43-a254-9e2223be8f32})|({e05ba06a-6d6a-4c51-b8fc-60b461ffecaf})|({a5808da1-5b4f-42f2-b030-161fd11a36f7})|({d355bee9-07f0-47d3-8de6-59b8eecba57b})|({a1f8e136-bce5-4fd3-9ed1-f260703a5582})$/","prefs":[],"schema":1517260691761,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.\n","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"70f37cc7-9f8a-4d0f-a881-f0c56934fa75","last_modified":1517260722621},{"guid":"/^({d78d27f4-9716-4f13-a8b6-842c455d6a46})|({bd5ba448-b096-4bd0-9582-eb7a5c9c0948})|({0b24cf69-02b8-407d-83db-e7af04fc1f3e})|({e08d85c5-4c0f-4ce3-9194-760187ce93ba})|({1c7d6d9e-325a-4260-8213-82d51277fc31})|({8a0699a0-09c3-4cf1-b38d-fec25441650c})|({1e68848a-2bb7-425c-81a2-524ab93763eb})$/","prefs":[],"schema":1517168490224,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"805ee80e-0929-4c92-93ed-062b98053f28","last_modified":1517260691755},{"guid":"/^({abec23c3-478f-4a5b-8a38-68ccd500ec42}|{a83c1cbb-7a41-41e7-a2ae-58efcb4dc2e4}|{62237447-e365-487e-8fc3-64ddf37bdaed}|{b12cfdc7-3c69-43cb-a3fb-38981b68a087}|{1a927d5b-42e7-4407-828a-fdc441d0daae}|{dd1cb0ec-be2a-432b-9c90-d64c824ac371}|{82c8ced2-e08c-4d6c-a12b-3e8227d7fc2a}|{87c552f9-7dbb-421b-8deb-571d4a2d7a21})$/","prefs":[],"schema":1516828883529,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"c92f2a05-73eb-454e-9583-f6d2382d8bca","last_modified":1516829074251},{"guid":"/^({618baeb9-e694-4c7b-9328-69f35b6a8839}|{b91fcda4-88b0-4a10-9015-9365e5340563}|{04150f98-2d7c-4ae2-8979-f5baa198a577}|{4b1050c6-9139-4126-9331-30a836e75db9}|{1e6f5a54-2c4f-4597-aa9e-3e278c617d38}|{e73854da-9503-423b-ab27-fafea2fbf443}|{a2427e23-d349-4b25-b5b8-46960b218079}|{f92c1155-97b3-40f4-9d5b-7efa897524bb}|{c8e14311-4b2d-4eb0-9a6b-062c6912f50e}|{45621564-b408-4c29-8515-4cf1f26e4bc3}|{27380afd-f42a-4c25-b57d-b9012e0d5d48})$/","prefs":[],"schema":1516828883529,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"2d4fe65b-6c02-4461-baa8-dda52e688cf6","last_modified":1516829040469},{"guid":"/^({4dac7c77-e117-4cae-a9f0-6bd89e9e26ab}|{cc689da4-203f-4a0c-a7a6-a00a5abe74c5}|{0eb4672d-58a6-4230-b74c-50ca3716c4b0}|{06a71249-ef35-4f61-b2c8-85c3c6ee5617}|{5280684d-f769-43c9-8eaa-fb04f7de9199}|{c2341a34-a3a0-4234-90cf-74df1db0aa49}|{85e31e7e-3e3a-42d3-9b7b-0a2ff1818b33}|{b5a35d05-fa28-41b5-ae22-db1665f93f6b}|{1bd8ba17-b3ed-412e-88db-35bc4d8771d7}|{a18087bb-4980-4349-898c-ca1b7a0e59cd}|{488e190b-d1f6-4de8-bffb-0c90cc805b62})$/","prefs":[],"schema":1516828883529,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"9a3fd797-0ab8-4286-9a1b-2b6c97f9075b","last_modified":1516829006347},{"guid":"/^({f6df4ef7-14bd-43b5-90c9-7bd02943789c}|{ccb7b5d6-a567-40a2-9686-a097a8b583dd}|{9b8df895-fcdd-452a-8c46-da5be345b5bc}|{5cf77367-b141-4ba4-ac2a-5b2ca3728e81}|{ada56fe6-f6df-4517-9ed0-b301686a34cc}|{95c7ae97-c87e-4827-a2b7-7b9934d7d642}|{e7b978ae-ffc2-4998-a99d-0f4e2f24da82}|{115a8321-4414-4f4c-aee6-9f812121b446}|{bf153de7-cdf2-4554-af46-29dabfb2aa2d}|{179710ba-0561-4551-8e8d-1809422cb09f}|{9d592fd5-e655-461a-9b28-9eba85d4c97f})$/","prefs":[],"schema":1516828883529,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"aae78cd5-6b26-472e-ab2d-db4105911250","last_modified":1516828973824},{"guid":"/^({30972e0a-f613-4c46-8c87-2e59878e7180}|{0599211f-6314-4bf9-854b-84cb18da97f8}|{4414af84-1e1f-449b-ac85-b79f812eb69b}|{2a8bec00-0ab0-4b4d-bd3d-4f59eada8fd8}|{bea8866f-01f8-49e9-92cd-61e96c05d288}|{046258c9-75c5-429d-8d5b-386cfbadc39d}|{c5d359ff-ae01-4f67-a4f7-bf234b5afd6e}|{fdc0601f-1fbb-40a5-84e1-8bbe96b22502}|{85349ea6-2b5d-496a-9379-d4be82c2c13d}|{640c40e5-a881-4d16-a4d0-6aa788399dd2}|{d42328e1-9749-46ba-b35c-cce85ddd4ace})$/","prefs":[],"schema":1516828883529,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"750aa293-3742-46b5-8761-51536afecaef","last_modified":1516828938683},{"guid":"/^({d03b6b0f-4d44-4666-a6d6-f16ad9483593}|{767d394a-aa77-40c9-9365-c1916b4a2f84}|{a0ce2605-b5fc-4265-aa65-863354e85058}|{b7f366fa-6c66-46bf-8df2-797c5e52859f}|{4ad16913-e5cb-4292-974c-d557ef5ec5bb}|{3c3ef2a3-0440-4e77-9e3c-1ca8d48f895c}|{543f7503-3620-4f41-8f9e-c258fdff07e9}|{98363f8b-d070-47b6-acc6-65b80acac4f3}|{5af74f5a-652b-4b83-a2a9-f3d21c3c0010}|{484e0ba4-a20b-4404-bb1b-b93473782ae0}|{b99847d6-c932-4b52-9650-af83c9dae649})$/","prefs":[],"schema":1516828883529,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"a29aed6f-6546-4fa2-8131-df5c9a5427af","last_modified":1516828911059},{"guid":"/^({2bb68b03-b528-4133-9fc4-4980fbb4e449}|{231e58ac-0f3c-460b-bb08-0e589360bec7}|{a506c5af-0f95-4107-86f8-3de05e2794c9}|{8886a262-1c25-490b-b797-2e750dd9f36b}|{65072bef-041f-492e-8a51-acca2aaeac70}|{6fa41039-572b-44a4-acd4-01fdaebf608d}|{87ba49bd-daba-4071-aedf-4f32a7e63dbe}|{95d58338-ba6a-40c8-93fd-05a34731dc0e}|{4cbef3f0-4205-4165-8871-2844f9737602}|{1855d130-4893-4c79-b4aa-cbdf6fee86d3}|{87dcb9bf-3a3e-4b93-9c85-ba750a55831a})$/","prefs":[],"schema":1516822896448,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are malicious add-ons that automatically close the Add-ons Manager.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"5c092b0d-7205-43a1-aa75-b7a42372fb52","last_modified":1516828883523},{"guid":"/^({fce89242-66d3-4946-9ed0-e66078f172fc})|({0c4df994-4f4a-4646-ae5d-8936be8a4188})|({6cee30bc-a27c-43ea-ac72-302862db62b2})|({e08ebf0b-431d-4ed1-88bb-02e5db8b9443})$/","prefs":[],"schema":1516650096284,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1432560","why":"These are malicious add-ons that make it hard for the user to be removed.","name":"FF AntiVir Monitoring"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"9dfeee42-e6a8-49e0-8979-0648f7368239","last_modified":1516744119329},{"guid":"/^(\\{1490068c-d8b7-4bd2-9621-a648942b312c\\})|(\\{d47ebc8a-c1ea-4a42-9ca3-f723fff034bd\\})|(\\{83d6f65c-7fc0-47d0-9864-a488bfcaa376\\})|(\\{e804fa4c-08e0-4dae-a237-8680074eba07\\})|(\\{ea618d26-780e-4f0f-91fd-2a6911064204\\})|(\\{ce93dcc7-f911-4098-8238-7f023dcdfd0d\\})|(\\{7eaf96aa-d4e7-41b0-9f12-775c2ac7f7c0\\})|(\\{b019c485-2a48-4f5b-be13-a7af94bc1a3e\\})|(\\{9b8a3057-8bf4-4a9e-b94b-867e4e71a50c\\})|(\\{eb3ebb14-6ced-4f60-9800-85c3de3680a4\\})|(\\{01f409a5-d617-47be-a574-d54325fe05d1\\})$/","prefs":[],"schema":1516394914836,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are a set of malicious add-ons that block the add-ons manager tab from opening so they can't be uninstalled.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"5bf72f70-a611-4845-af3f-d4dabe8862b6","last_modified":1516394982586},{"guid":"/^(\\{ac06c6b2-3fd6-45ee-9237-6235aa347215\\})|(\\{d461cc1b-8a36-4ff0-b330-1824c148f326\\})|(\\{d1ab5ebd-9505-481d-a6cd-6b9db8d65977\\})|(\\{07953f60-447e-4f53-a5ef-ed060487f616\\})|(\\{2d3c5a5a-8e6f-4762-8aff-b24953fe1cc9\\})|(\\{f82b3ad5-e590-4286-891f-05adf5028d2f\\})|(\\{f96245ad-3bb0-46c5-8ca9-2917d69aa6ca\\})|(\\{2f53e091-4b16-4b60-9cae-69d0c55b2e78\\})|(\\{18868c3a-a209-41a6-855d-f99f782d1606\\})|(\\{47352fbf-80d9-4b70-9398-fb7bffa3da53\\})$/","prefs":[],"schema":1516311993443,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1431748","why":"These are a set of malicious add-ons that block the add-ons manager tab from opening so they can't be uninstalled.","name":"FF Tool"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"4ca8206f-bc2a-4428-9439-7f3142dc08db","last_modified":1516394914828},{"guid":"{5b0f6d3c-10fd-414c-a135-dffd26d7de0f}","prefs":[],"schema":1516131689499,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1430577","why":"This is a malicious add-on that executes remote scripts, redirects popular search URLs and tracks users.","name":"P Birthday"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"8088b39a-3e6d-4a17-a22f-3f95c0464bd6","last_modified":1516303320468},{"guid":"{1490068c-d8b7-4bd2-9621-a648942b312c}","prefs":[],"schema":1515267698296,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1428754","why":"This add-on is using a deceptive name and performing unwanted actions on users' systems.","name":"FF Safe Helper"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"674b6e19-f087-4706-a91d-1e723ed6f79e","last_modified":1515433728497},{"guid":"{dfa727cb-0246-4c5a-843a-e4a8592cc7b9}","prefs":[],"schema":1514922095288,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1426582","why":"Version 2.0.0 shipped with a hidden coin miner, which degrades performance in users who have it enabled. Version 1.2.3 currently available on AMO is not affected.","name":"Open With Adobe PDF Reader 2.0.0"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.0.0","minVersion":"2.0.0"}],"id":"455772a3-8360-4f5a-9a5f-a45b904d0b51","last_modified":1515007270887},{"guid":"{d03b6b0f-4d44-4666-a6d6-f16ad9483593}","prefs":[],"schema":1513366896461,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1425581","why":"This is a malicious add-on posing as a legitimate update.","name":"FF Guard Tool (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"10d9ce89-b8d4-4b53-b3d7-ecd192681f4e","last_modified":1513376470395},{"guid":"{7e907a15-0a4c-4ff4-b64f-5eeb8f841349}","prefs":[],"schema":1510083698490,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1411885","why":"This is a malicious add-on posing as a legitimate update.","name":"Manual Update"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"f7569261-f575-4719-8202-552b20d013b0","last_modified":1510168860382},{"guid":"{3602008d-8195-4860-965a-d01ac4f9ca96}","prefs":[],"schema":1509120801051,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1411885","why":"This is a malicious add-on posing as a legitimate antivirus.\n","name":"Manual Antivirus"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"28c805a9-e692-4ef8-b3ae-14e085c19ecd","last_modified":1509120934909},{"guid":"{87010166-e3d0-4db5-a394-0517917201df}","prefs":[],"schema":1509120801051,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1411885","why":"This is a malicious add-on posing as a legitimate antivirus.\n","name":"Manual Antivirus"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"84dd8a02-c879-4477-8ea7-bf2f225b0940","last_modified":1509120881470},{"guid":"{8ab60777-e899-475d-9a4f-5f2ee02c7ea4}","prefs":[],"schema":1509120801051,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1411885","why":"This is a malicious add-on posing as a legitimate antivirus.\n","name":"Manual Antivirus"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"ccebab59-7190-4258-8faa-a0b752dd5301","last_modified":1509120831329},{"guid":"{368eb817-31b4-4be9-a761-b67598faf9fa}","prefs":[],"schema":1509046897080,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1411885","why":"This is a malicious add-on posing as a legitimate antivirus.","name":"Manual Antivirus"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"9abc7502-bd6f-40d7-b035-abe721345360","last_modified":1509120801043},{"guid":"fi@dictionaries.addons.mozilla.org","prefs":[],"schema":1508701297180,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1407147","why":"This add-on is causing frequent crashes in Firefox 56.","name":"Finnish spellchecker"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"2.1.0","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"56.0a1"}]}],"id":"22431713-a93b-40f4-8264-0b341b5f6454","last_modified":1508856488536},{"guid":"firefox@mega.co.nz","prefs":[],"schema":1506800496781,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1404290","why":"Add-on is causing tabs to load blank.","name":"Mega.nz"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"3.16.1","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"56.0a1"}]}],"id":"a84e6eba-4bc1-4416-b481-9b837d39f9f0","last_modified":1506963401477},{"guid":"@68eba425-7a05-4d62-82b1-1d6d5a51716b","prefs":[],"schema":1505072496256,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1398905","why":"Misleads users into thinking this is a security and privacy tool (also distributed on a site that makes it look like an official Mozilla product).","name":"SearchAssist Incognito"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0"}],"id":"595e0e53-b76b-4188-a160-66f29c636094","last_modified":1505211411253},{"guid":"{efda3854-2bd9-45a1-9766-49d7ff18931d}","prefs":[],"schema":1503344500341,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1392625","why":"Add-on injects remote code into privileged scope.","name":"Smart Referer"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"0.8.17.2","minVersion":"0"}],"id":"d83011de-67a4-479b-a778-916a7232095b","last_modified":1503411102265},{"guid":"@H99KV4DO-UCCF-9PFO-9ZLK-8RRP4FVOKD9O","prefs":[],"schema":1502483549048,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1340877","why":"This is a malicious add-on that is being installed silently.","name":"FF Adr (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"5df16afc-c804-43c9-9de5-f1835403e5fb","last_modified":1502483601731},{"guid":"@DA3566E2-F709-11E5-8E87-A604BC8E7F8B","prefs":[],"schema":1502480491460,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1340877","why":"This is a malicious add-on that is being installed silently into users' systems.","name":"SimilarWeb (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"0a47a2f7-f07c-489b-bd39-88122a2dfe6a","last_modified":1502483549043},{"guid":"xdict@www.iciba.com","prefs":[],"schema":1501098091500,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1384497","why":"This add-on has been discontinued and is creating a prompt loop that blocks users from using Firefox.","name":"PowerWord Grab Word Extension"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.3.1","minVersion":"0"}],"id":"28736359-700e-4b61-9c50-0b533a6bac55","last_modified":1501187580933},{"guid":"{3B4DE07A-DE43-4DBC-873F-05835FF67DCE}","prefs":[],"schema":1496950889322,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1371392","why":"This add-on performs hidden actions that cause the users' systems to act as a botnet.","name":"The Safe Surfing (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"510bbd9b-b883-4837-90ab-8e353e27e1be","last_modified":1496951442076},{"guid":"WebProtection@360safe.com","prefs":[],"schema":1496846005095,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1336635","who":"All users of Firefox 52 and above who have this add-on installed.","why":"This add-on breaks the Firefox user interface starting with version 52.","name":"360 Internet Protection versions 5.0.0.1009 and lower"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"5.0.0.1009","minVersion":"0"}],"id":"e16408c3-4e08-47fd-85a9-3cbbce534e95","last_modified":1496849965060},{"guid":"html5@encoding","prefs":[],"schema":1496788543767,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1370847","who":"All users.","why":"This malicious add-on targets a certain user group and spies on them.","name":"HTML5 Encoding (Malicious), all versions"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"c806b01c-3352-4083-afd9-9a8ab6e00b19","last_modified":1496833261424},{"guid":"/^({95E84BD3-3604-4AAC-B2CA-D9AC3E55B64B}|{E3605470-291B-44EB-8648-745EE356599A}|{95E5E0AD-65F9-4FFC-A2A2-0008DCF6ED25}|{FF20459C-DA6E-41A7-80BC-8F4FEFD9C575}|{6E727987-C8EA-44DA-8749-310C0FBE3C3E}|{12E8A6C2-B125-479F-AB3C-13B8757C7F04}|{EB6628CF-0675-4DAE-95CE-EFFA23169743})$/","prefs":[],"schema":1494022576295,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1362585","why":"All of these add-ons have been identified as malware, and are being installed in Firefox globally, most likely via a malicious application installer.","name":"Malicious globally-installed add-ons"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"3fd71895-7fc6-4f3f-aa22-1cbb0c5fd922","last_modified":1494024191520},{"guid":"@safesearchscoutee","prefs":[],"schema":1494013289942,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1362553","why":"This add-on intercepts queries sent to search engines and replaces them with its own, without user consent.","name":"SafeSearch Incognito (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0"}],"id":"edad04eb-ea16-42f3-a4a7-20dded33cc37","last_modified":1494022568654},{"guid":"{0D2172E4-C5AE-465A-B80D-53A840275B5E}","prefs":[],"schema":1493332768943,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1359473","who":"All users of Thunderbird 52 and above, using a version of the Priority Switcher add-on before version 0.7","why":"This add-on is causing recurring startup crashes in Thunderbird.","name":"Priority Switcher for Thunderbird before version 0.7"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"0.6.999","minVersion":"0","targetApplication":[{"guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*","minVersion":"52.0a1"}]}],"id":"8c8af415-46db-40be-a66e-38e3762493bd","last_modified":1493332986987},{"guid":"msktbird@mcafee.com","prefs":[],"schema":1493150718059,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1354912","why":"These versions of this add-on are known to cause frequent crashes in Thunderbird.","name":"McAfee Anti-Spam Thunderbird Extension 2.0 and lower"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"2.0","minVersion":"0","targetApplication":[{"guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*","minVersion":"0"}]}],"id":"9e86d1ff-727a-45e3-9fb6-17f32666daf2","last_modified":1493332747360},{"guid":"/^(\\{11112503-5e91-4299-bf4b-f8c07811aa50\\})|(\\{501815af-725e-45be-b0f2-8f36f5617afc\\})$/","prefs":[],"schema":1491421290217,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1354045","why":"This add-on steals user credentials for popular websites from Facebook.","name":"Flash Player Updater (Malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"c142360c-4f93-467e-9717-b638aa085d95","last_modified":1491472107658},{"guid":"fr@fbt.ovh","prefs":[],"schema":1490898754477,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1351689","why":"Scam add-on that silently steals user credentials of popular websites","name":"Adobe Flash Player (Malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"0f8344d0-8211-49a1-81be-c0084b3da9b1","last_modified":1490898787752},{"guid":"/^\\{(9321F452-96D5-11E6-BC3E-3769C7AD2208)|({18ED1ECA-96D3-11E6-A373-BD66C7AD2208})\\}$/","prefs":[],"schema":1490872899765,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1351710","why":"These add-ons modify websites and add deceptive or abusive content","name":"Scamming add-ons (Malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"d6425f24-8c9e-4c0a-89b4-6890fc68d5c9","last_modified":1490898748265},{"guid":"/^(test2@test\\.com)|(test3@test\\.com)|(mozilla_cc2\\.2@internetdownloadmanager\\.com)$/","prefs":[],"schema":1490557289817,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1351095","who":"All users who have any of these add-ons installed.","why":"Old versions of the Internet Download Manager Integration add-on cause performance and stability problems in Firefox 53 and above.","name":"IDM Integration forks"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"53.0a1"}]}],"id":"9085fdba-8498-46a9-b9fd-4c7343a15c62","last_modified":1490653926191},{"guid":"mozilla_cc2@internetdownloadmanager.com","prefs":[],"schema":1489007018796,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1338832","who":"All users who have these versions of the add-on installed.","why":"Old versions of the Internet Download Manager Integration add-on cause performance and stability problems in Firefox 53 and above.","name":"IDM Integration"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"6.26.11","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"53.0a1"}]}],"id":"d33f6d48-a555-49dd-96ff-8d75473403a8","last_modified":1489514734167},{"guid":"InternetProtection@360safe.com","prefs":[],"schema":1489006712382,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1336635","who":"All Firefox users who have this add-on installed.","why":"This add-on breaks the Firefox user interface starting with version 52.","name":"360 Internet Protection"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"5.0.0.1002","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"52.0a1"}]}],"id":"89a61123-79a2-45d1-aec2-97afca0863eb","last_modified":1489006816246},{"guid":"{95E84BD3-3604-4AAC-B2CA-D9AC3E55B64B}","prefs":[],"schema":1487179851382,"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1338690","who":"All users who have this add-on installed.","why":"This is a malicious add-on that is silently installed in users' systems.","name":"youtube adblock (malware)"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"04b25e3d-a725-493e-be07-cbd74fb37ea7","last_modified":1487288975999},{"guid":"ext@alibonus.com","prefs":[],"schema":1485297431051,"blockID":"i1524","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1333471","who":"All Firefox users who have these versions installed.","why":"Versions 1.20.9 and lower of this add-on contain critical security issues.","name":"Alibonus 1.20.9 and lower","created":"2017-01-24T22:45:39Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.20.9","minVersion":"0","targetApplication":[]}],"id":"a015d5a4-9184-95db-0c74-9262af2332fa","last_modified":1485301116629},{"guid":"{a0d7ccb3-214d-498b-b4aa-0e8fda9a7bf7}","prefs":[],"schema":1485295513652,"blockID":"i1523","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1314332","who":"All Firefox users who have these versions of the Web of Trust add-on installed.","why":"Versions 20170120 and lower of the Web of Trust add-on send excessive user data to its service, which has been reportedly shared with third parties without sufficient sanitization. These versions are also affected by a vulnerability that could lead to unwanted remote code execution.","name":"Web of Trust 20170120 and lower","created":"2017-01-24T22:01:08Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"20170120","minVersion":"0","targetApplication":[]}],"id":"2224c139-9b98-0900-61c1-04031de11ad3","last_modified":1485297214072},{"guid":"/^(ciscowebexstart1@cisco\\.com|ciscowebexstart_test@cisco\\.com|ciscowebexstart@cisco\\.com|ciscowebexgpc@cisco\\.com)$/","prefs":[],"schema":1485212610474,"blockID":"i1522","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1333225","who":"All Firefox users who have any Cisco WebEx add-ons installed.","why":"A critical security vulnerability has been discovered in Cisco WebEx add-ons that enable malicious websites to execute code on the user's system.","name":"Cisco WebEx add-ons","created":"2017-01-23T22:55:58Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.0.1","minVersion":"1.0.0","targetApplication":[]}],"id":"30368779-1d3b-490a-0a34-253085af7754","last_modified":1485215014902},{"guid":"{de71f09a-3342-48c5-95c1-4b0f17567554}","prefs":[],"schema":1484335370642,"blockID":"i1493","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1329654","who":"All users who have this add-on installed.","why":"This is a malicious add-on that is installed using a fake name. It changes search and homepage settings.","name":"Search for Firefox Convertor (malware)","created":"2017-01-12T22:17:59Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"1.3.9","minVersion":"0","targetApplication":[]}],"id":"d6ec9f54-9945-088e-ba68-40117eaba24e","last_modified":1484867614757},{"guid":"googlotim@gmail.com","prefs":[],"schema":1483389810787,"blockID":"i1492","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1328594","who":"All users who have Savogram version 1.3.2 installed. Version 1.3.1 doesn't have this problem and can be installed from the <a href=\"https://addons.mozilla.org/addon/savogram/\">add-on page</a>. Note that this is an older version, so affected users won't be automatically updated to it. New versions should correct this problem if they become available.","why":"Version 1.3.2 of this add-on loads remote code and performs DOM injection in an unsafe manner.","name":"Savogram 1.3.2","created":"2017-01-05T19:58:39Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.3.2","minVersion":"1.3.2","targetApplication":[]}],"id":"0756ed76-7bc7-ec1e-aba5-3a9fac2107ba","last_modified":1483646608603},{"guid":"support@update-firefox.com","prefs":[],"schema":1483387107003,"blockID":"i21","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=629717","who":"All users of the add-on in all Mozilla applications.","why":"This add-on is adware/spyware masquerading as a Firefox update mechanism.","name":"Browser Update (spyware)","created":"2011-01-31T16:23:48Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"dfb06be8-3594-28e4-d163-17e27119f15d","last_modified":1483389809169},{"guid":"{2224e955-00e9-4613-a844-ce69fccaae91}","prefs":[],"schema":1483387107003,"blockID":"i7","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=512406","who":"All users of Internet Saving Optimizer for all Mozilla applications.","why":"This add-on causes a high volume of Firefox crashes and is considered malware.","name":"Internet Saving Optimizer (extension)","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"b9efb796-97c2-6434-d28f-acc83436f8e5","last_modified":1483389809147},{"guid":"supportaccessplugin@gmail.com","prefs":[],"schema":1483387107003,"blockID":"i43","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=693673","who":"All users with Firefox Access Plugin installed","why":"This add-on is spyware that reports all visited websites to a third party with no user value.","name":"Firefox Access Plugin (spyware)","created":"2011-10-11T11:24:05Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"1ed230a4-e174-262a-55ab-0c33f93a2529","last_modified":1483389809124},{"guid":"{8CE11043-9A15-4207-A565-0C94C42D590D}","prefs":[],"schema":1483387107003,"blockID":"i10","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=541302","who":"All users of this add-on in all Mozilla applications.","why":"This add-on secretly hijacks all search results in most major search engines and masks as a security add-on.","name":"Internal security options editor (malware)","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"e2e0ac09-6d68-75f5-2424-140f51904876","last_modified":1483389809102},{"guid":"youtube@youtube2.com","prefs":[],"schema":1483387107003,"blockID":"i47","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=713050","who":"All users with any version of Free Cheesecake Factory installed on any Mozilla product.","why":"This add-on hijacks your Facebook account.","name":"Free Cheesecake Factory (malware)","created":"2011-12-22T13:11:36Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"85f5c1db-433b-bee3-2a3b-325165cacc6e","last_modified":1483389809079},{"guid":"admin@youtubespeedup.com","prefs":[],"schema":1483387107003,"blockID":"i48","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=714221","who":"All users with any version of Youtube Speed UP! installed on any Mozilla product.","why":"This add-on hijacks your Facebook account.","name":"Youtube Speed UP! (malware)","created":"2011-12-29T19:48:06Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"a93922c4-8a8a-5230-8f76-76fecb0653b6","last_modified":1483389809057},{"guid":"{E8E88AB0-7182-11DF-904E-6045E0D72085}","prefs":[],"schema":1483387107003,"blockID":"i13","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=578085","who":"All users of this add-on for all Mozilla applications.","why":"This add-on intercepts website login credentials and is malware. For more information, please <a href=\"http://blog.mozilla.com/addons/2010/07/13/add-on-security-announcement/\">read our security announcement</a>.","name":"Mozilla Sniffer (malware)","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"ebbd6de9-fc8a-3e5b-2a07-232bee589c7c","last_modified":1483389809035},{"guid":"sigma@labs.mozilla","prefs":[],"schema":1483387107003,"blockID":"i44","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=690819","who":"All users of Lab Kit in all versions of Firefox.","why":"The Lab Kit add-on has been retired due to compatibility issues with Firefox 7 and future Firefox browser releases. You can still install Mozilla Labs add-ons <a href=\"https://addons.mozilla.org/en-US/firefox/user/5133025/\">individually</a>.\r\n\r\nFor more information, please read <a href=\"http://mozillalabs.com/blog/2011/10/lab-kit-is-retiring/\">this announcement</a>.","name":"Mozilla Labs: Lab Kit","created":"2011-10-11T11:51:34Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"d614e9cd-220f-3a19-287b-57e122f8c4b5","last_modified":1483389809012},{"guid":"/^(jid0-S9kkzfTvEmC985BVmf8ZOzA5nLM@jetpack|jid1-qps14pkDB6UDvA@jetpack|jid1-Tsr09YnAqIWL0Q@jetpack|shole@ats.ext|{38a64ef0-7181-11e3-981f-0800200c9a66}|eochoa@ualberta.ca)$/","prefs":[],"schema":1483376308298,"blockID":"i1424","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1325060","who":"All users who have any of the affected versions installed.","why":"A security vulnerability was discovered in old versions of the Add-ons SDK, which is exposed by certain old versions of add-ons. In the case of some add-ons that haven't been updated for a long time, all versions are being blocked.","name":"Various vulnerable add-on versions","created":"2016-12-21T17:22:12Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"0699488d-2a19-6735-809e-f229849fe00b","last_modified":1483378113482},{"guid":"pink@rosaplugin.info","prefs":[],"schema":1482945809444,"blockID":"i84","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=743484","who":"All Firefox users who have this add-on installed","why":"Add-on acts like malware and performs user actions on Facebook without their consent.","name":"Facebook Rosa (malware)","created":"2012-04-09T10:13:51Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"66ad8de9-311d-076c-7356-87fde6d30d8f","last_modified":1482945810971},{"guid":"videoplugin@player.com","prefs":[],"schema":1482945809444,"blockID":"i90","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=752483","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware disguised as a Flash Player update. It can hijack Google searches and Facebook accounts.","name":"FlashPlayer 11 (malware)","created":"2012-05-07T08:58:30Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"d25943f1-39ef-b9ec-ab77-baeef3498365","last_modified":1482945810949},{"guid":"youtb3@youtb3.com","prefs":[],"schema":1482945809444,"blockID":"i60","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=723753","who":"All Firefox users who have this extension installed.","why":"Malicious extension installed under false pretenses.","name":"Video extension (malware)","created":"2012-02-02T16:38:41Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"cae3093f-a7b3-5352-a264-01dbfbf347ce","last_modified":1482945810927},{"guid":"{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}","prefs":[],"schema":1482945809444,"blockID":"i82","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=743012","who":"All Firefox users who have installed this add-on.","why":"This add-on maliciously manipulates Facebook and is installed under false pretenses.","name":"Face Plus (malware)","created":"2012-04-09T10:04:28Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"09319ab3-55e7-fec1-44e0-84067d014b9b","last_modified":1482945810904},{"guid":"cloudmask@cloudmask.com","prefs":[],"schema":1482945809444,"blockID":"i1233","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1280431","who":"Any user who has version 2.0.788, or earlier, installed.","why":"These versions of the add-on (before 2.0.788) execute code from a website in a privileged local browser context, potentially allowing dangerous, unreviewed, actions to affect the user's computer.  This is fixed in later versions.","name":"CloudMask","created":"2016-06-17T14:31:57Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.0.788","minVersion":"0","targetApplication":[]}],"id":"2a8b40c7-a1d2-29f4-b7d7-ccfc5066bae1","last_modified":1482945810881},{"guid":"{95ff02bc-ffc6-45f0-a5c8-619b8226a9de}","prefs":[],"schema":1482945809444,"blockID":"i105","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=763065","who":"All Firefox users who have this add-on installed.","why":"This is a malicious add-on that inserts scripts into Facebook and hijacks the user's session.\r\n","name":"Eklenti D\u00fcnyas\u0131 (malware)","created":"2012-06-08T14:34:25Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"afbbc08d-2414-f51e-fdb8-74c0a2d90323","last_modified":1482945810858},{"guid":"{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}","prefs":[],"schema":1482945809444,"blockID":"i77","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=738419","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware that is installed under false pretenses as an Adobe plugin.","name":"Adobe Flash (malware)","created":"2012-03-22T14:39:08Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"81753a93-382d-5f9d-a4ca-8a21b679ebb1","last_modified":1482945810835},{"guid":"youtube@youtube3.com","prefs":[],"schema":1482945809444,"blockID":"i57","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722823","who":"All Firefox users that have installed this add-on.","why":"Malware installed on false pretenses.","name":"Divx 2012 Plugin (malware)","created":"2012-01-31T13:54:20Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"4a93a0eb-a513-7272-6199-bc4d6228ff50","last_modified":1482945810811},{"guid":"{392e123b-b691-4a5e-b52f-c4c1027e749c}","prefs":[],"schema":1482945809444,"blockID":"i109","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=769781","who":"All Firefox users who have this add-on installed.","why":"This add-on pretends to be developed by Facebook and injects scripts that manipulate users' Facebook accounts.","name":"Zaman Tuneline Hay\u0131r! (malware)","created":"2012-06-29T13:20:22Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"b9a805aa-cae7-58d6-5a53-2af4442e4cf6","last_modified":1482945810788},{"guid":"msntoolbar@msn.com","prefs":[],"schema":1482945809444,"blockID":"i18","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=599971","who":"Users of Bing Bar 6.0 and older for all versions of Firefox.","why":"This add-on has security issues and was blocked at Microsoft's request. For more information, please see <a href=\"http://support.microsoft.com/kb/2430460\">this article</a>.","name":"Bing Bar","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"6.*","minVersion":" 0","targetApplication":[]}],"id":"9b2f2039-b997-8993-d6dc-d881bc1ca7a1","last_modified":1482945810764},{"guid":"yasd@youasdr3.com","prefs":[],"schema":1482945809444,"blockID":"i104","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=763065","who":"All Firefox users who have this add-on installed.","why":"This is a malicious add-on that inserts scripts into Facebook and hijacks the user's session.\r\n","name":"Play Now (malware)","created":"2012-06-08T14:33:31Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"8a352dff-d09d-1e78-7feb-45dec7ace5a5","last_modified":1482945810740},{"guid":"fdm_ffext@freedownloadmanager.org","prefs":[],"schema":1482945809444,"blockID":"i2","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=408445","who":"Users of Firefox 3 and later with versions 1.0 through 1.3.1 of Free Download Manager","why":"This add-on causes a high volume of crashes.","name":"Free Download Manager","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.3.1","minVersion":"1.0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"3.0a1"}]}],"id":"fc46f8e7-0489-b90f-a373-d93109479ca5","last_modified":1482945810393},{"guid":"flash@adobe.com","prefs":[],"schema":1482945809444,"blockID":"i56","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722526","who":"All Firefox users who have this add-on installed.","why":"This add-on poses as an Adobe Flash update and injects malicious scripts into web pages. It hides itself in the Add-ons Manager.","name":"Adobe Flash Update (malware)","created":"2012-01-30T15:41:51Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"696db959-fb0b-8aa4-928e-65f157cdd77a","last_modified":1482945810371},{"guid":"youtubeer@youtuber.com","prefs":[],"schema":1482945809444,"blockID":"i66","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=726787","who":"All Firefox users who have installed this add-on.","why":"Add-on behaves maliciously, and is installed under false pretenses.","name":"Plug VDS (malware)","created":"2012-02-13T15:44:20Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"0878ce4e-b476-ffa3-0e06-21a65b7917a1","last_modified":1482945810348},{"guid":"{B13721C7-F507-4982-B2E5-502A71474FED}","prefs":[],"schema":1482945809444,"blockID":"i8","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=627278","who":"Users of all versions of the original Skype Toolbar in all versions of Firefox.","why":"This add-on causes a high volume of Firefox crashes and introduces severe performance issues. Please <a href=\"http://www.skype.com/intl/en/get-skype/on-your-computer/click-and-call/\">update to the latest version</a>. For more information, please <a href=\"http://blog.mozilla.com/addons/2011/01/20/blocking-the-skype-toolbar-in-firefox/\">read our announcement</a>.","name":"Original Skype Toolbar","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"5a320611-59a3-0eee-bb30-9052be870e00","last_modified":1482945810326},{"guid":"yslow@yahoo-inc.com","prefs":[],"schema":1482945809444,"blockID":"i11","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=542686","who":"Users of YSlow version 2.0.5 for Firefox 3.5.7 and later.","why":"This add-on causes a high volume of Firefox crashes and other stability issues. Users should <a href=\"https://addons.mozilla.org/firefox/addon/yslow/\">update to the latest version</a>.","name":"YSlow","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.0.5","minVersion":"2.0.5","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"3.5.7"}]}],"id":"a9b34e8f-45ce-9217-b791-98e094c26352","last_modified":1482945810303},{"guid":"youtube@youtuber.com","prefs":[],"schema":1482945809444,"blockID":"i63","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=724691","who":"All Firefox users who have installed this add-on.","why":"Installs under false pretenses and delivers malware.","name":"Mozilla Essentials (malware)","created":"2012-02-06T15:39:38Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"18216e6f-9d70-816f-4d4c-63861f43ff3c","last_modified":1482945810281},{"guid":"flash@adobee.com","prefs":[],"schema":1482945809444,"blockID":"i83","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=743497","who":"All Firefox users who have this add-on installed.","why":"This add-on is malware installed under false pretenses.","name":"FlashPlayer 11 (malware)","created":"2012-04-09T10:08:22Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"09bb4661-331c-f7ba-865b-9e085dc437af","last_modified":1482945810259},{"guid":"youtube@2youtube.com","prefs":[],"schema":1482945809444,"blockID":"i71","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=730399","who":"All Firefox users who have installed this add-on.","why":"Extension is malware, installed under false pretenses.","name":"YouTube extension (malware)","created":"2012-02-27T10:23:23Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"5d389c1f-b3a0-b06f-6ffb-d1e8aa055e3c","last_modified":1482945810236},{"guid":"webmaster@buzzzzvideos.info","prefs":[],"schema":1482945809444,"blockID":"i58","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722844","who":"All Firefox users who have installed this add-on.","why":"Malware add-on that is installed under false pretenses.","name":"Buzz Video (malware)","created":"2012-01-31T14:51:06Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"f7aab105-e2c2-42f5-d9be-280eb9c0c8f7","last_modified":1482945810213},{"guid":"play5@vide04flash.com","prefs":[],"schema":1482945809444,"blockID":"i92","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=755443","who":"All Firefox users who have this add-on installed.","why":"This add-on impersonates a Flash Player update (poorly), and inserts malicious scripts into Facebook.","name":"Lastest Flash PLayer (malware)","created":"2012-05-15T13:27:22Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"7190860e-fc1f-cd9f-5d25-778e1e9043b2","last_modified":1482945810191},{"guid":"support3_en@adobe122.com","prefs":[],"schema":1482945809444,"blockID":"i97","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=759164","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware disguised as the Flash Player plugin.","name":"FlashPlayer 11 (malware)","created":"2012-05-28T13:42:54Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"decf93a1-2bb0-148c-a1a6-10b3757b554b","last_modified":1482945810168},{"guid":"a1g0a9g219d@a1.com","prefs":[],"schema":1482945809444,"blockID":"i73","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=736275","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware disguised as Flash Player. It steals user cookies and sends them to a remote location.","name":"Flash Player (malware)","created":"2012-03-15T15:03:04Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"6dd66b43-897d-874a-2227-54e240b8520f","last_modified":1482945810146},{"guid":"ghostviewer@youtube2.com","prefs":[],"schema":1482945809444,"blockID":"i59","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=723683","who":"All Firefox users who have installed this add-on.","why":"Malicious add-on that automatically posts to Facebook.","name":"Ghost Viewer (malware)","created":"2012-02-02T16:32:15Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"06dfe833-8c3d-90ee-3aa8-37c3c28f7c56","last_modified":1482945810123},{"guid":"{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}","prefs":[],"schema":1482945809444,"blockID":"i19","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=621660","who":"Users of Stylish version 1.1b1 for Firefox.","why":"Version 1.1b1 of this add-on causes compatibility issues with Firefox. Users should update to the latest version.","name":"Stylish","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.1b1","minVersion":"1.1b1","targetApplication":[]}],"id":"aaea37e1-ff86-4565-8bd5-55a6bf942791","last_modified":1482945810101},{"guid":"kdrgun@gmail.com","prefs":[],"schema":1482945809444,"blockID":"i103","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=763065","who":"All Firefox users who have this add-on installed.","why":"This is a malicious add-on that inserts scripts into Facebook and hijacks the user's session.","name":"Timeline Kapat (malware)","created":"2012-06-08T14:32:51Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"a9a46ab2-2f56-1046-201c-5faa3435e248","last_modified":1482945810078},{"guid":"youtube2@youtube2.com","prefs":[],"schema":1482945809444,"blockID":"i67","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=728476","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware, installed under false pretenses.","name":"Youtube Online (malware)","created":"2012-02-18T09:10:30Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"14650ece-295b-a667-f9bc-a3d973e2228c","last_modified":1482945810055},{"guid":"masterfiler@gmail.com","prefs":[],"schema":1482945809444,"blockID":"i12","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=542081","who":"All users of this add-on for all Mozilla applications.","why":"This add-on is malware and attempts to install a Trojan on the user's computer.","name":"Master File (malware)","created":"2010-02-05T15:01:27Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"a256d79d-5af8-92e9-a29d-350adf822efe","last_modified":1482945810032},{"guid":"{847b3a00-7ab1-11d4-8f02-006008948af5}","prefs":[],"schema":1482945809444,"blockID":"i9","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=531047","who":"Users of Enigmail versions older than 0.97a for Thunderbird 3 and later.","why":"This add-on causes a high volume of crashes and other stability issues. Users should <a href=\"https://addons.mozilla.org/en-US/thunderbird/addon/enigmail/\">update Enigmail</a>.","name":"Enigmail","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"0.97a","minVersion":"0","targetApplication":[{"guid":"{3550f703-e582-4d05-9a08-453d09bdfdc6}","maxVersion":"*","minVersion":"3.0pre"}]}],"id":"115f46b6-059d-202a-4373-2ca79b096347","last_modified":1482945810003},{"guid":"mozilla_cc@internetdownloadmanager.com","prefs":[],"schema":1482945809444,"blockID":"i14","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=578443","who":"Users of Firefox 4 and later with Internet Download Manager version 6.9.8 and older.","why":"This add-on causes a high volume of crashes and has other stability issues.","name":"Internet Download Manager","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"6.9.8","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"3.7a1pre"}]}],"id":"773ffcfb-75d1-081d-7431-ebe3fa5dbb44","last_modified":1482945809979},{"guid":"admin@youtubeplayer.com","prefs":[],"schema":1482945809444,"blockID":"i51","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=717165","who":"All Firefox users with this extension installed.","why":"This add-on is malware, doing nothing more than inserting advertisements into websites through iframes.","name":"Youtube player (malware)","created":"2012-01-18T14:34:55Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"16b2ce94-88db-0d79-33fc-a93070ceb509","last_modified":1482945809957},{"guid":"personas@christopher.beard","prefs":[],"schema":1482945809444,"blockID":"i15","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=590978","who":"All users of Personas Plus 1.6 in all versions of Firefox.","why":"This version of Personas Plus is incompatible with certain Firefox functionality and other add-ons. Users should upgrade to the latest version.","name":"Personas Plus","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.6","minVersion":"1.6","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"3.6.*","minVersion":"3.6"}]}],"id":"e36479c6-ca00-48d4-4fd9-ec677fd032da","last_modified":1482945809934},{"guid":"youtubeee@youtuber3.com","prefs":[],"schema":1482945809444,"blockID":"i96","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=758503","who":"All Firefox users who have installed this add-on.","why":"This is a malicious add-on that is disguised as a DivX plugin.","name":"Divx 2012 Plugins (malware)","created":"2012-05-25T09:26:47Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"f01be9cb-5cf2-774a-a4d7-e210a24db5b9","last_modified":1482945809912},{"guid":"{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}","prefs":[],"schema":1482945809444,"blockID":"i17","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=599971","who":"Users of version 2.2 of this add-on in all versions of Firefox.","why":"This add-on has security issues and was blocked at Microsoft's request. For more information, please see <a href=\"http://support.microsoft.com/kb/2430460\">this article</a>.","name":"Default Manager (Microsoft)","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.2","minVersion":"2.2","targetApplication":[]}],"id":"38be28ac-2e30-37fa-4332-852a55fafb43","last_modified":1482945809886},{"guid":"{68b8676b-99a5-46d1-b390-22411d8bcd61}","prefs":[],"schema":1482945809444,"blockID":"i93","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=755635","who":"All Firefox users who have this add-on installed.","why":"This is a malicious add-on that post content on Facebook accounts and steals user data.","name":"Zaman T\u00fcnelini Kald\u0131r! (malware)","created":"2012-05-16T10:44:42Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"733aff15-9b1f-ec04-288f-b78a55165a1c","last_modified":1482945809863},{"guid":"applebeegifts@mozilla.doslash.org","prefs":[],"schema":1482945809444,"blockID":"i54","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=721562","who":"All Firefox users that install this add-on.","why":"Add-on is malware installed under false pretenses.","name":"Applebees Gift Card (malware)","created":"2012-01-26T16:17:49Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"1372c8ab-5452-745a-461a-aa78e3e12c4b","last_modified":1482945809840},{"guid":"activity@facebook.com","prefs":[],"schema":1482945112982,"blockID":"i65","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=726803","who":"All Firefox users who have installed this add-on.","why":"Add-on behaves maliciously and poses as an official Facebook add-on.","name":"Facebook extension (malware)","created":"2012-02-13T15:41:02Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"79ad1c9b-0828-7823-4574-dd1cdd46c3d6","last_modified":1482945809437},{"guid":"jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack","prefs":[],"schema":1482945112982,"blockID":"i62","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=724650","who":"All Firefox users who have installed this add-on.","why":"Add-on is installed under false pretenses and delivers malware.","name":"YouTube extension (malware)","created":"2012-02-06T14:46:33Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"5ae1e642-b53c-54c0-19e7-5562cfdac3a3","last_modified":1482945809415},{"guid":"{B7082FAA-CB62-4872-9106-E42DD88EDE45}","prefs":[],"schema":1482945112982,"blockID":"i25","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=637542","who":"Users of McAfee SiteAdvisor below version 3.3.1 for Firefox 4.\r\n\r\nUsers of McAfee SiteAdvisor 3.3.1 and below for Firefox 5 and higher.","why":"This add-on causes a high volume of crashes and is incompatible with certain versions of Firefox.","name":"McAfee SiteAdvisor","created":"2011-03-14T15:53:07Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"3.3.0.*","minVersion":"0.1","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"3.7a1"}]}],"id":"c950501b-1f08-2ab2-d817-7c664c0d16fe","last_modified":1482945809393},{"guid":"{B7082FAA-CB62-4872-9106-E42DD88EDE45}","prefs":[],"schema":1482945112982,"blockID":"i38","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=660111","who":"Users of McAfee SiteAdvisor below version 3.3.1 for Firefox 4.\r\n\r\nUsers of McAfee SiteAdvisor 3.3.1 and below for Firefox 5 and higher.","why":"This add-on causes a high volume of crashes and is incompatible with certain versions of Firefox.","name":"McAfee SiteAdvisor","created":"2011-05-27T13:55:02Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"3.3.1","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"5.0a1"}]}],"id":"f11de388-4511-8d06-1414-95d3b2b122c5","last_modified":1482945809371},{"guid":"{3f963a5b-e555-4543-90e2-c3908898db71}","prefs":[],"schema":1482945112982,"blockID":"i6","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=527135","who":"Users of AVG SafeSearch version 8.5 and older for all Mozilla applications.","why":"This add-on causes a high volume of crashes and causes other stability issues.","name":"AVG SafeSearch","created":"2009-06-17T13:12:12Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"8.5","minVersion":"0","targetApplication":[]}],"id":"0d6f7d4c-bf5d-538f-1ded-ea4c6b775617","last_modified":1482945809348},{"guid":"langpack-vi-VN@firefox.mozilla.org","prefs":[],"schema":1482945112982,"blockID":"i3","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=432406","who":"Users of Vietnamese Language Pack version 2.0 for all Mozilla applications.","why":"Corrupted files. For more information, please see <a href=\"http://blog.mozilla.com/security/2008/05/07/compromised-file-in-vietnamese-language-pack-for-firefox-2/\">this blog post</a>.","name":"Vietnamese Language Pack","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.0","minVersion":"2.0","targetApplication":[]}],"id":"51d4b581-d21c-20a1-6147-b17c3adc7867","last_modified":1482945809326},{"guid":"youtube@youtube7.com","prefs":[],"schema":1482945112982,"blockID":"i55","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=721646","who":"All Firefox users with this add-on installed.","why":"This is malware posing as video software.","name":"Plugin Video (malware)","created":"2012-01-27T09:39:31Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"08ceedf5-c7c1-f54f-db0c-02f01f0e319a","last_modified":1482945809304},{"guid":"crossriderapp3924@crossrider.com","prefs":[],"schema":1482945112982,"blockID":"i76","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=738282","who":"All Firefox users who have installed this add-on.","why":"This add-on compromises Facebook privacy and security and spams friends lists without user intervention.","name":"Fblixx (malware)","created":"2012-03-22T10:38:47Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"39d0a019-62fb-837b-1f1f-6831e56442b5","last_modified":1482945809279},{"guid":"{45147e67-4020-47e2-8f7a-55464fb535aa}","prefs":[],"schema":1482945112982,"blockID":"i86","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=748993","who":"All Firefox users who have this add-on installed.","why":"This add-on injects scripts into Facebook and performs malicious activity.","name":"Mukemmel Face+","created":"2012-04-25T16:33:21Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"960443f9-cf48-0b71-1ff2-b8c34a3411ea","last_modified":1482945809255},{"guid":"{4B3803EA-5230-4DC3-A7FC-33638F3D3542}","prefs":[],"schema":1482945112982,"blockID":"i4","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=441649","who":"Users of Firefox 3 and later with version 1.2 of Crawler Toolbar","why":"This add-on causes a high volume of crashes.","name":"Crawler Toolbar","created":"2008-07-08T10:23:31Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.2","minVersion":"1.2","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"3.0a1"}]}],"id":"a9818d53-3a6a-8673-04dd-2a16f5644215","last_modified":1482945809232},{"guid":"flashupdate@adobe.com","prefs":[],"schema":1482945112982,"blockID":"i68","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=722526","who":"All Firefox users who have this add-on installed.","why":"Add-on is malware, installed under false pretenses.","name":"Flash Update (malware)","created":"2012-02-21T13:55:10Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"1ba5b46e-790d-5af2-9580-a5f1e6e65522","last_modified":1482945809208},{"guid":"plugin@youtubeplayer.com","prefs":[],"schema":1482945112982,"blockID":"i127","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=783356","who":"All users who have this add-on installed.","why":"This add-on tries to pass as a YouTube player and runs malicious scripts on webpages.","name":"Youtube Facebook Player (malware)","created":"2012-08-16T13:03:10Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"17a8bece-e2df-a55d-8a72-95faff028b83","last_modified":1482945809185},{"guid":"GifBlock@facebook.com","prefs":[],"schema":1482945112982,"blockID":"i79","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=739482","who":"All Firefox users who have installed this extension.","why":"This extension is malicious and is installed under false pretenses.","name":"Facebook Essentials (malware)","created":"2012-03-27T10:53:33Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"728451e8-1273-d887-37e9-5712b1cc3bff","last_modified":1482945809162},{"guid":"ff-ext@youtube","prefs":[],"schema":1482945112982,"blockID":"i52","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=719296","who":"All Firefox users that have this add-on installed.","why":"This add-on poses as a YouTube player while posting spam into Facebook account.","name":"Youtube player (malware)","created":"2012-01-19T08:26:35Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"cd2dd72a-dd52-6752-a0cd-a4b312fd0b65","last_modified":1482945809138},{"guid":"ShopperReports@ShopperReports.com","prefs":[],"schema":1482945112982,"blockID":"i22","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=630191","who":"Users of Shopper Reports version 3.1.22.0 in Firefox 4 and later.","why":"This add-on causes a high volume of Firefox crashes.","name":"Shopper Reports","created":"2011-02-09T17:03:39Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"3.1.22.0","minVersion":"3.1.22.0","targetApplication":[]}],"id":"f26b049c-d856-750f-f050-996e6bec7cbb","last_modified":1482945809115},{"guid":"{27182e60-b5f3-411c-b545-b44205977502}","prefs":[],"schema":1482945112982,"blockID":"i16","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=599971","who":"Users of version 1.0 of this add-on in all versions of Firefox.","why":"This add-on has security issues and was blocked at Microsoft's request. For more information, please see <a href=\"http://support.microsoft.com/kb/2430460\">this article</a>.","name":"Search Helper Extension (Microsoft)","created":"2011-03-31T16:28:25Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.0","minVersion":"1.0","targetApplication":[]}],"id":"2655f230-11f3-fe4c-7c3d-757d37d5f9a5","last_modified":1482945809092},{"guid":"{841468a1-d7f4-4bd3-84e6-bb0f13a06c64}","prefs":[],"schema":1482945112982,"blockID":"i46","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=712369","who":"Users of all versions of Nectar Search Toolbar in Firefox 9.","why":"This add-on causes crashes and other stability issues in Firefox.","name":"Nectar Search Toolbar","created":"2011-12-20T11:38:17Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0.1","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"9.0","minVersion":"9.0a1"}]}],"id":"b660dabd-0dc0-a55c-4b86-416080b345d9","last_modified":1482945809069},{"guid":"support@daemon-tools.cc","prefs":[],"schema":1482945112982,"blockID":"i5","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=459850","who":"Users of Daemon Tools Toolbar version 1.0.0.5 and older for all Mozilla applications.","why":"This add-on causes a high volume of crashes.","name":"Daemon Tools Toolbar","created":"2009-02-13T18:39:01Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"1.0.0.5","minVersion":"0","targetApplication":[]}],"id":"8cabafd3-576a-b487-31c8-ab59e0349a0e","last_modified":1482945809045},{"guid":"{a3a5c777-f583-4fef-9380-ab4add1bc2a8}","prefs":[],"schema":1482945112982,"blockID":"i53","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=719605","who":"All users of Firefox with this add-on installed.","why":"This add-on is being offered as an online movie viewer, when it reality it only inserts scripts and ads into known sites.","name":"Peliculas-FLV (malware)","created":"2012-01-19T15:58:10Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"2.0.3","minVersion":"2.0.3","targetApplication":[]}],"id":"07bc0962-60da-087b-c3ab-f2a6ab84d81c","last_modified":1482945809021},{"guid":"royal@facebook.com","prefs":[],"schema":1482945112982,"blockID":"i64","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=725777","who":"All Firefox users who have installed this add-on.","why":"Malicious add-on posing as a Facebook tool.","name":"Facebook ! (malware)","created":"2012-02-09T13:24:23Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"dd1d2623-0d15-c93e-8fbd-ba07b0299a44","last_modified":1482945808997},{"guid":"{28bfb930-7620-11e1-b0c4-0800200c9a66}","prefs":[],"schema":1482945112982,"blockID":"i108","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=766852","who":"All Firefox user who have this add-on installed.","why":"This is malware disguised as an Adobe product. It spams Facebook pages.","name":"Aplicativo (malware)","created":"2012-06-21T09:24:11Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"908dc4fb-ebc9-cea1-438f-55e4507ba834","last_modified":1482945808973},{"guid":"socialnetworktools@mozilla.doslash.org","prefs":[],"schema":1482945112982,"blockID":"i78","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=739441","who":"All Firefox users who have installed this add-on.","why":"This add-on hijacks the Facebook UI and adds scripts to track users.","name":"Social Network Tools (malware)","created":"2012-03-26T16:46:55Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"1064cd25-3b87-64bb-b0a6-2518ad281574","last_modified":1482945808950},{"guid":"youtubeeing@youtuberie.com","prefs":[],"schema":1482945112982,"blockID":"i98","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=759663","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware disguised as a Youtube add-on.","name":"Youtube Video Player (malware)","created":"2012-05-30T09:30:14Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"3484f860-56e1-28e8-5a70-cdcd5ab9d6ee","last_modified":1482945808927},{"guid":"{3a12052a-66ef-49db-8c39-e5b0bd5c83fa}","prefs":[],"schema":1482945112982,"blockID":"i101","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=761874","who":"All Firefox users who have installed this add-on.","why":"This add-on is malware disguised as a Facebook timeline remover.","name":"Timeline Remove (malware)","created":"2012-06-05T18:37:42Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"b01b321b-6628-7166-bd15-52f21a04d8bd","last_modified":1482945808904},{"guid":"pfzPXmnzQRXX6@2iABkVe.com","prefs":[],"schema":1482945112982,"blockID":"i99","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=759950","who":"All Firefox users who have this add-on installed.","why":"This add-on is malware disguised as a Flash Player update.","name":"Flash Player (malware)","created":"2012-05-30T17:10:18Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"29cc4abc-4f52-01f1-eb0b-cad84ba4db13","last_modified":1482945808881},{"guid":"/^(@pluginscribens_firefox|extension@vidscrab.com|firefox@jjj.ee|firefox@shop-reward.de|FxExtPasteNGoHtk@github.lostdj|himanshudotrai@gmail.com|jid0-bigoD0uivzAMmt07zrf3OHqa418@jetpack|jid0-iXbAR01tjT2BsbApyS6XWnjDhy8@jetpack)$/","prefs":[],"schema":1482341309012,"blockID":"i1423","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1325060","who":"All users who have any of the affected versions installed.","why":"A security vulnerability was discovered in old versions of the Add-ons SDK, which is exposed by certain old versions of add-ons. In the case of some add-ons that haven't been updated for a long time, all versions are being blocked.","name":"Various vulnerable add-on versions","created":"2016-12-21T17:21:10Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"a58a2836-e4e7-74b5-c109-fa3d41e9ed56","last_modified":1482343886390},{"guid":"/^(pdftoword@addingapps.com|jid0-EYTXLS0GyfQME5irGbnD4HksnbQ@jetpack|jid1-ZjJ7t75BAcbGCX@jetpack)$/","prefs":[],"schema":1482341309012,"blockID":"i1425","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1325060","who":"All users who have any of the affected versions installed.","why":"A security vulnerability was discovered in old versions of the Add-ons SDK, which is exposed by certain old versions of add-ons. In the case of some add-ons that haven't been updated for a long time, all versions are being blocked.","name":"Various vulnerable add-on versions","created":"2016-12-21T17:23:14Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"150e639f-c832-63d0-a775-59313b2e1bf9","last_modified":1482343886365},{"guid":"{cc8f597b-0765-404e-a575-82aefbd81daf}","prefs":[],"schema":1480349193877,"blockID":"i380","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=866332","who":"All Firefox users who have this add-on installed.","why":"This is a malicious add-on that hijacks Facebook accounts and performs unwanted actions on behalf of the user.","name":"Update My Browser (malware)","created":"2013-06-19T13:03:00Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"4950d7aa-c602-15f5-a7a2-d844182d5cbd","last_modified":1480349217152},{"guid":"extension@FastFreeConverter.com","prefs":[],"schema":1480349193877,"blockID":"i470","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=935779","who":"All Firefox users who have this add-on installed.","why":"This add-on is part of a malicious Firefox installer bundle.","name":"Installer bundle (malware)","created":"2013-11-07T15:38:26Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"649dd933-debf-69b7-020f-496c2c9f99c8","last_modified":1480349217071},{"guid":"59D317DB041748fdB89B47E6F96058F3@jetpack","prefs":[],"schema":1480349193877,"blockID":"i694","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1053540","who":"All Firefox users who have this add-ons installed. Users who wish to continue using this add-on can enable it in the Add-ons Manager.","why":"This is a suspicious add-on that appears to be installed without user consent, in violation of the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>.","name":"JsInjectExtension","created":"2014-08-21T13:46:30Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"75692bd4-18e5-a9be-7ec3-9327e159ef68","last_modified":1480349217005},{"guid":"/^({bfec236d-e122-4102-864f-f5f19d897f5e}|{3f842035-47f4-4f10-846b-6199b07f09b8}|{92ed4bbd-83f2-4c70-bb4e-f8d3716143fe})$/","prefs":[],"schema":1480349193877,"blockID":"i527","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=949566","who":"All Firefox users who have this add-on installed. Users who wish to continue using it can enable it in the Add-ons Manager.","why":"The installer that includes this add-on violates the <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Add-on_guidelines\">Add-on Guidelines</a> by making changes that can't be easily reverted and uses multiple IDs.","name":"KeyBar add-on","created":"2013-12-20T14:13:38Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"6d68dd97-7965-0a84-8ca7-435aac3c8040","last_modified":1480349216927},{"guid":"support@vide1flash2.com","prefs":[],"schema":1480349193877,"blockID":"i246","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=830159","who":"All Firefox users who have this add-on installed.","why":"This is an add-on that poses as the Adobe Flash Player and runs malicious code in the user's system.","name":"Lastest Adobe Flash Player (malware)","created":"2013-01-14T09:17:47Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"2004fba1-74bf-a072-2a59-6e0ba827b541","last_modified":1480349216871},{"guid":"extension21804@extension21804.com","prefs":[],"schema":1480349193877,"blockID":"i312","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=835665","who":"All Firefox users who have this add-on installed.","why":"This add-on doesn't follow our <a href=\"https://developer.mozilla.org/en-US/docs/Addons/Add-on_guidelines\">Add-on Guidelines</a>, bypassing our third party install opt-in screen. Users who wish to continue using this extension can enable it in the Add-ons Manager.","name":"Coupon Companion","created":"2013-03-06T14:14:05Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"b2cf1256-dadd-6501-1f4e-25902d408692","last_modified":1480349216827},{"guid":"toolbar@ask.com","prefs":[],"schema":1480349193877,"blockID":"i602","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1024719","who":"All Firefox users who have these versions of the Ask Toolbar installed. Users who wish to continue using it can enable it in the Add-ons Manager.\r\n","why":"Certain old versions of the Ask Toolbar are causing problems to users when trying to open new tabs. Using more recent versions of the Ask Toolbar should also fix this problem.\r\n","name":"Ask Toolbar (old Avira Security Toolbar bundle)","created":"2014-06-12T14:18:05Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"3.15.8.*","minVersion":"3.15.8","targetApplication":[]}],"id":"b2b4236d-5d4d-82b2-99cd-00ff688badf1","last_modified":1480349216765},{"guid":"nosquint@urandom.ca","prefs":[],"schema":1480349193877,"blockID":"i1232","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1279561","who":"Users on Firefox 47, and higher, using version 2.1.9.1, and earlier, of this add-on. If you wish to continue using it, you can enable it in the Add-ons Manager.","why":"The add-on is breaking the in-built zoom functionality on Firefox 47.","name":"NoSquint","created":"2016-06-10T17:12:55Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"2.1.9.1-signed.1-signed","minVersion":"0","targetApplication":[{"guid":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","maxVersion":"*","minVersion":"47"}]}],"id":"30e0a35c-056a-054b-04f3-ade68b83985a","last_modified":1480349216711},{"guid":"{FE1DEEEA-DB6D-44b8-83F0-34FC0F9D1052}","prefs":[],"schema":1480349193877,"blockID":"i364","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=867670","who":"All Firefox users who have this add-on installed. Users who want to enable the add-on again can do so in the Add-ons Manager.","why":"This add-on is side-installed with other software, and blocks setting reversions attempted by users who want to recover their settings after they are hijacked by other add-ons.","name":"IB Updater","created":"2013-06-10T16:14:41Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"a59b967c-66ca-7ad9-2dc6-d0ad37ded5fd","last_modified":1480349216652},{"guid":"vpyekkifgv@vpyekkifgv.org","prefs":[],"schema":1480349193877,"blockID":"i352","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=872211","who":"All Firefox users who have this add-on installed.","why":"Uses a deceptive name and injects ads into pages without user consent.","name":"SQLlite Addon (malware)","created":"2013-05-14T13:42:04Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"8fd981ab-7ee0-e367-d804-0efe29d63178","last_modified":1480349216614},{"guid":"/^firefox@(albrechto|swiftbrowse|springsmart|storimbo|squirrelweb|betterbrowse|lizardlink|rolimno|browsebeyond|clingclang|weblayers|kasimos|higher-aurum|xaven|bomlabio)\\.(com?|net|org|info|biz)$/","prefs":[],"schema":1480349193877,"blockID":"i549","details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=937405","who":"All Firefox users who have one or more of these add-ons installed. If you wish to continue using any of these add-ons, they can be enabled in the Add-ons Manager.","why":"A large amount of add-ons developed by Yontoo are known to be silently installed and otherwise violate the <a href=\"https://developer.mozilla.org/en-US/Add-ons/Add-on_guidelines\">Add-on Guidelines</a>.","name":"Yontoo add-ons","created":"2014-01-30T15:08:04Z"},"enabled":true,"versionRange":[{"severity":1,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"3a124164-b177-805b-06f7-70a358b37e08","last_modified":1480349216570},{"guid":"thefoxonlybetter@quicksaver",&