Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 17 Feb 2017 23:33:24 -0500
changeset 373018 6e2d0a0a93695edcd4ecd7f6fe9504e3e2a02c54
parent 373017 6489e07b82659f9a0b8924b7347ea7858a304628 (current diff)
parent 372650 d11c29c1db3a1bc96ad5792ebf8a89b2fbadcf85 (diff)
child 373019 e5a4ef40e13b5132fc666fdc8898aa6727dda99d
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge m-c to graphics MozReview-Commit-ID: IN2hMCjMHLL
browser/components/extensions/test/browser/browser_ext_url_overrides.js
browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
browser/extensions/formautofill/FormAutofillContent.jsm
browser/extensions/formautofill/FormAutofillHandler.jsm
browser/extensions/formautofill/FormAutofillHeuristics.jsm
browser/extensions/formautofill/content/FormAutofillContent.js
browser/extensions/formautofill/content/FormAutofillFrameScript.js
browser/extensions/formautofill/test/unit/test_populateFieldValues.js
browser/themes/shared/compacttheme/urlbar-history-dropmarker.svg
browser/themes/shared/devedition/urlbar-history-dropmarker.svg
build/macosx/universal/mozconfig
build/macosx/universal/mozconfig.common
build/macosx/universal/unify
config/tests/makefiles/autodeps/Makefile.in
config/tests/makefiles/autodeps/check_mkdir.py
config/tests/makefiles/autodeps/check_mkdir.tpy
config/tests/makefiles/autodeps/moz.build
devtools/client/netmonitor/components/request-list-context-menu.js
devtools/client/netmonitor/components/request-list-tooltip.js
devtools/client/netmonitor/netmonitor-view.js
devtools/client/netmonitor/netmonitor.xul
devtools/client/netmonitor/shared/components/headers-mdn.js
dom/bindings/MozMap.h
dom/bindings/Record.h
dom/bindings/parser/tests/test_mozmap.py
dom/bindings/parser/tests/test_record.py
dom/events/test/pointerevents/pointerevent_gotpointercapture_before_first_pointerevent-manual.html
dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html
dom/file/BaseBlobImpl.cpp
dom/file/BaseBlobImpl.h
dom/file/Blob.cpp
dom/file/Blob.h
dom/file/BlobImpl.cpp
dom/file/BlobImpl.h
dom/file/EmptyBlobImpl.cpp
dom/file/EmptyBlobImpl.h
dom/file/FileBlobImpl.cpp
dom/file/FileBlobImpl.h
dom/file/MemoryBlobImpl.cpp
dom/file/MemoryBlobImpl.h
dom/file/StreamBlobImpl.cpp
dom/file/StreamBlobImpl.h
dom/file/StringBlobImpl.h
dom/file/TemporaryBlobImpl.h
dom/ipc/TabChild.cpp
dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp
dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.h
dom/media/platforms/android/MediaCodecDataDecoder.cpp
dom/media/platforms/android/MediaCodecDataDecoder.h
dom/media/platforms/wrappers/MediaDataDecoderProxy.cpp
dom/media/platforms/wrappers/MediaDataDecoderProxy.h
dom/tests/mochitest/general/test_interfaces.js
dom/workers/test/test_navigator.js
gfx/layers/Compositor.h
gfx/layers/LayerTreeInvalidation.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/LayersTypes.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/UiCompositorControllerChild.cpp
gfx/layers/moz.build
gfx/layers/opengl/Composer2D.h
gfx/layers/wr/WebRenderBorderLayer.cpp
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderCanvasLayer.cpp
gfx/layers/wr/WebRenderColorLayer.cpp
gfx/layers/wr/WebRenderContainerLayer.cpp
gfx/layers/wr/WebRenderImageHost.cpp
gfx/layers/wr/WebRenderImageLayer.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderPaintedLayer.cpp
gfx/layers/wr/WebRenderTextLayer.cpp
gfx/thebes/gfxPrefs.h
git
js/src/frontend/GenerateReservedWords.py
js/src/frontend/ReservedWords.h
js/src/jsautokw.py
js/src/tests/ecma/LexicalConventions/7.4.3-3-n.js
js/src/tests/ecma_2/Exceptions/lexical-010.js
js/src/tests/ecma_2/Exceptions/lexical-022.js
js/src/tests/ecma_2/RegExp/exec-001.js
js/src/tests/ecma_3/Statements/regress-121744.js
js/src/tests/ecma_3/Unicode/uc-001.js
js/src/tests/js1_2/Array/array_split_1.js
js/src/tests/js1_2/Array/tostring_1.js
js/src/tests/js1_2/Array/tostring_2.js
js/src/tests/js1_2/Objects/toString-001.js
js/src/tests/js1_2/String/concat.js
js/src/tests/js1_2/function/Function_object.js
js/src/tests/js1_2/function/Number.js
js/src/tests/js1_2/function/String.js
js/src/tests/js1_2/function/function-001-n.js
js/src/tests/js1_2/function/length.js
js/src/tests/js1_2/function/regexparg-2-n.js
js/src/tests/js1_2/function/tostring-1.js
js/src/tests/js1_2/function/tostring-2.js
js/src/tests/js1_2/operator/equality.js
js/src/tests/js1_2/regexp/RegExp_lastIndex.js
js/src/tests/js1_2/regexp/string_split.js
js/src/tests/js1_2/version120/boolean-001.js
js/src/tests/js1_2/version120/regress-99663.js
js/src/tests/js1_3/Script/function-001-n.js
js/src/tests/js1_3/regress/function-001-n.js
js/src/tests/js1_5/GetSet/regress-353264.js
js/src/tests/js1_5/Regress/regress-106244.js
js/src/tests/js1_5/Regress/regress-119719.js
js/src/tests/js1_5/Regress/regress-173067.js
js/src/tests/js1_5/Regress/regress-249211.js
js/src/tests/js1_5/Regress/regress-320119.js
js/src/tests/js1_5/Regress/regress-350692.js
js/src/tests/js1_5/Regress/regress-354924.js
js/src/tests/js1_5/Regress/regress-362583.js
js/src/tests/js1_5/extensions/regress-352281.js
js/src/tests/js1_5/extensions/regress-355622.js
js/src/tests/js1_5/extensions/regress-418730.js
js/src/tests/js1_5/extensions/regress-421621.js
js/src/tests/js1_5/extensions/regress-432075.js
js/src/tests/js1_5/extensions/regress-437288-01.js
js/src/tests/js1_5/extensions/regress-50447.js
js/src/tests/js1_6/Array/regress-320887.js
js/src/tests/js1_7/extensions/regress-353214-01.js
js/src/tests/js1_7/extensions/regress-353249.js
js/src/tests/js1_7/geniter/regress-347739.js
js/src/tests/js1_7/geniter/regress-349012-01.js
js/src/tests/js1_7/geniter/regress-349331.js
js/src/tests/js1_7/iterable/regress-340526-02.js
js/src/tests/js1_7/lexical/regress-346642-03.js
js/src/tests/js1_7/regress/regress-350387.js
js/src/tests/js1_8_1/jit/regress-462459-01.js
js/src/tests/js1_8_1/jit/regress-462459-02.js
js/src/tests/js1_8_1/jit/regress-462459-03.js
js/src/tests/js1_8_1/jit/regress-462459-04.js
js/src/tests/js1_8_1/jit/regress-462459-05.js
js/src/tests/js1_8_1/jit/regress-462459-06.js
js/src/tests/js1_8_1/jit/regress-462459-07.js
js/src/tests/js1_8_1/jit/regress-462459-08.js
js/src/tests/js1_8_1/jit/regress-462459-09.js
js/src/tests/js1_8_1/jit/regress-462459-10.js
js/src/tests/js1_8_1/jit/regress-462459-11.js
js/src/tests/js1_8_1/jit/regress-462459-12.js
js/src/tests/js1_8_1/jit/regress-471635.js
js/src/vm/Keywords.h
layout/base/GeckoRestyleManager.cpp
layout/base/GeckoRestyleManager.h
layout/base/RestyleManagerBase.cpp
layout/base/RestyleManagerBase.h
layout/base/nsCaret.cpp
layout/painting/nsDisplayList.cpp
layout/reftests/bugs/1313772-ref.xhtml
layout/reftests/bugs/1313772.xhtml
layout/reftests/bugs/reftest.list
layout/reftests/layers/reftest.list
layout/reftests/svg/cssComment-in-attribute-01-ref.svg
layout/reftests/svg/cssComment-in-attribute-01.svg
layout/reftests/svg/reftest.list
media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
mobile/android/base/java/org/mozilla/gecko/media/LocalMediaDrmBridge.java
netwerk/cookie/test/unit_ipc/test_ipc_parser_0001.js
netwerk/cookie/test/unit_ipc/test_ipc_parser_0019.js
netwerk/cookie/test/unit_ipc/xpcshell.ini
netwerk/standalone/moz.build
netwerk/standalone/nsNetModuleStandalone.cpp
netwerk/standalone/nsNetModuleStandalone.h
python/mozbuild/mozpack/test/test_unify.py
python/mozbuild/mozpack/unify.py
security/nss/fuzz/mpi_target.cc
security/nss/gtests/common/scoped_ptrs.h
security/nss/nss-tool/common/scoped_ptrs.h
services/.eslintrc.js
services/sync/tests/unit/test_extension_storage_crypto.js
servo/components/deny_public_fields/Cargo.toml
servo/components/domobject_derive/Cargo.toml
servo/components/plugins/jstraceable.rs
servo/components/plugins/lints/ban.rs
servo/components/plugins/lints/inheritance_integrity.rs
servo/components/plugins/lints/mod.rs
servo/components/plugins/lints/privatize.rs
servo/components/plugins/lints/transmute_type.rs
servo/components/plugins/lints/unrooted_must_root.rs
servo/components/plugins/reflector.rs
servo/components/plugins/utils.rs
servo/components/script_plugins/ban.rs
servo/components/script_plugins/jstraceable.rs
servo/components/script_plugins/lib.rs
servo/components/script_plugins/unrooted_must_root.rs
servo/components/script_plugins/utils.rs
servo/rust-nightly-date
servo/tests/compiletest/plugin/compile-fail/transmute_type.rs
testing/web-platform/meta/FileAPI/blob/Blob-close.html.ini
testing/web-platform/meta/IndexedDB/open-request-queue.html.ini
testing/web-platform/meta/XMLHttpRequest/setrequestheader-bogus-value.htm.ini
testing/web-platform/meta/content-security-policy/blink-contrib/worker-importscripts-blocked.sub.html.ini
testing/web-platform/meta/dom/events/ProgressEvent.html.ini
testing/web-platform/meta/eventsource/interfaces.html.ini
testing/web-platform/meta/html/browsers/origin/cross-origin-objects/cross-origin-objects-exceptions.html.ini
testing/web-platform/meta/html/semantics/forms/the-input-element/time-2.html.ini
testing/web-platform/meta/html/semantics/forms/the-input-element/week.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-template-element/template-element/template-as-a-descendant.html.ini
testing/web-platform/meta/html/syntax/parsing/html5lib_innerHTML_foreign-fragment.html.ini
testing/web-platform/meta/html/syntax/parsing/html5lib_template.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_autocomplete.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_list.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_valueasdate.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_valueasnumber.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/select_length.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/select_selectedindex.html.ini
testing/web-platform/meta/old-tests/submission/Infraware/Forms/contents/Forms/keygen_labels.html.ini
testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/096.html.ini
testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/098.html.ini
testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/128.html.ini
testing/web-platform/meta/preload/download_resources.html.ini
testing/web-platform/meta/preload/dynamic_adding_preload.html.ini
testing/web-platform/meta/preload/fetch_destination.https.html.ini
testing/web-platform/meta/preload/link_header_preload.html.ini
testing/web-platform/meta/preload/onerror_event.html.ini
testing/web-platform/meta/preload/onload_event.html.ini
testing/web-platform/meta/preload/preload_with_type.html.ini
testing/web-platform/meta/preload/single_download_preload.html.ini
testing/web-platform/meta/presentation-api/controlling-ua/getAvailability_mixedcontent.https.html.ini
testing/web-platform/meta/presentation-api/controlling-ua/getAvailability_sandboxing_error.html.ini
testing/web-platform/meta/presentation-api/controlling-ua/reconnectToPresentation_mixedcontent.https.html.ini
testing/web-platform/meta/presentation-api/controlling-ua/reconnectToPresentation_sandboxing_error.html.ini
testing/web-platform/meta/selection/addRange.html.ini
testing/web-platform/meta/selection/collapse.html.ini
testing/web-platform/meta/selection/extend.html.ini
testing/web-platform/meta/service-workers/service-worker/service-worker-csp-connect.https.html.ini
testing/web-platform/meta/service-workers/service-worker/service-worker-csp-default.https.html.ini
testing/web-platform/meta/service-workers/service-worker/service-worker-csp-script.https.html.ini
testing/web-platform/meta/streams/byte-length-queuing-strategy.https.html.ini
testing/web-platform/meta/streams/count-queuing-strategy.https.html.ini
testing/web-platform/meta/streams/readable-streams/bad-strategies.https.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.https.html.ini
testing/web-platform/meta/streams/readable-streams/brand-checks.https.html.ini
testing/web-platform/meta/streams/readable-streams/cancel.https.html.ini
testing/web-platform/meta/streams/readable-streams/count-queuing-strategy-integration.https.html.ini
testing/web-platform/meta/streams/readable-streams/garbage-collection.https.html.ini
testing/web-platform/meta/streams/readable-streams/general.https.html.ini
testing/web-platform/meta/streams/readable-streams/pipe-through.https.html.ini
testing/web-platform/meta/streams/readable-streams/readable-stream-reader.https.html.ini
testing/web-platform/meta/streams/readable-streams/tee.https.html.ini
testing/web-platform/meta/streams/readable-streams/templated.https.html.ini
testing/web-platform/meta/vr/idlharness.html.ini
testing/web-platform/tests/FileAPI/blob/Blob-close.html
testing/web-platform/tests/docs/configuration.md
testing/web-platform/tests/docs/css-metadata.md
testing/web-platform/tests/docs/css-naming.md
testing/web-platform/tests/docs/css-user-styles.md
testing/web-platform/tests/docs/github-101.md
testing/web-platform/tests/docs/lint-tool.md
testing/web-platform/tests/docs/manual-test.md
testing/web-platform/tests/docs/reftests.md
testing/web-platform/tests/docs/review-checklist.md
testing/web-platform/tests/docs/review-process.md
testing/web-platform/tests/docs/running_tests.md
testing/web-platform/tests/docs/submission-process.md
testing/web-platform/tests/docs/test-format-guidelines.md
testing/web-platform/tests/docs/test-style-guidelines.md
testing/web-platform/tests/docs/test-templates.md
testing/web-platform/tests/dom/events/ProgressEvent.html
testing/web-platform/tests/fullscreen/api/blank.html
testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/MANIFEST
testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/MANIFEST
testing/web-platform/tests/html/browsers/browsing-the-web/scroll-to-fragid/MANIFEST
testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST
testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/prompt/MANIFEST
testing/web-platform/tests/html/browsers/browsing-the-web/unloading-documents/unload/MANIFEST
testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-exceptions.html
testing/web-platform/tests/html/browsers/windows/browsing-context-names/MANIFEST
testing/web-platform/tests/html/browsers/windows/nested-browsing-contexts/MANIFEST
testing/web-platform/tests/html/rendering/bindings/the-button-element-0/.gitkeep
testing/web-platform/tests/html/rendering/bindings/the-keygen-element-0/.gitkeep
testing/web-platform/tests/html/semantics/forms/the-keygen-element/.gitkeep
testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner-ref.html
testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-no-list-owner.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_autocomplete-manual.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_autocomplete.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_list-manual.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_list.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_valueasdate-manual.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_valueasdate.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_valueasnumber-manual.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/input_valueasnumber.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/select_length-manual.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/select_length.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/select_selectedindex-manual.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/Not_Use/select_selectedindex.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/keygen_checkvalidity.html
testing/web-platform/tests/old-tests/submission/Infraware/Forms/contents/Forms/keygen_labels.html
testing/web-platform/tests/old-tests/submission/Opera/script_scheduling/098.html
testing/web-platform/tests/preload/avoid_delaying_onload_link_preload.html
testing/web-platform/tests/preload/delaying_onload_link_preload_after_discovery.html
testing/web-platform/tests/preload/download_resources.html
testing/web-platform/tests/preload/dynamic_adding_preload.html
testing/web-platform/tests/preload/fetch_destination.https.html
testing/web-platform/tests/preload/link-header-preload-delay-onload.html.headers
testing/web-platform/tests/preload/link-header-preload.html.headers
testing/web-platform/tests/preload/link_header_preload.html
testing/web-platform/tests/preload/link_header_preload.html.headers
testing/web-platform/tests/preload/link_header_preload_delay_onload.html
testing/web-platform/tests/preload/link_header_preload_delay_onload.html.headers
testing/web-platform/tests/preload/onerror_event.html
testing/web-platform/tests/preload/onload_event.html
testing/web-platform/tests/preload/preload_with_type.html
testing/web-platform/tests/preload/single_download_late_used_preload.html
testing/web-platform/tests/preload/single_download_preload.html
testing/web-platform/tests/presentation-api/controlling-ua/getAvailability_mixedcontent.https.html
testing/web-platform/tests/presentation-api/controlling-ua/getAvailability_sandboxing_error.html
testing/web-platform/tests/presentation-api/controlling-ua/reconnectToPresentation_mixedcontent.https.html
testing/web-platform/tests/presentation-api/controlling-ua/reconnectToPresentation_sandboxing_error.html
testing/web-platform/tests/presentation-api/controlling-ua/startNewPresentation_mixedcontent-manual.https.html
testing/web-platform/tests/presentation-api/controlling-ua/startNewPresentation_mixedcontent_multiple-manual.https.html
testing/web-platform/tests/presentation-api/controlling-ua/startNewPresentation_sandboxing_error-manual.html
testing/web-platform/tests/selection/addRange.html
testing/web-platform/tests/selection/collapse.html
testing/web-platform/tests/selection/extend.html
testing/web-platform/tests/streams/byte-length-queuing-strategy.https.html
testing/web-platform/tests/streams/count-queuing-strategy.https.html
testing/web-platform/tests/streams/readable-streams/bad-strategies.https.html
testing/web-platform/tests/streams/readable-streams/bad-underlying-sources.https.html
testing/web-platform/tests/streams/readable-streams/brand-checks.https.html
testing/web-platform/tests/streams/readable-streams/cancel.https.html
testing/web-platform/tests/streams/readable-streams/count-queuing-strategy-integration.https.html
testing/web-platform/tests/streams/readable-streams/garbage-collection.https.html
testing/web-platform/tests/streams/readable-streams/general.https.html
testing/web-platform/tests/streams/readable-streams/pipe-through.https.html
testing/web-platform/tests/streams/readable-streams/readable-stream-reader.https.html
testing/web-platform/tests/streams/readable-streams/tee.https.html
testing/web-platform/tests/streams/readable-streams/templated.https.html
testing/web-platform/tests/streams/resources/test-initializer.js
testing/web-platform/tests/vr/idlharness.html
testing/web-platform/tests/websockets/Close-0.htm
testing/web-platform/tests/websockets/Close-NaN.htm
testing/web-platform/tests/websockets/Close-clamp.htm
testing/web-platform/tests/websockets/Close-null.htm
testing/web-platform/tests/websockets/Close-string.htm
testing/web-platform/tests/websockets/Secure-Close-0.htm
testing/web-platform/tests/websockets/Secure-Close-NaN.htm
testing/web-platform/tests/websockets/Secure-Close-null.htm
testing/web-platform/tests/websockets/Secure-Close-string.htm
third_party/rust/bindgen/src/ir/type_collector.rs
third_party/rust/clang-sys/.cargo-checksum.json
toolkit/components/extensions/test/xpcshell/test_ext_storage_sync_crypto.js
toolkit/components/sqlite/moz.build
toolkit/components/sqlite/sqlite_internal.js
toolkit/components/sqlite/tests/xpcshell/.eslintrc.js
toolkit/components/sqlite/tests/xpcshell/data/chrome.manifest
toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_internal.js
toolkit/components/sqlite/tests/xpcshell/data/worker_sqlite_shared.js
toolkit/components/sqlite/tests/xpcshell/test_sqlite_internal.js
toolkit/components/sqlite/tests/xpcshell/xpcshell.ini
toolkit/components/telemetry/TelemetryComms.h
toolkit/components/telemetry/TelemetryIPCAccumulator.cpp
toolkit/components/telemetry/TelemetryIPCAccumulator.h
toolkit/components/telemetry/ipc/TelemetryComms.h
toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp
toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.h
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
tools/profiler/core/v8-support.h
tools/profiler/lul/LulPlatformMacros.h
widget/nsBaseWidget.cpp
--- a/.eslintignore
+++ b/.eslintignore
@@ -93,17 +93,16 @@ devtools/client/shared/*.jsm
 devtools/client/shared/components/reps/reps.js
 devtools/client/shared/webgl-utils.js
 devtools/client/shared/widgets/*.jsm
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/net/**
 devtools/client/webconsole/test/**
 devtools/client/webconsole/console-output.js
 devtools/client/webconsole/hudservice.js
-devtools/client/webconsole/utils.js
 devtools/client/webconsole/webconsole-connection-proxy.js
 devtools/client/webconsole/webconsole.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
 devtools/server/actors/webconsole.js
 devtools/server/actors/object.js
 devtools/server/actors/script.js
 devtools/server/actors/styleeditor.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Touch clobber again because of bug 1336456
+Touching clobber for Telemetry IPC refactor in bug 1339749.
--- a/Makefile.in
+++ b/Makefile.in
@@ -181,21 +181,16 @@ endif
 	$(addprefix $(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )$*) ,$(wildcard _build_manifests/install/$(subst /,_,$*)))
 
 # Dummy wrapper rule to allow the faster backend to piggy back
 $(addprefix install-,$(subst /,_,$(filter dist/%,$(install_manifests)))): install-dist_%: install-dist/% ;
 
 .PHONY: install-tests
 install-tests: install-test-files
 
-# We no longer run "make install-tests" directly before running tests, but we still
-# want to depend on things like config.status, hence this target.
-.PHONY: run-tests-deps
-run-tests-deps: $(install_manifest_depends)
-
 # Force --no-remove, because $objdir/_tests is handled by multiple manifests.
 .PHONY: install-test-files
 install-test-files:
 	$(call py_action,process_install_manifest,--no-remove _tests _build_manifests/install/_test_files)
 
 include $(topsrcdir)/build/moz-automation.mk
 
 # dist and _tests should be purged during cleaning. However, we don't want them
@@ -264,23 +259,18 @@ DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_s
 else
 DUMP_SYMS_BIN ?= $(topsrcdir)/toolkit/crashreporter/tools/win32/dump_syms_vc$(_MSC_VER).exe
 endif
 # PDB files don't get moved to dist, so we need to scan the whole objdir
 MAKE_SYM_STORE_PATH := .
 endif
 ifeq ($(OS_ARCH),Darwin)
 # need to pass arch flags for universal builds
-ifdef UNIVERSAL_BINARY
-MAKE_SYM_STORE_ARGS := -c --vcs-info
-MAKE_SYM_STORE_PATH := $(DIST)/bin $(UNIFY_DIST)/bin
-else
 MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
 MAKE_SYM_STORE_PATH := $(DIST)/bin
-endif
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 endif
 ifeq (,$(filter-out Linux SunOS,$(OS_ARCH)))
 MAKE_SYM_STORE_ARGS := -c --vcs-info
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 MAKE_SYM_STORE_PATH := $(DIST)/bin
 endif
 MAKE_SYM_STORE_ARGS += --install-manifest=$(DEPTH)/_build_manifests/install/dist_include,$(DIST)/include
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -8,16 +8,17 @@
 
 #include "Accessible-inl.h"
 #include "ApplicationAccessibleWrap.h"
 #include "InterfaceInitFuncs.h"
 #include "nsAccUtils.h"
 #include "mozilla/a11y/PDocAccessible.h"
 #include "OuterDocAccessible.h"
 #include "ProxyAccessible.h"
+#include "DocAccessibleParent.h"
 #include "RootAccessible.h"
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
 #include "nsMai.h"
 #include "nsMaiHyperlink.h"
 #include "nsString.h"
 #include "nsStateMap.h"
 #include "mozilla/a11y/Platform.h"
--- a/accessible/atk/nsMaiInterfaceTable.cpp
+++ b/accessible/atk/nsMaiInterfaceTable.cpp
@@ -322,44 +322,47 @@ getSelectedRowsCB(AtkTable *aTable, gint
 }
 
 static gboolean
 isColumnSelectedCB(AtkTable *aTable, gint aColIdx)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
   if (accWrap) {
     return static_cast<gboolean>(accWrap->AsTable()->IsColSelected(aColIdx));
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
     return static_cast<gboolean>(proxy->TableColumnSelected(aColIdx));
   }
 
   return FALSE;
 }
 
 static gboolean
 isRowSelectedCB(AtkTable *aTable, gint aRowIdx)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
   if (accWrap) {
     return static_cast<gboolean>(accWrap->AsTable()->IsRowSelected(aRowIdx));
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
     return static_cast<gboolean>(proxy->TableRowSelected(aRowIdx));
   }
 
   return FALSE;
 }
 
 static gboolean
 isCellSelectedCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
   if (accWrap) {
     return static_cast<gboolean>(accWrap->AsTable()->
       IsCellSelected(aRowIdx, aColIdx));
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
     return static_cast<gboolean>(proxy->TableCellSelected(aRowIdx, aColIdx));
   }
 
   return FALSE;
 }
 }
 
 void
--- a/accessible/atk/nsMaiInterfaceText.cpp
+++ b/accessible/atk/nsMaiInterfaceText.cpp
@@ -495,17 +495,18 @@ getTextSelectionCB(AtkText *aText, gint 
       return nullptr;
     }
 
     text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset);
     *aStartOffset = startOffset;
     *aEndOffset = endOffset;
 
     return getTextCB(aText, *aStartOffset, *aEndOffset);
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
     nsString data;
     proxy->SelectionBoundsAt(aSelectionNum, data, &startOffset, &endOffset);
     *aStartOffset = startOffset;
     *aEndOffset = endOffset;
 
     NS_ConvertUTF16toUTF8 dataAsUTF8(data);
     return (dataAsUTF8.get()) ? g_strdup(dataAsUTF8.get()) : nullptr;
   }
@@ -521,17 +522,18 @@ addTextSelectionCB(AtkText *aText,
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (accWrap) {
     HyperTextAccessible* text = accWrap->AsHyperText();
     if (!text || !text->IsTextRole()) {
       return FALSE;
     }
 
     return text->AddToSelection(aStartOffset, aEndOffset);
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
     return proxy->AddToSelection(aStartOffset, aEndOffset);
   }
 
   return FALSE;
 }
 
 static gboolean
 removeTextSelectionCB(AtkText *aText,
@@ -540,17 +542,18 @@ removeTextSelectionCB(AtkText *aText,
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (accWrap) {
     HyperTextAccessible* text = accWrap->AsHyperText();
     if (!text || !text->IsTextRole()) {
       return FALSE;
     }
 
     return text->RemoveFromSelection(aSelectionNum);
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
     return proxy->RemoveFromSelection(aSelectionNum);
   }
 
   return FALSE;
 }
 
 static gboolean
 setTextSelectionCB(AtkText *aText, gint aSelectionNum,
@@ -559,17 +562,18 @@ setTextSelectionCB(AtkText *aText, gint 
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (accWrap) {
     HyperTextAccessible* text = accWrap->AsHyperText();
     if (!text || !text->IsTextRole()) {
       return FALSE;
     }
 
     return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
-  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+  }
+  if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
     return proxy->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
   }
 
   return FALSE;
 }
 
 static gboolean
 setCaretOffsetCB(AtkText *aText, gint aOffset)
--- a/accessible/generic/OuterDocAccessible.cpp
+++ b/accessible/generic/OuterDocAccessible.cpp
@@ -25,16 +25,22 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 
 OuterDocAccessible::
   OuterDocAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
   mType = eOuterDocType;
 
+#ifdef XP_WIN
+  if (DocAccessibleParent* remoteDoc = RemoteChildDoc()) {
+    remoteDoc->SendParentCOMProxy();
+  }
+#endif
+
   // Request document accessible for the content document to make sure it's
   // created. It will appended to outerdoc accessible children asynchronously.
   nsIDocument* outerDoc = mContent->GetUncomposedDoc();
   if (outerDoc) {
     nsIDocument* innerDoc = outerDoc->GetSubDocumentFor(mContent);
     if (innerDoc)
       GetAccService()->GetDocAccessible(innerDoc);
   }
@@ -202,17 +208,17 @@ OuterDocAccessible::GetChildAt(uint32_t 
   if (!remoteChild) {
     return nullptr;
   }
   return WrapperFor(remoteChild);
 }
 
 #endif // defined(XP_WIN)
 
-ProxyAccessible*
+DocAccessibleParent*
 OuterDocAccessible::RemoteChildDoc() const
 {
   dom::TabParent* tab = dom::TabParent::GetFrom(GetContent());
   if (!tab)
     return nullptr;
 
   return tab->GetTopLevelDocAccessible();
 }
--- a/accessible/generic/OuterDocAccessible.h
+++ b/accessible/generic/OuterDocAccessible.h
@@ -5,17 +5,17 @@
 
 #ifndef MOZILLA_A11Y_OUTERDOCACCESSIBLE_H_
 #define MOZILLA_A11Y_OUTERDOCACCESSIBLE_H_
 
 #include "AccessibleWrap.h"
 
 namespace mozilla {
 namespace a11y {
-class ProxyAccessible;
+class DocAccessibleParent;
 
 /**
  * Used for <browser>, <frame>, <iframe>, <page> or editor> elements.
  * 
  * In these variable names, "outer" relates to the OuterDocAccessible as
  * opposed to the DocAccessibleWrap which is "inner". The outer node is
  * a something like tags listed above, whereas the inner node corresponds to
  * the inner document root.
@@ -23,17 +23,17 @@ class ProxyAccessible;
 
 class OuterDocAccessible final : public AccessibleWrap
 {
 public:
   OuterDocAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   NS_DECL_ISUPPORTS_INHERITED
 
-  ProxyAccessible* RemoteChildDoc() const;
+  DocAccessibleParent* RemoteChildDoc() const;
 
   // Accessible
   virtual void Shutdown() override;
   virtual mozilla::a11y::role NativeRole() override;
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild) override;
 
   virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
--- a/accessible/interfaces/gecko/Makefile.in
+++ b/accessible/interfaces/gecko/Makefile.in
@@ -1,19 +1,21 @@
 # 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/.
 
-GARBAGE += $(MIDL_GENERATED_FILES) done_gen dlldata.c
+GARBAGE += $(MIDL_GENERATED_FILES) done_gen
 
 MIDL_GENERATED_FILES = \
-	IGeckoCustom.h \
-	IGeckoCustom_p.c \
-	IGeckoCustom_i.c \
-	$(NULL)
+  dlldata.c \
+  IGeckoCustom.h \
+  IGeckoCustom_p.c \
+  IGeckoCustom_i.c \
+  IGeckoCustom.tlb \
+  $(NULL)
 
 $(MIDL_GENERATED_FILES): done_gen
 
 done_gen: IGeckoCustom.idl
 	$(MIDL) $(MIDL_FLAGS) -I $(srcdir) -Oicf $(srcdir)/IGeckoCustom.idl
 	touch $@
 
 export:: done_gen
--- a/accessible/interfaces/gecko/moz.build
+++ b/accessible/interfaces/gecko/moz.build
@@ -6,17 +6,21 @@
 
 SOURCES += [
     '!dlldata.c',
     '!IGeckoCustom_i.c',
     '!IGeckoCustom_p.c',
 ]
 
 GENERATED_FILES += [
+    'dlldata.c',
+    'IGeckoCustom.h',
     'IGeckoCustom.tlb',
+    'IGeckoCustom_i.c',
+    'IGeckoCustom_p.c',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 # The Windows MIDL code generator creates things like:
 #
 #   #endif !_MIDL_USE_GUIDDEF_
 #
--- a/accessible/interfaces/ia2/Makefile.in
+++ b/accessible/interfaces/ia2/Makefile.in
@@ -1,16 +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/.
 
 IA2DIR        = $(topsrcdir)/other-licenses/ia2
 
 GARBAGE       += $(MIDL_GENERATED_FILES) \
                  $(MIDL_UNUSED_GENERATED_FILES) \
+                 midl_done \
+                 typelib_done \
                  $(NULL)
 
 # Please keep this list in sync with the moz.build file until the rest of this
 # Makefile is ported over.
 MIDL_INTERFACES = \
   Accessible2.idl \
   Accessible2_2.idl \
   Accessible2_3.idl \
@@ -48,20 +50,21 @@ MIDL_LIBRARIES = \
 CSRCS = \
   dlldata.c \
   $(MIDL_INTERFACES:%.idl=%_p.c) \
   $(MIDL_INTERFACES:%.idl=%_i.c) \
   $(NULL)
 
 MIDL_GENERATED_FILES = \
   dlldata.c \
+  $(MIDL_ENUMS:%.idl=%.h) \
   $(MIDL_INTERFACES:%.idl=%_p.c) \
   $(MIDL_INTERFACES:%.idl=%_i.c) \
   $(MIDL_INTERFACES:%.idl=%.h) \
-  $(MIDL_ENUMS:%.idl=%.h) \
+  $(MIDL_LIBRARIES:%.idl=%.tlb) \
   $(NULL)
 
 # We want to generate a .tlb from MIDL_LIBRARIES, but midl also generates
 # a bunch of .h and .c files that we're not interested in.
 MIDL_UNUSED_GENERATED_FILES = \
   $(MIDL_LIBRARIES:%.idl=%_p.c) \
   $(MIDL_LIBRARIES:%.idl=%_i.c) \
   $(MIDL_LIBRARIES:%.idl=%.h) \
@@ -75,18 +78,18 @@ midl_FILES := $(filter %.h %_i.c,$(MIDL_
 midl_DEST = $(DIST)/include
 midl_TARGET := midl
 
 export:: midl
 
 include $(topsrcdir)/config/rules.mk
 
 # generate list of to-be-generated files that are missing
-# but ignore special file dlldata.c
-missing:=$(strip $(foreach onefile,$(strip $(subst dlldata.c,,$(MIDL_GENERATED_FILES))),$(if $(wildcard $(onefile)),,$(onefile))))
+# but ignore special file dlldata.c and .tlb files
+missing:=$(strip $(foreach onefile,$(strip $(patsubst %.tlb,,$(subst dlldata.c,,$(MIDL_GENERATED_FILES)))),$(if $(wildcard $(onefile)),,$(onefile))))
 
 missing_base:=$(sort $(basename $(subst _p.c,,$(subst _i.c,,$(missing)))))
 
 $(MIDL_GENERATED_FILES) : midl_done typelib_done
 
 ifneq ("$(missing)","")
 midl_done : FORCE
 endif
@@ -94,17 +97,17 @@ endif
 midl_done : $(addprefix $(IA2DIR)/,$(MIDL_INTERFACES) $(MIDL_ENUMS))
 	for idl in $(sort $(subst FORCE,,$?) $(addsuffix .idl,$(addprefix $(IA2DIR)/,$(missing_base)))); do \
 	  $(MIDL) $(MIDL_FLAGS) -app_config -I $(IA2DIR) -Oicf $$idl; \
 	done
 	touch $@
 
 # The intent of this rule is to generate the .tlb file that is referenced in the
 # .rc file for IA2Marshal.dll
-typelib_done : $(MIDL_LIBRARIES)
+typelib_done : $(addprefix $(srcdir)/,$(MIDL_LIBRARIES))
 	for idl in $?; do \
-		$(MIDL) $(MIDL_FLAGS) -app_config -I $(IA2DIR) -D _MIDL_DECLARE_WIREM_HANDLE -dlldata `basename $$idl .idl`.c -Oicf $$idl; \
+	  $(MIDL) $(MIDL_FLAGS) -app_config -I $(IA2DIR) -D _MIDL_DECLARE_WIREM_HANDLE -dlldata `basename $$idl .idl`.c -Oicf $$idl; \
 	done
 	touch $@
 
 # This marshall dll is NOT registered in the installer (agreed to by IA2 participants)
 register::
 	regsvr32 -s $(DIST)/bin/$(SHARED_LIBRARY)
--- a/accessible/interfaces/ia2/moz.build
+++ b/accessible/interfaces/ia2/moz.build
@@ -14,16 +14,78 @@ OS_LIBS += [
     'uuid',
     'kernel32',
     'rpcrt4',
     'ole32',
     'oleaut32',
 ]
 
 GENERATED_FILES += [
+    'Accessible2.h',
+    'Accessible2_2.h',
+    'Accessible2_2_i.c',
+    'Accessible2_2_p.c',
+    'Accessible2_3.h',
+    'Accessible2_3_i.c',
+    'Accessible2_3_p.c',
+    'Accessible2_i.c',
+    'Accessible2_p.c',
+    'AccessibleAction.h',
+    'AccessibleAction_i.c',
+    'AccessibleAction_p.c',
+    'AccessibleApplication.h',
+    'AccessibleApplication_i.c',
+    'AccessibleApplication_p.c',
+    'AccessibleComponent.h',
+    'AccessibleComponent_i.c',
+    'AccessibleComponent_p.c',
+    'AccessibleDocument.h',
+    'AccessibleDocument_i.c',
+    'AccessibleDocument_p.c',
+    'AccessibleEditableText.h',
+    'AccessibleEditableText_i.c',
+    'AccessibleEditableText_p.c',
+    'AccessibleEventId.h',
+    'AccessibleHyperlink.h',
+    'AccessibleHyperlink_i.c',
+    'AccessibleHyperlink_p.c',
+    'AccessibleHypertext.h',
+    'AccessibleHypertext2.h',
+    'AccessibleHypertext2_i.c',
+    'AccessibleHypertext2_p.c',
+    'AccessibleHypertext_i.c',
+    'AccessibleHypertext_p.c',
+    'AccessibleImage.h',
+    'AccessibleImage_i.c',
+    'AccessibleImage_p.c',
+    'AccessibleRelation.h',
+    'AccessibleRelation_i.c',
+    'AccessibleRelation_p.c',
+    'AccessibleRole.h',
+    'AccessibleStates.h',
+    'AccessibleTable.h',
+    'AccessibleTable2.h',
+    'AccessibleTable2_i.c',
+    'AccessibleTable2_p.c',
+    'AccessibleTable_i.c',
+    'AccessibleTable_p.c',
+    'AccessibleTableCell.h',
+    'AccessibleTableCell_i.c',
+    'AccessibleTableCell_p.c',
+    'AccessibleText.h',
+    'AccessibleText2.h',
+    'AccessibleText2_i.c',
+    'AccessibleText2_p.c',
+    'AccessibleText_i.c',
+    'AccessibleText_p.c',
+    'AccessibleValue.h',
+    'AccessibleValue_i.c',
+    'AccessibleValue_p.c',
+    'dlldata.c',
+    'IA2CommonTypes.h',
     'IA2Typelib.tlb',
 ]
 
 RCINCLUDE = 'IA2Marshal.rc'
 
 # The Windows MIDL code generator creates things like:
 #
 #   #endif !_MIDL_USE_GUIDDEF_
--- a/accessible/interfaces/msaa/Makefile.in
+++ b/accessible/interfaces/msaa/Makefile.in
@@ -1,25 +1,27 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-GARBAGE += $(MIDL_GENERATED_FILES) done_gen dlldata.c
+GARBAGE += $(MIDL_GENERATED_FILES) done_gen
 
 MIDL_GENERATED_FILES = \
-	ISimpleDOMNode.h \
-	ISimpleDOMNode_p.c \
-	ISimpleDOMNode_i.c \
-	ISimpleDOMDocument.h \
-	ISimpleDOMDocument_p.c \
-	ISimpleDOMDocument_i.c \
-	ISimpleDOMText.h \
-	ISimpleDOMText_p.c \
-	ISimpleDOMText_i.c \
-	$(NULL)
+  dlldata.c \
+  ISimpleDOMNode.h \
+  ISimpleDOMNode_p.c \
+  ISimpleDOMNode_i.c \
+  ISimpleDOMNode.tlb \
+  ISimpleDOMDocument.h \
+  ISimpleDOMDocument_p.c \
+  ISimpleDOMDocument_i.c \
+  ISimpleDOMText.h \
+  ISimpleDOMText_p.c \
+  ISimpleDOMText_i.c \
+  $(NULL)
 
 $(MIDL_GENERATED_FILES): done_gen
 
 done_gen: ISimpleDOMNode.idl \
           ISimpleDOMDocument.idl \
           ISimpleDOMText.idl
 
 	$(MIDL) $(MIDL_FLAGS) -I $(srcdir) -Oicf $(srcdir)/ISimpleDOMNode.idl
--- a/accessible/interfaces/msaa/moz.build
+++ b/accessible/interfaces/msaa/moz.build
@@ -22,17 +22,27 @@ DEFFILE = SRCDIR + '/AccessibleMarshal.d
 
 OS_LIBS += [
     'kernel32',
     'rpcrt4',
     'oleaut32',
 ]
 
 GENERATED_FILES += [
+    'dlldata.c',
+    'ISimpleDOMDocument.h',
+    'ISimpleDOMDocument_i.c',
+    'ISimpleDOMDocument_p.c',
+    'ISimpleDOMNode.h',
     'ISimpleDOMNode.tlb',
+    'ISimpleDOMNode_i.c',
+    'ISimpleDOMNode_p.c',
+    'ISimpleDOMText.h',
+    'ISimpleDOMText_i.c',
+    'ISimpleDOMText_p.c',
 ]
 
 RCINCLUDE = 'AccessibleMarshal.rc'
 
 # The Windows MIDL code generator creates things like:
 #
 #   #endif !_MIDL_USE_GUIDDEF_
 #
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -24,17 +24,17 @@ namespace a11y {
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
                                    const bool& aFromUser)
 {
   if (mShutdown)
     return IPC_OK();
 
-  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+  MOZ_ASSERT(CheckDocTree());
 
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
     return IPC_FAIL_NO_REASON(this);
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
@@ -70,17 +70,17 @@ DocAccessibleParent::RecvShowEvent(const
 
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
-  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+  MOZ_ASSERT(CheckDocTree());
 
   ProxyAccessible* target = parent->ChildAt(newChildIdx);
   ProxyShowHideEvent(target, parent, true, aFromUser);
 
   if (!nsCoreUtils::AccEventObserversExist()) {
     return IPC_OK();
   }
 
@@ -147,17 +147,17 @@ DocAccessibleParent::AddSubtree(ProxyAcc
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID,
                                    const bool& aFromUser)
 {
   if (mShutdown)
     return IPC_OK();
 
-  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+  MOZ_ASSERT(CheckDocTree());
 
   // We shouldn't actually need this because mAccessibles shouldn't have an
   // entry for the document itself, but it doesn't hurt to be explicit.
   if (!aRootID) {
     NS_ERROR("trying to hide entire document?");
     return IPC_FAIL_NO_REASON(this);
   }
 
@@ -189,17 +189,17 @@ DocAccessibleParent::RecvHideEvent(const
     nsIDOMNode* node = nullptr;
     event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
                                 xpcNext, xpcPrev);
   }
 
   parent->RemoveChild(root);
   root->Shutdown();
 
-  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+  MOZ_ASSERT(CheckDocTree());
 
   if (event) {
     nsCoreUtils::DispatchAccEvent(Move(event));
   }
 
   return IPC_OK();
 }
 
@@ -386,23 +386,23 @@ mozilla::ipc::IPCResult
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
 {
   // One document should never directly be the child of another.
   // We should always have at least an outer doc accessible in between.
   MOZ_ASSERT(aID);
   if (!aID)
     return IPC_FAIL(this, "ID is 0!");
 
-  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+  MOZ_ASSERT(CheckDocTree());
 
   auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
   childDoc->Unbind();
   ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
   MOZ_ASSERT(result);
-  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+  MOZ_ASSERT(CheckDocTree());
   if (!result) {
     return result;
   }
   return IPC_OK();
 }
 
 ipc::IPCResult
 DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
@@ -422,18 +422,18 @@ DocAccessibleParent::AddChildDoc(DocAcce
   // here.
   if (outerDoc->ChildrenCount() > 1 ||
       (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
     return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
   }
 
   aChildDoc->SetParent(outerDoc);
   outerDoc->SetChildDoc(aChildDoc);
-  mChildDocs.AppendElement(aChildDoc);
-  aChildDoc->mParentDoc = this;
+  mChildDocs.AppendElement(aChildDoc->IProtocol::Id());
+  aChildDoc->mParentDoc = IProtocol::Id();
 
   if (aCreating) {
     ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
   }
 
   return IPC_OK();
 }
 
@@ -466,46 +466,57 @@ DocAccessibleParent::Destroy()
   uint32_t childDocCount = mChildDocs.Length();
   for (uint32_t i = 0; i < childDocCount; i++) {
     for (uint32_t j = i + 1; j < childDocCount; j++) {
       MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
     }
   }
 
   for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
-    mChildDocs[i]->Destroy();
+    ChildDocAt(i)->Destroy();
 
   for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
     MOZ_ASSERT(iter.Get()->mProxy != this);
     ProxyDestroyed(iter.Get()->mProxy);
     iter.Remove();
   }
 
   // The code above should have already completely cleared these, but to be
   // extra safe make sure they are cleared here.
   mAccessibles.Clear();
   mChildDocs.Clear();
 
   DocManager::NotifyOfRemoteDocShutdown(this);
   ProxyDestroyed(this);
-  if (mParentDoc)
-    mParentDoc->RemoveChildDoc(this);
+  if (DocAccessibleParent* parentDoc = ParentDoc())
+    parentDoc->RemoveChildDoc(this);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
+DocAccessibleParent*
+DocAccessibleParent::ParentDoc() const
+{
+  if (mParentDoc == kNoParentDoc) {
+    return nullptr;
+  }
+
+  return LiveDocs().Get(mParentDoc);
+}
+
 bool
 DocAccessibleParent::CheckDocTree() const
 {
   size_t childDocs = mChildDocs.Length();
   for (size_t i = 0; i < childDocs; i++) {
-    if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
+    const DocAccessibleParent* childDoc = ChildDocAt(i);
+    if (!childDoc || childDoc->ParentDoc() != this)
       return false;
 
-    if (!mChildDocs[i]->CheckDocTree()) {
+    if (!childDoc->CheckDocTree()) {
       return false;
     }
   }
 
   return true;
 }
 
 xpcAccessibleGeneric*
@@ -513,76 +524,92 @@ DocAccessibleParent::GetXPCAccessible(Pr
 {
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   MOZ_ASSERT(doc);
 
   return doc->GetXPCAccessible(aProxy);
 }
 
 #if defined(XP_WIN)
+void
+DocAccessibleParent::MaybeInitWindowEmulation()
+{
+  if (!nsWinUtils::IsWindowEmulationStarted()) {
+    return;
+  }
+
+  // XXX get the bounds from the tabParent instead of poking at accessibles
+  // which might not exist yet.
+  Accessible* outerDoc = OuterDocOfRemoteBrowser();
+  if (!outerDoc) {
+    return;
+  }
+
+  RootAccessible* rootDocument = outerDoc->RootAccessible();
+  MOZ_ASSERT(rootDocument);
+
+  bool isActive = true;
+  nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
+  if (Compatibility::IsDolphin()) {
+    rect = Bounds();
+    nsIntRect rootRect = rootDocument->Bounds();
+    rect.x = rootRect.x - rect.x;
+    rect.y -= rootRect.y;
+
+    auto tab = static_cast<dom::TabParent*>(Manager());
+    tab->GetDocShellIsActive(&isActive);
+  }
+
+  IAccessibleHolder hWndAccHolder;
+  HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
+  HWND hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
+                                             parentWnd, rect.x, rect.y,
+                                             rect.width, rect.height,
+                                             isActive);
+  if (hWnd) {
+    // Attach accessible document to the emulated native window
+    ::SetPropW(hWnd, kPropNameDocAccParent, (HANDLE)this);
+    SetEmulatedWindowHandle(hWnd);
+    IAccessible* rawHWNDAcc = nullptr;
+    if (SUCCEEDED(::AccessibleObjectFromWindow(hWnd, OBJID_WINDOW,
+                                               IID_IAccessible,
+                                               (void**)&rawHWNDAcc))) {
+      hWndAccHolder.Set(IAccessibleHolder::COMPtrType(rawHWNDAcc));
+    }
+  }
+
+  Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle),
+                               hWndAccHolder);
+}
+
 /**
  * @param aCOMProxy COM Proxy to the document in the content process.
  */
 void
-DocAccessibleParent::SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy)
+DocAccessibleParent::SendParentCOMProxy()
 {
-  SetCOMInterface(aCOMProxy);
-
   // Make sure that we're not racing with a tab shutdown
   auto tab = static_cast<dom::TabParent*>(Manager());
   MOZ_ASSERT(tab);
   if (tab->IsDestroyed()) {
     return;
   }
 
   Accessible* outerDoc = OuterDocOfRemoteBrowser();
+  if (!outerDoc) {
+    return;
+  }
 
   IAccessible* rawNative = nullptr;
-  if (outerDoc) {
-    outerDoc->GetNativeInterface((void**) &rawNative);
-    MOZ_ASSERT(rawNative);
-  }
+  outerDoc->GetNativeInterface((void**) &rawNative);
+  MOZ_ASSERT(rawNative);
 
   IAccessibleHolder::COMPtrType ptr(rawNative);
   IAccessibleHolder holder(Move(ptr));
-
-  IAccessibleHolder hWndAccHolder;
-  if (nsWinUtils::IsWindowEmulationStarted()) {
-    RootAccessible* rootDocument = outerDoc->RootAccessible();
-    MOZ_ASSERT(rootDocument);
-
-    bool isActive = true;
-    nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
-    if (Compatibility::IsDolphin()) {
-      rect = Bounds();
-      nsIntRect rootRect = rootDocument->Bounds();
-      rect.x = rootRect.x - rect.x;
-      rect.y -= rootRect.y;
-      tab->GetDocShellIsActive(&isActive);
-    }
-
-    HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
-    HWND hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
-                                               parentWnd, rect.x, rect.y,
-                                               rect.width, rect.height,
-                                               isActive);
-    if (hWnd) {
-      // Attach accessible document to the emulated native window
-      ::SetPropW(hWnd, kPropNameDocAccParent, (HANDLE)this);
-      SetEmulatedWindowHandle(hWnd);
-      IAccessible* rawHWNDAcc = nullptr;
-      if (SUCCEEDED(::AccessibleObjectFromWindow(hWnd, OBJID_WINDOW,
-                                                 IID_IAccessible,
-                                                 (void**)&rawHWNDAcc))) {
-        hWndAccHolder.Set(IAccessibleHolder::COMPtrType(rawHWNDAcc));
-      }
-    }
-  }
-  Unused << SendParentCOMProxy(holder, reinterpret_cast<uintptr_t>(
-                               mEmulatedWindowHandle), hWndAccHolder);
+  Unused << PDocAccessibleParent::SendParentCOMProxy(holder);
 }
 
 void
 DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle)
 {
   if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
     ::DestroyWindow(mEmulatedWindowHandle);
   }
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -23,24 +23,25 @@ class xpcAccessibleGeneric;
  * These objects live in the main process and comunicate with and represent
  * an accessible document in a content process.
  */
 class DocAccessibleParent : public ProxyAccessible,
     public PDocAccessibleParent
 {
 public:
   DocAccessibleParent() :
-    ProxyAccessible(this), mParentDoc(nullptr),
+    ProxyAccessible(this), mParentDoc(kNoParentDoc),
     mTopLevel(false), mShutdown(false)
 #if defined(XP_WIN)
                                       , mEmulatedWindowHandle(nullptr)
 #endif // defined(XP_WIN)
   { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
   ~DocAccessibleParent()
   {
+    LiveDocs().Remove(IProtocol::Id());
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
     MOZ_ASSERT(mChildDocs.Length() == 0);
     MOZ_ASSERT(!ParentDoc());
   }
 
   void SetTopLevel() { mTopLevel = true; }
   bool IsTopLevel() const { return mTopLevel; }
 
@@ -53,16 +54,21 @@ public:
    */
   void MarkAsShutdown()
   {
     MOZ_ASSERT(mChildDocs.IsEmpty());
     MOZ_ASSERT(mAccessibles.Count() == 0);
     mShutdown = true;
   }
 
+  /**
+   * Add this document to the set of tracked documents.
+   */
+  void AddToMap() { LiveDocs().Put(IProtocol::Id(), this); }
+
   /*
    * Called when a message from a document in a child process notifies the main
    * process it is firing an event.
    */
   virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, const uint32_t& aType)
     override;
 
   virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
@@ -97,26 +103,27 @@ public:
 
     SetParent(nullptr);
   }
 
   virtual mozilla::ipc::IPCResult RecvShutdown() override;
   void Destroy();
   virtual void ActorDestroy(ActorDestroyReason aWhy) override
   {
-    MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
+    MOZ_ASSERT(CheckDocTree());
     if (!mShutdown)
       Destroy();
   }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
-  DocAccessibleParent* ParentDoc() const { return mParentDoc; }
+  DocAccessibleParent* ParentDoc() const;
+  static const int32_t kNoParentDoc = INT32_MIN;
 
   /*
    * Called when a document in a content process notifies the main process of a
    * new child document.
    */
   ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc,
                              uint64_t aParentID, bool aCreating = true);
 
@@ -126,18 +133,19 @@ public:
    */
   void RemoveChildDoc(DocAccessibleParent* aChildDoc)
   {
     ProxyAccessible* parent = aChildDoc->Parent();
     MOZ_ASSERT(parent);
     if (parent) {
       aChildDoc->Parent()->ClearChildDoc(aChildDoc);
     }
-    mChildDocs.RemoveElement(aChildDoc);
-    aChildDoc->mParentDoc = nullptr;
+    DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->IProtocol::Id());
+    aChildDoc->mParentDoc = kNoParentDoc;
+    MOZ_ASSERT(result);
     MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
   }
 
   void RemoveAccessible(ProxyAccessible* aAccessible)
   {
     MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
     mAccessibles.RemoveEntry(aAccessible->ID());
   }
@@ -154,20 +162,23 @@ public:
     return e ? e->mProxy : nullptr;
   }
 
   const ProxyAccessible* GetAccessible(uintptr_t aID) const
     { return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); }
 
   size_t ChildDocCount() const { return mChildDocs.Length(); }
   const DocAccessibleParent* ChildDocAt(size_t aIdx) const
-    { return mChildDocs[aIdx]; }
+  { return const_cast<DocAccessibleParent*>(this)->ChildDocAt(aIdx); }
+  DocAccessibleParent* ChildDocAt(size_t aIdx)
+    { return LiveDocs().Get(mChildDocs[aIdx]); }
 
 #if defined(XP_WIN)
-  void SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy);
+  void MaybeInitWindowEmulation();
+  void SendParentCOMProxy();
 
   virtual mozilla::ipc::IPCResult RecvGetWindowedPluginIAccessible(
       const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) override;
 
   /**
    * Set emulated native window handle for a document.
    * @param aWindowHandle emulated native window handle
    */
@@ -201,29 +212,36 @@ private:
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
   MOZ_MUST_USE bool CheckDocTree() const;
   xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
 
-  nsTArray<DocAccessibleParent*> mChildDocs;
-  DocAccessibleParent* mParentDoc;
+  nsTArray<int32_t> mChildDocs;
+  int32_t mParentDoc;
 
 #if defined(XP_WIN)
   // The handle associated with the emulated window that contains this document
   HWND mEmulatedWindowHandle;
 #endif
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
   nsTHashtable<ProxyEntry> mAccessibles;
   bool mTopLevel;
   bool mShutdown;
+
+  static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*>&
+    LiveDocs()
+    {
+      static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*> sLiveDocs;
+      return sLiveDocs;
+    }
 };
 
 }
 }
 
 #endif
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -41,36 +41,42 @@ DocAccessibleChild::Shutdown()
     return;
   }
 
   PushDeferredEvent(MakeUnique<SerializedShutdown>(this));
   DetachDocument();
 }
 
 ipc::IPCResult
-DocAccessibleChild::RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy,
-                                       const WindowsHandle& aEmulatedWindowHandle,
-                                       const IAccessibleHolder& aEmulatedWindowCOMProxy)
+DocAccessibleChild::RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy)
 {
   MOZ_ASSERT(!mParentProxy && !aParentCOMProxy.IsNull());
   mParentProxy.reset(const_cast<IAccessibleHolder&>(aParentCOMProxy).Release());
   SetConstructedInParentProcess();
+
+  for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
+    mDeferredEvents[i]->Dispatch();
+  }
+
+  mDeferredEvents.Clear();
+
+  return IPC_OK();
+}
+
+ipc::IPCResult
+DocAccessibleChild::RecvEmulatedWindow(const WindowsHandle& aEmulatedWindowHandle,
+                                       const IAccessibleHolder& aEmulatedWindowCOMProxy)
+{
   mEmulatedWindowHandle = reinterpret_cast<HWND>(aEmulatedWindowHandle);
   if (!aEmulatedWindowCOMProxy.IsNull()) {
     MOZ_ASSERT(!mEmulatedWindowProxy);
     mEmulatedWindowProxy.reset(
       const_cast<IAccessibleHolder&>(aEmulatedWindowCOMProxy).Release());
   }
 
-  for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
-    mDeferredEvents[i]->Dispatch();
-  }
-
-  mDeferredEvents.Clear();
-
   return IPC_OK();
 }
 
 void
 DocAccessibleChild::PushDeferredEvent(UniquePtr<DeferredEvent> aEvent)
 {
   DocAccessibleChild* topLevelIPCDoc = nullptr;
 
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -23,19 +23,20 @@ class DocAccessibleChild : public DocAcc
 {
 public:
   explicit DocAccessibleChild(DocAccessible* aDoc);
   ~DocAccessibleChild();
 
   virtual void Shutdown() override;
 
   virtual ipc::IPCResult
-  RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy,
-                     const WindowsHandle& aEmulatedWindowHandle,
-                     const IAccessibleHolder& aEmulatedWindowCOMProxy) override;
+  RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy) override;
+  virtual ipc::IPCResult
+    RecvEmulatedWindow(const WindowsHandle& aEmulatedWindowHandle,
+                       const IAccessibleHolder& aEmulatedWindowCOMProxy) override;
 
   HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
   IAccessible* GetEmulatedWindowIAccessible() const { return mEmulatedWindowProxy.get(); }
 
   IAccessible* GetParentIAccessible() const { return mParentProxy.get(); }
 
   bool SendEvent(const uint64_t& aID, const uint32_t& type);
   bool SendHideEvent(const uint64_t& aRootID, const bool& aFromUser);
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -61,17 +61,17 @@ parent:
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   sync GetWindowedPluginIAccessible(WindowsHandle aHwnd)
     returns (IAccessibleHolder aPluginCOMProxy);
 
 child:
-  async ParentCOMProxy(IAccessibleHolder aParentCOMProxy,
-                       WindowsHandle aEmulatedWindowHandle,
+  async ParentCOMProxy(IAccessibleHolder aParentCOMProxy);
+  async EmulatedWindow(WindowsHandle aEmulatedWindowHandle,
                        IAccessibleHolder aEmulatedWindowCOMProxy);
 
   async __delete__();
 };
 
 }
 }
--- a/accessible/ipc/win/ProxyAccessible.h
+++ b/accessible/ipc/win/ProxyAccessible.h
@@ -34,25 +34,24 @@ public:
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
   }
 
 #include "mozilla/a11y/ProxyAccessibleShared.h"
 
   bool GetCOMInterface(void** aOutAccessible) const;
+  void SetCOMInterface(const RefPtr<IAccessible>& aIAccessible)
+  { mCOMProxy = aIAccessible; }
 
 protected:
   explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc)
     : ProxyAccessibleBase(aThisAsDoc)
   { MOZ_COUNT_CTOR(ProxyAccessible); }
 
-  void SetCOMInterface(const RefPtr<IAccessible>& aIAccessible)
-  { mCOMProxy = aIAccessible; }
-
 private:
   RefPtr<IAccessible> mCOMProxy;
 };
 
 }
 }
 
 #endif
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -8,16 +8,17 @@
 #import "MacUtils.h"
 #import "mozView.h"
 
 #include "Accessible-inl.h"
 #include "nsAccUtils.h"
 #include "nsIAccessibleRelation.h"
 #include "nsIAccessibleEditableText.h"
 #include "nsIPersistentProperties2.h"
+#include "DocAccessibleParent.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
 #include "mozilla/a11y/PDocAccessible.h"
 #include "OuterDocAccessible.h"
 
--- a/accessible/windows/msaa/ServiceProvider.cpp
+++ b/accessible/windows/msaa/ServiceProvider.cpp
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "ServiceProvider.h"
 
 #include "ApplicationAccessibleWrap.h"
-#include "Compatibility.h"
 #include "DocAccessible.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "Relation.h"
 #include "uiaRawElmProvider.h"
 
 #include "mozilla/Preferences.h"
 #include "nsIDocShell.h"
@@ -67,18 +66,20 @@ ServiceProvider::QueryService(REFGUID aG
       return E_NOINTERFACE;
 
     *aInstancePtr = static_cast<IAccessible*>(tabDoc);
     (reinterpret_cast<IUnknown*>(*aInstancePtr))->AddRef();
     return S_OK;
   }
 
   // Can get to IAccessibleApplication from any node via QS
+  // Note: in case of JAWS we want to check if aIID is
+  // IID_IAccessibleApplication.
   if (aGuidService == IID_IAccessibleApplication ||
-      (Compatibility::IsJAWS() && aIID == IID_IAccessibleApplication)) {
+      aIID == IID_IAccessibleApplication) {
     ApplicationAccessibleWrap* applicationAcc =
       static_cast<ApplicationAccessibleWrap*>(ApplicationAcc());
     if (!applicationAcc)
       return E_NOINTERFACE;
 
     return applicationAcc->QueryInterface(aIID, aInstancePtr);
   }
 
--- a/addon-sdk/source/lib/sdk/remote/child.js
+++ b/addon-sdk/source/lib/sdk/remote/child.js
@@ -127,48 +127,52 @@ function makeFrameEventListener(frame, c
 var FRAME_ID = 0;
 var tabMap = new Map();
 
 const Frame = Class({
   implements: [ Disposable ],
   extends: EventTarget,
   setup: function(contentFrame) {
     // This ID should be unique for this loader across all processes
-    ns(this).id = runtime.processID + ":" + FRAME_ID++;
+    let priv = ns(this);
+
+    priv.id = runtime.processID + ":" + FRAME_ID++;
 
-    ns(this).contentFrame = contentFrame;
-    ns(this).messageManager = contentFrame;
-    ns(this).domListeners = [];
+    priv.contentFrame = contentFrame;
+    priv.messageManager = contentFrame;
+    priv.domListeners = [];
 
     tabMap.set(contentFrame.docShell, this);
 
-    ns(this).messageReceived = messageReceived.bind(this);
-    ns(this).messageManager.addMessageListener('sdk/remote/frame/message', ns(this).messageReceived);
+    priv.messageReceived = messageReceived.bind(this);
+    priv.messageManager.addMessageListener('sdk/remote/frame/message', priv.messageReceived);
 
     this.port = new EventTarget();
     definePort(this, 'sdk/remote/frame/message');
 
-    ns(this).messageManager.sendAsyncMessage('sdk/remote/frame/attach', {
+    priv.messageManager.sendAsyncMessage('sdk/remote/frame/attach', {
       loaderID,
-      frameID: ns(this).id,
+      frameID: priv.id,
       processID: runtime.processID
     });
 
     frames.attachItem(this);
   },
 
   dispose: function() {
+    let priv = ns(this);
+
     emit(this, 'detach', this);
 
-    for (let listener of ns(this).domListeners)
-      ns(this).contentFrame.removeEventListener(...listener.args);
+    for (let listener of priv.domListeners)
+      priv.contentFrame.removeEventListener(...listener.args);
 
-    ns(this).messageManager.removeMessageListener('sdk/remote/frame/message', ns(this).messageReceived);
-    tabMap.delete(ns(this).contentFrame.docShell);
-    ns(this).contentFrame = null;
+    priv.messageManager.removeMessageListener('sdk/remote/frame/message', priv.messageReceived);
+    tabMap.delete(priv.contentFrame.docShell);
+    priv.contentFrame = null;
   },
 
   get content() {
     return ns(this).contentFrame.content;
   },
 
   get isTab() {
     let docShell = ns(this).contentFrame.docShell;
@@ -186,33 +190,37 @@ const Frame = Class({
       // And check we can find a tab for the browser element directly.
       let browser = docShell.chromeEventHandler;
       let tab = require('../tabs/utils').getTabForBrowser(browser);
       return !!tab;
     }
   },
 
   addEventListener: function(...args) {
+    let priv = ns(this);
+
     let listener = listenerFor(...args);
-    if (arrayContainsListener(ns(this).domListeners, listener))
+    if (arrayContainsListener(priv.domListeners, listener))
       return;
 
     listener.registeredCallback = makeFrameEventListener(this, listener.callback);
 
-    ns(this).domListeners.push(listener);
-    ns(this).contentFrame.addEventListener(...listener.args);
+    priv.domListeners.push(listener);
+    priv.contentFrame.addEventListener(...listener.args);
   },
 
   removeEventListener: function(...args) {
-    let listener = getListenerFromArray(ns(this).domListeners, listenerFor(...args));
+    let priv = ns(this);
+
+    let listener = getListenerFromArray(priv.domListeners, listenerFor(...args));
     if (!listener)
       return;
 
-    removeListenerFromArray(ns(this).domListeners, listener);
-    ns(this).contentFrame.removeEventListener(...listener.args);
+    removeListenerFromArray(priv.domListeners, listener);
+    priv.contentFrame.removeEventListener(...listener.args);
   }
 });
 
 const FrameList = Class({
   implements: [ EventParent, Disposable ],
   extends: EventTarget,
   setup: function() {
     EventParent.prototype.initialize.call(this);
@@ -228,22 +236,20 @@ const FrameList = Class({
 
   dispose: function() {
     // The only case where we get destroyed is when the loader is unloaded in
     // which case each frame will clean up its own event listeners.
     ns(this).domListeners = null;
   },
 
   getFrameForWindow: function(window) {
-    for (let frame of this) {
-      if (frame.content == window)
-        return frame;
-    }
+    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDocShell);
 
-    return null;
+    return tabMap.get(docShell) || null;
   },
 
   addEventListener: function(...args) {
     let listener = listenerFor(...args);
     if (arrayContainsListener(ns(this).domListeners, listener))
       return;
 
     ns(this).domListeners.push(listener);
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -2138,26 +2138,22 @@
       <versionRange maxVersion="3.0.5.99999999999999" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p944">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="13.0.0.302" minVersion="13.0.0.302" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1495" os="Linux">
-      <match exp="" name="name"/>
       <match exp="libflashplayer\.so" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="24.0.0.186" minVersion="23.0.0.207" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p1420" os="">
-      <match exp="" name="name"/>
+    <pluginItem blockID="p1420">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="23.0.0.205" minVersion="23.0.0.185" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p248">
       <match exp="Scorch\.plugin" name="filename"/>
       <versionRange maxVersion="6.2.0b88" minVersion="0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p1141">
@@ -2201,19 +2197,17 @@
         </targetApplication>
       </versionRange>
     </pluginItem>
     <pluginItem blockID="p102">
       <match exp="npmozax\.dll" name="filename"/>
       <versionRange maxVersion="*" minVersion="0"/>
     </pluginItem>
     <pluginItem blockID="p1419" os="Linux">
-      <match exp="" name="name"/>
       <match exp="libflashplayer\.so" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.643" minVersion="11.2.202.637" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p80">
       <match exp="\(TM\)" name="name"/>
       <match exp="(npjp2\.dll)|(libnpjp2\.so)" name="filename"/>
       <match exp="[^\d\._]((0(\.\d+(\.\d+([_\.]\d+)?)?)?)|(1\.(([0-5](\.\d+([_\.]\d+)?)?)|(6(\.0([_\.](0?\d|1\d|2\d|30))?)?)|(7(\.0([_\.][0-2])?)?))))([^\d\._]|$)" name="description"/>
       <versionRange severity="1"/>
@@ -2270,16 +2264,26 @@
       <match exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="10.3.183.18.999" minVersion="10.3" severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="17.0.*" minVersion="17.0.4"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
+    <pluginItem blockID="3f136e56-4c93-4619-8c0d-d86258c1065d">
+      <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
+      <infoURL>https://get.adobe.com/reader/</infoURL>
+      <versionRange maxVersion="15.006.30244" minVersion="15.006" severity="1" vulnerabilitystatus="1"/>
+    </pluginItem>
+    <pluginItem blockID="43b45ad8-a373-42c1-89c6-64e2746885e5">
+      <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
+      <infoURL>https://get.adobe.com/reader/</infoURL>
+      <versionRange maxVersion="15.020.20042" minVersion="15.020" severity="1" vulnerabilitystatus="1"/>
+    </pluginItem>
     <pluginItem blockID="p366">
       <match exp="Scorch\.plugin" name="filename"/>
       <versionRange maxVersion="6.2.0" minVersion="6.2.0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p936" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.480" minVersion="11.2.202.468" severity="0" vulnerabilitystatus="1"/>
@@ -2350,16 +2354,21 @@
       <match exp="Java\(TM\) Platform SE 7 U(1[6-9]|2[0-4])(\s[^\d\._U]|$)" name="name"/>
       <match exp="npjp2\.dll" name="filename"/>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="17.0"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
+    <pluginItem blockID="59c31ade-88d6-4b22-8601-5316f82e3977">
+      <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
+      <infoURL>https://get.adobe.com/reader/</infoURL>
+      <versionRange maxVersion="11.0.18" minVersion="11.0" severity="1" vulnerabilitystatus="1"/>
+    </pluginItem>
     <pluginItem blockID="p1004">
       <match exp="Unity Web Player\.plugin" name="filename"/>
       <match exp="^($|Unity Web Player version 5.0(\.([0-2]|3f1))?[^0-9.])" name="description"/>
       <versionRange severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p459">
       <match exp="JavaAppletPlugin\.plugin" name="filename"/>
       <versionRange maxVersion="Java 7 Update 44" minVersion="Java 7 Update 25" severity="0" vulnerabilitystatus="1">
@@ -2460,20 +2469,18 @@
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.548" minVersion="11.2.202.540" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1026">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="18.0.0.232" minVersion="18.0.0.204" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p1274" os="">
-      <match exp="" name="name"/>
+    <pluginItem blockID="p1274">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="22.0.0.211" minVersion="22.0.0.192" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p422">
       <match exp="JavaAppletPlugin\.plugin" name="filename"/>
       <versionRange maxVersion="Java 7 Update 24" minVersion="Java 7 Update 16" severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="17.0"/>
@@ -2574,20 +2581,18 @@
     <pluginItem blockID="p34">
       <match exp="[Nn][Pp][Jj][Pp][Ii]1[56]0_[0-9]+\.[Dd][Ll][Ll]" name="filename"/>
       <versionRange>
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.6a1pre"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
-    <pluginItem blockID="p1494" os="">
-      <match exp="" name="name"/>
+    <pluginItem blockID="p1494">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="24.0.0.186" minVersion="23.0.0.207" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p798">
       <match exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="15.0.0.242" minVersion="14.0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
@@ -2645,20 +2650,18 @@
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="21.0.0.197" minVersion="20.0.0.306" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1235">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="18.0.0.352" minVersion="18.0.0.343" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p160" os="">
-      <match exp="" name="name"/>
+    <pluginItem blockID="p160">
       <match exp="NPSWF32\.dll" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="10.2.9999" minVersion="0" severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="16.*" minVersion="4.0"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
     <pluginItem blockID="p129">
@@ -2817,19 +2820,17 @@
     <pluginItem blockID="p574">
       <match exp="NPDjVu\.plugin" name="filename"/>
       <versionRange maxVersion="6.1.1" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p28">
       <match exp="NPFFAddOn.dll" name="filename"/>
     </pluginItem>
     <pluginItem blockID="p1421" os="Linux">
-      <match exp="" name="name"/>
       <match exp="libflashplayer\.so" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="23.0.0.207" minVersion="11.2.202.643" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p302">
       <match exp="Java\(TM\) Plug-in 1\.6\.0_(39|40|41)([^\d\._]|$)" name="name"/>
       <match exp="libnpjp2\.so" name="filename"/>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -3015,20 +3016,18 @@
       <match exp="npuplaypc\.dll" name="filename"/>
       <versionRange maxVersion="1.0.0.0" minVersion="0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p1247">
       <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/reader</infoURL>
       <versionRange maxVersion="15.006.30174" minVersion="15.006.30174" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p1422" os="">
-      <match exp="" name="name"/>
+    <pluginItem blockID="p1422">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
-      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="23.0.0.207" minVersion="23.0.0.205" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p418">
       <match exp="Java\(TM\) Plug-in 1\.7\.0_(1[6-9]|2[0-4])([^\d\._]|$)" name="name"/>
       <match exp="libnpjp2\.so" name="filename"/>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/app/macbuild/Contents/MacOS-files.in
+++ b/browser/app/macbuild/Contents/MacOS-files.in
@@ -1,9 +1,13 @@
 /*.app/***
 /*.dylib
 /certutil
 /firefox-bin
 /gtest/***
+#if defined(MOZ_ASAN) || defined(MOZ_TSAN)
+/llvm-symbolizer
+#endif
+/pingsender
 /pk12util
 /ssltunnel
 /xpcshell
 /XUL
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1372,16 +1372,22 @@ pref("ui.key.menuAccessKeyFocuses", true
 // time a site using EME is encountered, the user will be prompted to
 // enable DRM, whereupon the EME plugin binaries will be downloaded if
 // permission is granted.
 pref("media.eme.enabled", false);
 #else
 pref("media.eme.enabled", true);
 #endif
 
+#ifdef NIGHTLY_BUILD
+pref("media.eme.vp9-in-mp4.enabled", true);
+#else
+pref("media.eme.vp9-in-mp4.enabled", false);
+#endif
+
 // Whether we should run a test-pattern through EME GMPs before assuming they'll
 // decode H.264.
 pref("media.gmp.trial-create.enabled", true);
 
 // Note: when media.gmp-*.visible is true, provided we're running on a
 // supported platform/OS version, the corresponding CDM appears in the
 // plugins list, Firefox will download the GMP/CDM if enabled, and our
 // UI to re-enable EME prompts the user to re-enable EME if it's disabled
@@ -1524,16 +1530,19 @@ pref("browser.esedbreader.loglevel", "Er
 pref("browser.laterrun.enabled", false);
 
 pref("browser.migrate.automigrate.enabled", false);
 // 4 here means the suggestion notification will be automatically
 // hidden the 4th day, so it will actually be shown on 3 different days.
 pref("browser.migrate.automigrate.daysToOfferUndo", 4);
 pref("browser.migrate.automigrate.ui.enabled", true);
 
+pref("browser.migrate.chrome.history.limit", 0);
+pref("browser.migrate.chrome.history.maxAgeInDays", 0);
+
 // Enable browser frames for use on desktop.  Only exposed to chrome callers.
 pref("dom.mozBrowserFramesEnabled", true);
 
 pref("extensions.pocket.enabled", true);
 
 pref("signon.schemeUpgrades", true);
 
 // "Simplify Page" feature in Print Preview. This feature is disabled by default
@@ -1575,14 +1584,15 @@ pref("browser.crashReports.unsubmittedCh
 // Enable the (fairly costly) client/server validation on nightly only. The other prefs
 // controlling validation are located in /services/sync/services-sync.js
 pref("services.sync.validation.enabled", true);
 #endif
 
 // Preferences for the form autofill system extension
 pref("browser.formautofill.experimental", false);
 pref("browser.formautofill.enabled", false);
+pref("browser.formautofill.loglevel", "Warn");
 
 // Enable safebrowsing v4 tables (suffixed by "-proto") update.
 #ifdef NIGHTLY_BUILD
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,goog-malware-proto,goog-unwanted-proto,test-malware-simple,test-unwanted-simple");
 pref("urlclassifier.phishTable", "goog-phish-shavar,goog-phish-proto,test-phish-simple");
 #endif
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -94,16 +94,17 @@
       function showPrefChangeContainer() {
         const panel = document.getElementById("prefChangeContainer");
         panel.style.display = "block";
         document.getElementById("netErrorButtonContainer").style.display = "none";
         document.getElementById("prefResetButton").addEventListener("click", function resetPreferences(e) {
           const event = new CustomEvent("AboutNetErrorResetPreferences", {bubbles:true});
           document.dispatchEvent(event);
         });
+        addAutofocus("prefResetButton", "beforeend");
       }
 
       function setupAdvancedButton() {
         // Get the hostname and add it to the panel
         var panel = document.getElementById("badCertAdvancedPanel");
         for (var span of panel.querySelectorAll("span.hostname")) {
           span.textContent = document.location.hostname;
         }
@@ -193,16 +194,17 @@
         if (showCaptivePortalUI) {
           initPageCaptivePortal();
           return;
         }
         if (gIsCertError) {
           initPageCertError();
           return;
         }
+        addAutofocus("errorTryAgain");
 
         document.body.className = "neterror";
 
         var ld = document.getElementById("errorLongDesc");
         if (ld) {
           ld.innerHTML = errDesc.innerHTML;
         }
 
@@ -312,16 +314,17 @@
         document.title = document.getElementById("captivePortalPageTitle").textContent;
 
         document.getElementById("openPortalLoginPageButton")
                 .addEventListener("click", () => {
           let event = new CustomEvent("AboutNetErrorOpenCaptivePortal", {bubbles:true});
           document.dispatchEvent(event);
         });
 
+        addAutofocus("openPortalLoginPageButton");
         setupAdvancedButton();
 
         addDomainErrorLinks();
 
         // When the portal is freed, an event is generated by the frame script
         // that we can pick up and attempt to reload the original page.
         window.addEventListener("AboutNetErrorCaptivePortalFreed", () => {
           document.location.reload();
@@ -330,16 +333,17 @@
 
       function initPageCertError() {
         document.body.className = "certerror";
         document.title = document.getElementById("certErrorPageTitle").textContent;
         for (let host of document.querySelectorAll(".hostname")) {
           host.textContent = document.location.hostname;
         }
 
+        addAutofocus("returnButton");
         setupAdvancedButton();
 
         document.getElementById("learnMoreContainer").style.display = "block";
 
         let checkbox = document.getElementById("automaticallyReportInFuture");
         checkbox.addEventListener("change", function({target: {checked}}) {
           document.dispatchEvent(new CustomEvent("AboutNetErrorSetAutomatic", {
             detail: checked,
@@ -359,16 +363,32 @@
         }, true, true);
 
         let event = new CustomEvent("AboutNetErrorLoad", {bubbles:true});
         document.getElementById("advancedButton").dispatchEvent(event);
 
         addDomainErrorLinks();
       }
 
+      /* Only do autofocus if we're the toplevel frame; otherwise we
+         don't want to call attention to ourselves!  The key part is
+         that autofocus happens on insertion into the tree, so we
+         can remove the button, add @autofocus, and reinsert the
+         button.
+      */
+      function addAutofocus(buttonId, position = "afterbegin") {
+        if (window.top == window) {
+            var button = document.getElementById(buttonId);
+            var parent = button.parentNode;
+            button.remove();
+            button.setAttribute("autofocus", "true");
+            parent.insertAdjacentElement(position, button);
+        }
+      }
+
       /* Try to preserve the links contained in the error description, like
          the error code.
 
          Also, in the case of SSL error pages about domain mismatch, see if
          we can hyperlink the user to the correct site.  We don't want
          to do this generically since it allows MitM attacks to redirect
          users to a site under attacker control, but in certain cases
          it is safe (and helpful!) to do so.  Bug 402210
@@ -590,42 +610,27 @@
         </div>
 
         <div id="prefChangeContainer" class="button-container">
           <p>&prefReset.longDesc;</p>
           <button id="prefResetButton" class="primary" autocomplete="off">&prefReset.label;</button>
         </div>
 
         <div id="certErrorAndCaptivePortalButtonContainer" class="button-container">
-          <button id="returnButton" class="primary" autocomplete="off" autofocus="true">&returnToPreviousPage.label;</button>
-          <button id="openPortalLoginPageButton" class="primary" autocomplete="off" autofocus="true">&openPortalLoginPage.label2;</button>
+          <button id="returnButton" class="primary" autocomplete="off">&returnToPreviousPage.label;</button>
+          <button id="openPortalLoginPageButton" class="primary" autocomplete="off">&openPortalLoginPage.label2;</button>
           <div class="button-spacer"></div>
-          <button id="advancedButton" autocomplete="off" autofocus="true">&advanced.label;</button>
+          <button id="advancedButton" autocomplete="off">&advanced.label;</button>
         </div>
       </div>
 
       <div id="netErrorButtonContainer" class="button-container">
         <button id="errorTryAgain" class="primary" autocomplete="off" onclick="retryThis(this);">&retry.label;</button>
       </div>
 
-      <script>
-        // Only do autofocus if we're the toplevel frame; otherwise we
-        // don't want to call attention to ourselves!  The key part is
-        // that autofocus happens on insertion into the tree, so we
-        // can remove the button, add @autofocus, and reinsert the
-        // button.
-        if (window.top == window) {
-            var button = document.getElementById("errorTryAgain");
-            var parent = button.parentNode;
-            button.remove();
-            button.setAttribute("autofocus", "true");
-            parent.appendChild(button);
-        }
-      </script>
-
       <!-- UI for option to report certificate errors to Mozilla. Removed on
            init for other error types .-->
       <div id="certificateErrorReporting">
         <p class="toggle-container-with-text">
           <input type="checkbox" id="automaticallyReportInFuture" />
           <label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic2;</label>
         </p>
       </div>
--- a/browser/base/content/aboutTabCrashed.js
+++ b/browser/base/content/aboutTabCrashed.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* eslint-env mozilla/frame-script */
+
 var AboutTabCrashed = {
   /**
    * This can be set to true once this page receives a message from the
    * parent saying whether or not a crash report is available.
    */
   hasReport: false,
 
   /**
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -87,17 +87,18 @@ var gBrowserThumbnails = {
                                                    aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
       this._delayedCapture(aBrowser);
   },
 
   _capture: function Thumbnails_capture(aBrowser) {
     // Only capture about:newtab top sites.
-    if (this._topSiteURLs.indexOf(aBrowser.currentURI.spec) == -1)
+    if (!aBrowser.currentURI ||
+        this._topSiteURLs.indexOf(aBrowser.currentURI.spec) == -1)
       return;
     this._shouldCapture(aBrowser, function(aResult) {
       if (aResult) {
         PageThumbs.captureAndStoreIfStale(aBrowser);
       }
     });
   },
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -426,16 +426,91 @@ const gSessionHistoryObserver = {
     // Hide session restore button on about:home
     window.messageManager.broadcastAsyncMessage("Browser:HideSessionRestoreButton");
 
     // Clear undo history of the URL bar
     gURLBar.editor.transactionManager.clear()
   }
 };
 
+const gStoragePressureObserver = {
+  _lastNotificationTime: -1,
+
+  observe(subject, topic, data) {
+    if (topic != "QuotaManager::StoragePressure" ||
+        !Services.prefs.getBoolPref("browser.storageManager.enabled")) {
+      return;
+    }
+
+    // Don't display notification twice within the given interval.
+    // This is because
+    //   - not to annoy user
+    //   - give user some time to clean space.
+    //     Even user sees notification and starts acting, it still takes some time.
+    const MIN_NOTIFICATION_INTERVAL_MS =
+      Services.prefs.getIntPref("browser.storageManager.pressureNotification.minIntervalMS");
+    let duration = Date.now() - this._lastNotificationTime;
+    if (duration <= MIN_NOTIFICATION_INTERVAL_MS) {
+      return;
+    }
+    this._lastNotificationTime = Date.now();
+
+    const BYTES_IN_GIGABYTE = 1073741824;
+    const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
+      Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
+    let msg = "";
+    let buttons = [];
+    let usage = parseInt(data);
+    let prefStrBundle = document.getElementById("bundle_preferences");
+    let notificationBox = document.getElementById("high-priority-global-notificationbox");
+    buttons.push({
+      label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"),
+      accessKey: prefStrBundle.getString("spaceAlert.learnMoreButton.accesskey"),
+      callback(notificationBar, button) {
+        let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
+        gBrowser.selectedTab = gBrowser.addTab(learnMoreURL);
+      }
+    });
+    if (usage < USAGE_THRESHOLD_BYTES) {
+      // The firefox-used space < 5GB, then warn user to free some disk space.
+      // This is because this usage is small and not the main cause for space issue.
+      // In order to avoid the bad and wrong impression among users that
+      // firefox eats disk space a lot, indicate users to clean up other disk space.
+      msg = prefStrBundle.getString("spaceAlert.under5GB.description");
+      buttons.push({
+        label: prefStrBundle.getString("spaceAlert.under5GB.okButton.label"),
+        accessKey: prefStrBundle.getString("spaceAlert.under5GB.okButton.accesskey"),
+        callback() {}
+      });
+    } else {
+      // The firefox-used space >= 5GB, then guide users to about:preferences
+      // to clear some data stored on firefox by websites.
+      let descriptionStringID = "spaceAlert.over5GB.description";
+      let prefButtonLabelStringID = "spaceAlert.over5GB.prefButton.label";
+      let prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButton.accesskey";
+      if (AppConstants.platform == "win") {
+        descriptionStringID = "spaceAlert.over5GB.descriptionWin";
+        prefButtonLabelStringID = "spaceAlert.over5GB.prefButtonWin.label";
+        prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButtonWin.accesskey";
+      }
+      msg = prefStrBundle.getString(descriptionStringID);
+      buttons.push({
+        label: prefStrBundle.getString(prefButtonLabelStringID),
+        accessKey: prefStrBundle.getString(prefButtonAccesskeyStringID),
+        callback(notificationBar, button) {
+          gBrowser.ownerGlobal.openPreferences("advanced", { advancedTab: "networkTab" });
+        }
+      });
+    }
+
+    notificationBox.appendNotification(
+      msg, "storage-pressure-notification", null, notificationBox.PRIORITY_WARNING_HIGH, buttons, null);
+  }
+};
+
 /**
  * Given a starting docshell and a URI to look up, find the docshell the URI
  * is loaded in.
  * @param   aDocument
  *          A document to find instead of using just a URI - this is more specific.
  * @param   aDocShell
  *          The doc shell to start at
  * @param   aSoughtURI
@@ -1265,16 +1340,17 @@ var gBrowserInit = {
       }
     }
 
     // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
     setTimeout(function() { SafeBrowsing.init(); }, 2000);
 
     Services.obs.addObserver(gIdentityHandler, "perm-changed", false);
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
+    Services.obs.addObserver(gStoragePressureObserver, "QuotaManager::StoragePressure", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
@@ -1599,16 +1675,17 @@ var gBrowserInit = {
       gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
       ctrlTab.uninit();
       SocialUI.uninit();
       gBrowserThumbnails.uninit();
       FullZoom.destroy();
 
       Services.obs.removeObserver(gIdentityHandler, "perm-changed");
       Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
+      Services.obs.removeObserver(gStoragePressureObserver, "QuotaManager::StoragePressure");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-origin-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-confirmation");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
       window.messageManager.removeMessageListener("Browser:URIFixup", gKeywordURIFixup);
@@ -2362,17 +2439,17 @@ function BrowserViewSourceOfDocument(aAr
       // descriptor for the tab (when possible) or fallback to the network if
       // that fails.  Either way, the view source module will manage the tab's
       // location, so use "about:blank" here to avoid unnecessary redundant
       // requests.
       let tab = tabBrowser.loadOneTab("about:blank", {
         relatedToCurrent: true,
         inBackground: false,
         preferredRemoteType,
-        relatedBrowser: args.browser
+        sameProcessAsFrameLoader: args.browser ? args.browser.frameLoader : null
       });
       args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab);
       top.gViewSourceUtils.viewSourceInBrowser(args);
     } else {
       top.gViewSourceUtils.viewSource(args);
     }
   }
 
@@ -3342,29 +3419,31 @@ var PrintPreviewListener = {
   _tabBeforePrintPreview: null,
   _simplifyPageTab: null,
 
   getPrintPreviewBrowser() {
     if (!this._printPreviewTab) {
       let browser = gBrowser.selectedBrowser;
       let preferredRemoteType = browser.remoteType;
       this._tabBeforePrintPreview = gBrowser.selectedTab;
-      this._printPreviewTab = gBrowser.loadOneTab("about:blank",
-                                                  { inBackground: false,
-                                                    preferredRemoteType,
-                                                    relatedBrowser: browser });
+      this._printPreviewTab = gBrowser.loadOneTab("about:blank", {
+        inBackground: false,
+        preferredRemoteType,
+        sameProcessAsFrameLoader: browser.frameLoader
+      });
       gBrowser.selectedTab = this._printPreviewTab;
     }
     return gBrowser.getBrowserForTab(this._printPreviewTab);
   },
   createSimplifiedBrowser() {
     let browser = this._tabBeforePrintPreview.linkedBrowser;
-    this._simplifyPageTab = gBrowser.loadOneTab("about:blank",
-                                                { inBackground: true,
-                                                  relatedBrowser: browser });
+    this._simplifyPageTab = gBrowser.loadOneTab("about:blank", {
+      inBackground: true,
+      sameProcessAsFrameLoader: browser.frameLoader
+     });
     return this.getSimplifiedSourceBrowser();
   },
   getSourceBrowser() {
     return this._tabBeforePrintPreview ?
       this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
   },
   getSimplifiedSourceBrowser() {
     return this._simplifyPageTab ?
@@ -4200,17 +4279,17 @@ function updateUserContextUIIndicator() 
 
   let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid");
   if (!userContextId) {
     hbox.setAttribute("data-identity-color", "");
     hbox.hidden = true;
     return;
   }
 
-  let identity = ContextualIdentityService.getIdentityFromId(userContextId);
+  let identity = ContextualIdentityService.getPublicIdentityFromId(userContextId);
   if (!identity) {
     hbox.setAttribute("data-identity-color", "");
     hbox.hidden = true;
     return;
   }
 
   hbox.setAttribute("data-identity-color", identity.color);
 
@@ -7280,16 +7359,21 @@ var gIdentityHandler = {
     this._identityBox.setAttribute("open", "true");
 
     // Now open the popup, anchored off the primary chrome element
     this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
   },
 
   onPopupShown(event) {
     if (event.target == this._identityPopup) {
+      // Move focus to the next available element in the identity popup.
+      // This is required by role=alertdialog and fixes an issue where
+      // an already open panel would steal focus from the identity popup.
+      document.commandDispatcher.advanceFocusIntoSubtree(this._identityPopup);
+
       window.addEventListener("focus", this, true);
     }
   },
 
   onPopupHidden(event) {
     if (event.target == this._identityPopup) {
       window.removeEventListener("focus", this, true);
       this._identityBox.removeAttribute("open");
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -1,16 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
 /* This content script should work in any browser or iframe and should not
  * depend on the frame being contained in tabbrowser. */
 
+/* eslint-env mozilla/frame-script */
+
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/ContentWebRTC.jsm");
 Cu.import("resource:///modules/ContentObservers.jsm");
 Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
 Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -137,27 +137,34 @@ var gGrid = {
     // Create cells.
     let cells = Array.from(fragment.childNodes, (cell) => new Cell(this, cell));
 
     // Fetch links.
     let links = gLinks.getLinks();
 
     // Create sites.
     let numLinks = Math.min(links.length, cells.length);
+    let hasHistoryTiles = false;
     for (let i = 0; i < numLinks; i++) {
       if (links[i]) {
         this.createSite(links[i], cells[i]);
+        if (links[i].type == "history") {
+          hasHistoryTiles = true;
+        }
       }
     }
 
     this._cells = cells;
     while (this._gridDefaultContent.nextSibling) {
       this._gridDefaultContent.nextSibling.remove();
     }
     this._node.appendChild(fragment);
+
+    document.getElementById("topsites-heading").textContent =
+      newTabString(hasHistoryTiles ? "userTopSites.heading" : "defaultTopSites.heading");
   },
 
   /**
    * Calculate the height for a number of rows up to the maximum rows
    * @param rows Number of rows defaulting to the max
    */
   _computeHeight: function Grid_computeHeight(aRows) {
     let {gridRows} = gGridPrefs;
--- a/browser/base/content/newtab/newTab.xhtml
+++ b/browser/base/content/newtab/newTab.xhtml
@@ -75,17 +75,17 @@
         <input id="newtab-search-submit" type="button"
              title="&contentSearchSubmit.tooltip;"/>
       </div>
     </div>
 
     <div id="newtab-horizontal-margin">
       <div class="newtab-side-margin"/>
       <div id="newtab-grid">
-        <h1 id="topsites-heading">&newtab.topSites.heading;</h1>
+        <h1 id="topsites-heading"/>
       </div>
       <div class="newtab-side-margin"/>
     </div>
 
     <div id="newtab-margin-bottom"/>
   </div>
   <input id="newtab-customize-button" type="button" dir="&locale.dir;"
          value="&#x2699;"
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -1,16 +1,17 @@
 # to be included inside a popupset element
 
     <panel id="notification-popup"
            type="arrow"
            position="after_start"
            hidden="true"
            orient="vertical"
            noautofocus="true"
+           followanchor="false"
            role="alert"/>
 
     <popupnotification id="webRTC-shareDevices-notification" hidden="true">
       <popupnotificationcontent id="webRTC-selectCamera" orient="vertical">
         <label value="&getUserMedia.selectCamera.label;"
                accesskey="&getUserMedia.selectCamera.accesskey;"
                control="webRTC-selectCamera-menulist"/>
         <menulist id="webRTC-selectCamera-menulist">
--- a/browser/base/content/social-content.js
+++ b/browser/base/content/social-content.js
@@ -1,15 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
 /* This content script is intended for use by iframes in the share panel. */
 
+/* eslint-env mozilla/frame-script */
+
 var {interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 // social frames are always treated as app tabs
 docShell.isAppTab = true;
 
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -1,15 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
 /* This content script contains code that requires a tab browser. */
 
+/* eslint-env mozilla/frame-script */
+
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ExtensionContent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
   "resource:///modules/E10SUtils.jsm");
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1506,42 +1506,42 @@
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
             var aForceNotRemote;
             var aPreferredRemoteType;
             var aNoReferrer;
             var aUserContextId;
-            var aRelatedBrowser;
+            var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
-              aTriggeringPrincipal  = params.triggeringPrincipal
-              aReferrerURI          = params.referrerURI;
-              aReferrerPolicy       = params.referrerPolicy;
-              aCharset              = params.charset;
-              aPostData             = params.postData;
-              aLoadInBackground     = params.inBackground;
-              aAllowThirdPartyFixup = params.allowThirdPartyFixup;
-              aFromExternal         = params.fromExternal;
-              aRelatedToCurrent     = params.relatedToCurrent;
-              aAllowMixedContent    = params.allowMixedContent;
-              aSkipAnimation        = params.skipAnimation;
-              aForceNotRemote       = params.forceNotRemote;
-              aPreferredRemoteType  = params.preferredRemoteType;
-              aNoReferrer           = params.noReferrer;
-              aUserContextId        = params.userContextId;
-              aRelatedBrowser       = params.relatedBrowser;
-              aOriginPrincipal      = params.originPrincipal;
-              aOpener               = params.opener;
-              aIsPrerendered        = params.isPrerendered;
+              aTriggeringPrincipal      = params.triggeringPrincipal
+              aReferrerURI              = params.referrerURI;
+              aReferrerPolicy           = params.referrerPolicy;
+              aCharset                  = params.charset;
+              aPostData                 = params.postData;
+              aLoadInBackground         = params.inBackground;
+              aAllowThirdPartyFixup     = params.allowThirdPartyFixup;
+              aFromExternal             = params.fromExternal;
+              aRelatedToCurrent         = params.relatedToCurrent;
+              aAllowMixedContent        = params.allowMixedContent;
+              aSkipAnimation            = params.skipAnimation;
+              aForceNotRemote           = params.forceNotRemote;
+              aPreferredRemoteType      = params.preferredRemoteType;
+              aNoReferrer               = params.noReferrer;
+              aUserContextId            = params.userContextId;
+              aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
+              aOriginPrincipal          = params.originPrincipal;
+              aOpener                   = params.opener;
+              aIsPrerendered            = params.isPrerendered;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
 
             var tab = this.addTab(aURI, {
                                   triggeringPrincipal: aTriggeringPrincipal,
@@ -1555,17 +1555,17 @@
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
                                   allowMixedContent: aAllowMixedContent,
                                   forceNotRemote: aForceNotRemote,
                                   preferredRemoteType: aPreferredRemoteType,
                                   noReferrer: aNoReferrer,
                                   userContextId: aUserContextId,
                                   originPrincipal: aOriginPrincipal,
-                                  relatedBrowser: aRelatedBrowser,
+                                  sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
                                   opener: aOpener,
                                   isPrerendered: aIsPrerendered });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
@@ -1699,19 +1699,18 @@
               }
               if (!isRemote && aBrowser.contentWindow.opener != aOptions.opener) {
                 throw new Error("Cannot change opener on an already non-remote browser!");
               }
             }
 
             // Abort if we're not going to change anything
             let currentRemoteType = aBrowser.getAttribute("remoteType");
-            if (isRemote == aShouldBeRemote && !aOptions.newFrameloader && !aOptions.freshProcess &&
-                (!isRemote || currentRemoteType == aOptions.remoteType) &&
-                !aBrowser.frameLoader.isFreshProcess) {
+            if (isRemote == aShouldBeRemote && !aOptions.newFrameloader &&
+                (!isRemote || currentRemoteType == aOptions.remoteType)) {
               return false;
             }
 
             let tab = this.getTabForBrowser(aBrowser);
             let evt = document.createEvent("Events");
             evt.initEvent("BeforeTabRemotenessChange", true, false);
             tab.dispatchEvent(evt);
 
@@ -1731,19 +1730,19 @@
 
             let oldUserTypedValue = aBrowser.userTypedValue;
             let hadStartedLoad = aBrowser.didStartLoadSinceLastUserTyping();
 
             // Make sure the browser is destroyed so it unregisters from observer notifications
             aBrowser.destroy();
 
             // Make sure to restore the original droppedLinkHandler and
-            // relatedBrowser.
+            // sameProcessAsFrameLoader.
             let droppedLinkHandler = aBrowser.droppedLinkHandler;
-            let relatedBrowser = aBrowser.relatedBrowser;
+            let sameProcessAsFrameLoader = aBrowser.sameProcessAsFrameLoader;
 
             // Change the "remote" attribute.
             let parent = aBrowser.parentNode;
             parent.removeChild(aBrowser);
             if (aShouldBeRemote) {
               aBrowser.setAttribute("remote", "true");
               aBrowser.setAttribute("remoteType", aOptions.remoteType);
             } else {
@@ -1751,37 +1750,27 @@
               aBrowser.removeAttribute("remoteType");
             }
 
             // NB: This works with the hack in the browser constructor that
             // turns this normal property into a field. When switching remote
             // type copying related browser would stop the switch and the
             // previously related browser will no longer be relevant.
             if (!aShouldBeRemote || currentRemoteType == aOptions.remoteType) {
-              aBrowser.relatedBrowser = relatedBrowser;
+              aBrowser.sameProcessAsFrameLoader = sameProcessAsFrameLoader;
             }
 
             if (aOptions.opener) {
               // Set the opener window on the browser, such that when the frame
               // loader is created the opener is set correctly.
               aBrowser.presetOpenerWindow(aOptions.opener);
             }
 
-            // Set the freshProcess attribute so that the frameloader knows to
-            // create a new process
-            if (aOptions.freshProcess) {
-              aBrowser.setAttribute("freshProcess", "true");
-            }
-
             parent.appendChild(aBrowser);
 
-            // Remove the freshProcess attribute if we set it, as we don't
-            // want it to apply for the next time the frameloader is created
-            aBrowser.removeAttribute("freshProcess");
-
             aBrowser.userTypedValue = oldUserTypedValue;
             if (hadStartedLoad) {
               aBrowser.urlbarChangeTracker.startedLoad();
             }
 
             aBrowser.droppedLinkHandler = droppedLinkHandler;
 
             // Switching a browser's remoteness will create a new frameLoader.
@@ -1845,25 +1834,33 @@
         <parameter name="aOptions"/>
         <body>
           <![CDATA[
             aOptions = aOptions || {};
 
             if (!gMultiProcessBrowser)
               return this.updateBrowserRemoteness(aBrowser, false);
 
+            // If we're in a LargeAllocation process, we prefer switching back
+            // into a normal content process, as that way we can clean up the
+            // L-A process.
+            let preferredRemoteType = aBrowser.remoteType;
+            if (aBrowser.remoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) {
+              preferredRemoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
+            }
+            aOptions.remoteType =
+              E10SUtils.getRemoteTypeForURI(aURL,
+                                            gMultiProcessBrowser,
+                                            preferredRemoteType,
+                                            aOptions.freshProcess);
+
             // If this URL can't load in the current browser then flip it to the
             // correct type.
-            let currentRemoteType = aBrowser.remoteType;
-            aOptions.remoteType =
-              E10SUtils.getRemoteTypeForURI(aURL, gMultiProcessBrowser,
-                                            currentRemoteType);
-            if (currentRemoteType != aOptions.remoteType ||
-                aOptions.freshProcess || aOptions.newFrameloader ||
-                aBrowser.frameLoader.isFreshProcess) {
+            if (aBrowser.remoteType != aOptions.remoteType ||
+                aOptions.newFrameloader) {
               let remote = aOptions.remoteType != E10SUtils.NOT_REMOTE;
               return this.updateBrowserRemoteness(aBrowser, remote, aOptions);
             }
 
             return false;
           ]]>
         </body>
       </method>
@@ -1991,18 +1988,18 @@
               b.setAttribute("selectmenulist", this.getAttribute("selectmenulist"));
 
             if (this.hasAttribute("datetimepicker")) {
               b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
             }
 
             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
 
-            if (aParams.relatedBrowser) {
-              b.relatedBrowser = aParams.relatedBrowser;
+            if (aParams.sameProcessAsFrameLoader) {
+              b.sameProcessAsFrameLoader = aParams.sameProcessAsFrameLoader;
             }
 
             // Create the browserStack container
             var stack = document.createElementNS(NS_XUL, "stack");
             stack.className = "browserStack";
             stack.appendChild(b);
             stack.setAttribute("flex", "1");
 
@@ -2073,17 +2070,17 @@
             }
 
             if (!browser) {
               // No preloaded browser found, create one.
               browser = this._createBrowser({permanentKey: aTab.permanentKey,
                                              remoteType,
                                              uriIsAboutBlank,
                                              userContextId: aParams.userContextId,
-                                             relatedBrowser: aParams.relatedBrowser,
+                                             sameProcessAsFrameLoader: aParams.sameProcessAsFrameLoader,
                                              opener: aParams.opener,
                                              isPrerendered: aParams.isPrerendered});
             }
 
             let notificationbox = this.getNotificationBox(browser);
             let uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             aTab.linkedPanel = uniqueId;
@@ -2152,17 +2149,17 @@
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aPreferredRemoteType;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
-            var aRelatedBrowser;
+            var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aDisallowInheritPrincipal;
             var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal;
@@ -2176,17 +2173,17 @@
               aRelatedToCurrent         = params.relatedToCurrent;
               aSkipAnimation            = params.skipAnimation;
               aAllowMixedContent        = params.allowMixedContent;
               aForceNotRemote           = params.forceNotRemote;
               aPreferredRemoteType      = params.preferredRemoteType;
               aNoReferrer               = params.noReferrer;
               aUserContextId            = params.userContextId;
               aEventDetail              = params.eventDetail;
-              aRelatedBrowser           = params.relatedBrowser;
+              aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
               aOriginPrincipal          = params.originPrincipal;
               aDisallowInheritPrincipal = params.disallowInheritPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
@@ -2268,17 +2265,17 @@
             // state to be exploited for startup optimization.  Note that for
             // now this must occur before "TabOpen" event is fired, as that will
             // trigger SessionStore.jsm to run code that expects the existence
             // of tab.linkedBrowser.
             let browserParams = {
               forceNotRemote: aForceNotRemote,
               preferredRemoteType: aPreferredRemoteType,
               userContextId:  aUserContextId,
-              relatedBrowser: aRelatedBrowser,
+              sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
               opener: aOpener,
               isPrerendered: aIsPrerendered,
             };
             let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams);
             let b = t.linkedBrowser;
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
--- a/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js
+++ b/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js
@@ -41,17 +41,19 @@ add_task(function* checkCaptivePortalCer
   let portalTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, CANONICAL_URL);
 
   yield ContentTask.spawn(browser, null, () => {
     let doc = content.document;
     ok(doc.body.classList.contains("captiveportal"),
        "Captive portal error page UI is visible.");
 
     info("Clicking the Open Login Page button.");
-    doc.getElementById("openPortalLoginPageButton").click();
+    let loginButton = doc.getElementById("openPortalLoginPageButton");
+    is(loginButton.getAttribute("autofocus"), "true", "openPortalLoginPageButton has autofocus");
+    loginButton.click();
   });
 
   let portalTab = yield portalTabPromise;
   is(gBrowser.selectedTab, portalTab, "Login page should be open in a new foreground tab.");
 
   // Make sure clicking the "Open Login Page" button again focuses the existing portal tab.
   yield BrowserTestUtils.switchTab(gBrowser, errorTab);
   // Passing an empty function to BrowserTestUtils.switchTab lets us wait for an arbitrary
--- a/browser/base/content/test/general/aboutHome_content_script.js
+++ b/browser/base/content/test/general/aboutHome_content_script.js
@@ -1,6 +1,8 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 addMessageListener("AboutHome:SearchTriggered", function(msg) {
   sendAsyncMessage("AboutHomeTest:CheckRecordedSearch", msg.data);
 });
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -70,25 +70,28 @@ support-files =
   unknownContentType_file.pif
   unknownContentType_file.pif^headers^
   video.ogg
   web_video.html
   web_video1.ogv
   web_video1.ogv^headers^
   zoom_test.html
   file_install_extensions.html
+  browser_legacy.xpi
+  browser_legacy_webext.xpi
   browser_webext_permissions.xpi
   browser_webext_nopermissions.xpi
   browser_webext_update1.xpi
   browser_webext_update2.xpi
   browser_webext_update_icon1.xpi
   browser_webext_update_icon2.xpi
   browser_webext_update_perms1.xpi
   browser_webext_update_perms2.xpi
   browser_webext_update.json
+  browser_webext_search.xml
   !/image/test/mochitest/blue.png
   !/toolkit/content/tests/browser/common/mockTransfer.js
   !/toolkit/modules/tests/browser/metadata_*.html
   !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
   !/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs
@@ -338,16 +341,17 @@ skip-if = e10s && debug && os == "win" #
 support-files =
   contentSearchUI.html
   contentSearchUI.js
 [browser_selectpopup.js]
 skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
 [browser_selectTabAtIndex.js]
 [browser_ssl_error_reports.js]
 [browser_star_hsts.js]
+[browser_storagePressure_notification.js]
 [browser_subframe_favicons_not_used.js]
 [browser_syncui.js]
 skip-if = os == 'linux' # Bug 1304272
 [browser_tab_close_dependent_window.js]
 [browser_tabDrop.js]
 [browser_tabReorder.js]
 [browser_tab_detach_restore.js]
 [browser_tab_drag_drop_perwindow.js]
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -34,16 +34,17 @@ add_task(function* checkReturnToAboutHom
   let {entries} = JSON.parse(ss.getTabState(tab));
   is(entries.length, 1, "there is one shistory entry");
 
   info("Clicking the go back button on about:certerror");
   let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
   yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let returnButton = doc.getElementById("returnButton");
+    is(returnButton.getAttribute("autofocus"), "true", "returnButton has autofocus");
     returnButton.click();
   });
   yield pageshowPromise;
 
   is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
   is(gBrowser.currentURI.spec, "about:home", "Went back");
 
--- a/browser/base/content/test/general/browser_aboutNetError.js
+++ b/browser/base/content/test/general/browser_aboutNetError.js
@@ -26,16 +26,19 @@ add_task(function* checkReturnToPrevious
 
   Assert.ok(content.document.getElementById("prefResetButton").getBoundingClientRect().left >= 0,
     "Should have a visible button");
 
   Assert.ok(content.document.documentURI.startsWith("about:neterror"), "Should be showing error page");
 
   let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
   yield ContentTask.spawn(browser, null, function* () {
-    content.document.getElementById("prefResetButton").click();
+    let doc = content.document;
+    let prefResetButton = doc.getElementById("prefResetButton");
+    Assert.equal(prefResetButton.getAttribute("autofocus"), "true", "prefResetButton has autofocus");
+    prefResetButton.click();
   });
   yield pageshowPromise;
 
   Assert.equal(content.document.documentURI, LOW_TLS_VERSION, "Should not be showing page");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/general/browser_accesskeys.js
+++ b/browser/base/content/test/general/browser_accesskeys.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 add_task(function *() {
   yield pushPrefs(["ui.key.contentAccess", 5], ["ui.key.chromeAccess", 5]);
 
   const gPageURL1 = "data:text/html,<body><p>" +
                     "<button id='button' accesskey='y'>Button</button>" +
                     "<input id='checkbox' type='checkbox' accesskey='z'>Checkbox" +
                     "</p></body>";
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gPageURL1);
--- a/browser/base/content/test/general/browser_backButtonFitts.js
+++ b/browser/base/content/test/general/browser_backButtonFitts.js
@@ -1,12 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+/* eslint-env mozilla/frame-script */
+
 add_task(function* () {
   let firstLocation = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     // Push the state before maximizing the window and clicking below.
     content.history.pushState("page2", "page2", "page2");
 
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -1,14 +1,16 @@
 /*
  * This test checks that focus is adjusted properly in a browser when pressing F6 and Shift+F6.
  * There are additional tests in dom/tests/mochitest/chrome/test_focus_docnav.xul which test
  * non-browser cases.
  */
 
+/* eslint-env mozilla/frame-script */
+
 var testPage1 = "data:text/html,<html id='html1'><body id='body1'><button id='button1'>Tab 1</button></body></html>";
 var testPage2 = "data:text/html,<html id='html2'><body id='body2'><button id='button2'>Tab 2</button></body></html>";
 var testPage3 = "data:text/html,<html id='html3'><body id='body3' contenteditable='true'><button id='button3'>Tab 3</button></body></html>";
 
 var fm = Services.focus;
 
 function* expectFocusOnF6(backward, expectedDocument, expectedElement, onContent, desc) {
   let focusChangedInChildResolver = null;
--- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 "use strict";
 
 var gMessageManager;
 
 function frameScript() {
   addMessageListener("Test:RequestFullscreen", () => {
     content.document.body.requestFullscreen();
   });
--- a/browser/base/content/test/general/browser_e10s_switchbrowser.js
+++ b/browser/base/content/test/general/browser_e10s_switchbrowser.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 requestLongerTimeout(2);
 
 const DUMMY_PATH = "browser/browser/base/content/test/general/dummy_page.html";
 
 const gExpectedHistory = {
   index: -1,
   entries: []
 };
--- a/browser/base/content/test/general/browser_extension_permissions.js
+++ b/browser/base/content/test/general/browser_extension_permissions.js
@@ -1,31 +1,41 @@
 "use strict";
 
+// See but 1340586 for proposal to reorganize permissions tests to
+// get rid of this...
+requestLongerTimeout(2);
+
 const BASE = getRootDirectory(gTestPath)
   .replace("chrome://mochitests/content/", "https://example.com/");
 
 const INSTALL_PAGE = `${BASE}/file_install_extensions.html`;
 const PERMS_XPI = "browser_webext_permissions.xpi";
 const NO_PERMS_XPI = "browser_webext_nopermissions.xpi";
 const ID = "permissions@test.mozilla.org";
 
-const DEFAULT_EXTENSION_ICON = "chrome://browser/content/extension.svg";
-
 Services.perms.add(makeURI("https://example.com/"), "install",
                    Services.perms.ALLOW_ACTION);
 
 registerCleanupFunction(async function() {
   let addon = await AddonManager.getAddonByID(ID);
   if (addon) {
     ok(false, `Addon ${ID} was still installed at the end of the test`);
     addon.uninstall();
   }
 });
 
+function isDefaultIcon(icon) {
+  // These are basically the same icon, but code within webextensions
+  // generates references to the former and generic add-ons manager code
+  // generates referces to the latter.
+  return (icon == "chrome://browser/content/extension.svg" ||
+          icon == "chrome://mozapps/skin/extensions/extensionGeneric.svg");
+}
+
 function promisePopupNotificationShown(name) {
   return new Promise(resolve => {
     function popupshown() {
       let notification = PopupNotifications.getNotification(name);
       if (!notification) { return; }
 
       ok(notification, `${name} notification shown`);
       ok(PopupNotifications.isPanelOpen, "notification panel open");
@@ -51,17 +61,17 @@ function checkNotification(panel, filena
     ok(icon.startsWith("jar:file://"), "Icon is a jar url");
     ok(icon.endsWith("/icon.png"), "Icon is icon.png inside a jar");
 
     is(header.getAttribute("hidden"), "", "Permission list header is visible");
     is(ul.childElementCount, 4, "Permissions list has 4 entries");
     // Real checking of the contents here is deferred until bug 1316996 lands
   } else if (filename == NO_PERMS_XPI) {
     // This extension has no icon, it should have the default
-    is(icon, DEFAULT_EXTENSION_ICON, "Icon is the default extension icon");
+    ok(isDefaultIcon(icon), "Icon is the default extension icon");
 
     is(header.getAttribute("hidden"), "true", "Permission list header is hidden");
     is(ul.childElementCount, 0, "Permissions list has 0 entries");
   }
 }
 
 // Navigate the current tab to the given url and return a Promise
 // that resolves when the page is loaded.
@@ -101,16 +111,55 @@ const INSTALL_FUNCTIONS = [
 
     await BrowserOpenAddonsMgr("addons://list/extension");
     let contentWin = gBrowser.selectedTab.linkedBrowser.contentWindow;
 
     // Do the install...
     contentWin.gViewController.doCommand("cmd_installFromFile");
     MockFilePicker.cleanup();
   },
+
+  async function installSearch(filename) {
+    await SpecialPowers.pushPrefEnv({set: [
+      ["extensions.getAddons.maxResults", 10],
+      ["extensions.getAddons.search.url", `${BASE}/browser_webext_search.xml`],
+    ]});
+
+    let win = await BrowserOpenAddonsMgr("addons://list/extension");
+
+    let searchResultsPromise = new Promise(resolve => {
+      win.document.addEventListener("ViewChanged", resolve, {once: true});
+    });
+    let search = win.document.getElementById("header-search");
+    search.focus();
+    search.value = "search text";
+    EventUtils.synthesizeKey("VK_RETURN", {}, win);
+
+    await searchResultsPromise;
+    ok(win.gViewController.currentViewId.startsWith("addons://search"),
+       "about:addons is displaying search results");
+
+    let list = win.document.getElementById("search-list");
+    let item = null;
+    for (let child of list.childNodes) {
+      if (child.nodeName == "richlistitem" &&
+          child.mAddon.install.sourceURI.path.endsWith(filename)) {
+            item = child;
+            break;
+      }
+    }
+    ok(item, `Found ${filename} in search results`);
+
+    // abracadabara XBL
+    item.clientTop;
+
+    let install = win.document.getAnonymousElementByAttribute(item, "anonid", "install-status");
+    let button = win.document.getAnonymousElementByAttribute(install, "anonid", "install-remote-btn");
+    EventUtils.synthesizeMouseAtCenter(button, {}, win);
+  },
 ];
 
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [
     ["extensions.webapi.testing", true],
     ["extensions.install.requireBuiltInCerts", false],
 
     // XXX remove this when prompts are enabled by default
--- a/browser/base/content/test/general/browser_extension_update_background.js
+++ b/browser/base/content/test/general/browser_extension_update_background.js
@@ -1,16 +1,18 @@
 const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
 const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
 const ID = "update@tests.mozilla.org";
 const ID_ICON = "update_icon@tests.mozilla.org";
+const ID_PERMS = "update_perms@tests.mozilla.org";
+const ID_LEGACY = "legacy_update@tests.mozilla.org";
 
 registerCleanupFunction(async function() {
-  for (let id of [ID, ID_ICON]) {
+  for (let id of [ID, ID_ICON, ID_PERMS, ID_LEGACY]) {
     let addon = await AddonManager.getAddonByID(id);
     if (addon) {
       ok(false, `Addon ${id} was still installed at the end of the test`);
       addon.uninstall();
     }
   }
 });
 
@@ -227,50 +229,60 @@ function checkNonDefaultIcon(icon) {
   // inside the jar.
   ok(icon.startsWith("jar:file://"), "Icon is a jar url");
   ok(icon.endsWith("/icon.png"), "Icon is icon.png inside a jar");
 }
 
 add_task(() => backgroundUpdateTest(`${URL_BASE}/browser_webext_update_icon1.xpi`,
                                     ID_ICON, checkNonDefaultIcon));
 
-// Test that an update that adds new non-promptable permissions is just
-// applied without showing a notification dialog.
-add_task(function*() {
-  yield SpecialPowers.pushPrefEnv({set: [
+// Helper function to test an upgrade that should not show a prompt
+async function testNoPrompt(origUrl, id) {
+  await SpecialPowers.pushPrefEnv({set: [
     // Turn on background updates
     ["extensions.update.enabled", true],
 
     // Point updates to the local mochitest server
     ["extensions.update.background.url", `${URL_BASE}/browser_webext_update.json`],
   ]});
 
   // Install version 1.0 of the test extension
-  let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update_perms1.xpi`);
+  let addon = await promiseInstallAddon(origUrl);
 
   ok(addon, "Addon was installed");
 
   let sawPopup = false;
   PopupNotifications.panel.addEventListener("popupshown",
                                             () => sawPopup = true,
                                             {once: true});
 
   // Trigger an update check and wait for the update to be applied.
   let updatePromise = promiseInstallEvent(addon, "onInstallEnded");
   AddonManagerPrivate.backgroundUpdateCheck();
-  yield updatePromise;
+  await updatePromise;
 
   // There should be no notifications about the update
   is(getBadgeStatus(), "", "Should not have addon alert badge");
 
-  yield PanelUI.show();
+  await PanelUI.show();
   let addons = document.getElementById("PanelUI-footer-addons");
   is(addons.children.length, 0, "Have 0 updates in the PanelUI menu");
-  yield PanelUI.hide();
+  await PanelUI.hide();
 
   ok(!sawPopup, "Should not have seen permissions notification");
 
-  addon = yield AddonManager.getAddonByID("update_perms@tests.mozilla.org");
+  addon = await AddonManager.getAddonByID(id);
   is(addon.version, "2.0", "Update should have applied");
 
   addon.uninstall();
-  yield SpecialPowers.popPrefEnv();
-});
+  await SpecialPowers.popPrefEnv();
+}
+
+// Test that an update that adds new non-promptable permissions is just
+// applied without showing a notification dialog.
+add_task(() => testNoPrompt(`${URL_BASE}/browser_webext_update_perms1.xpi`,
+                            ID_PERMS));
+
+// Test that an update from a legacy extension to a webextension
+// doesn't show a prompt even when the webextension uses
+// promptable required permissions.
+add_task(() => testNoPrompt(`${URL_BASE}/browser_legacy.xpi`, ID_LEGACY));
+
--- a/browser/base/content/test/general/browser_extension_update_interactive.js
+++ b/browser/base/content/test/general/browser_extension_update_interactive.js
@@ -1,18 +1,21 @@
 const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
 const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
 const ID = "update@tests.mozilla.org";
+const ID_LEGACY = "legacy_update@tests.mozilla.org";
 
 registerCleanupFunction(async function() {
-  let addon = await AddonManager.getAddonByID(ID);
-  if (addon) {
-    ok(false, `Addon ${ID} was still installed at the end of the test`);
-    addon.uninstall();
+  for (let id of [ID, ID_LEGACY]) {
+    let addon = await AddonManager.getAddonByID(id);
+    if (addon) {
+      ok(false, `Addon ${id} was still installed at the end of the test`);
+      addon.uninstall();
+    }
   }
 });
 
 function promiseInstallAddon(url) {
   return AddonManager.getInstallForURL(url, null, "application/x-xpinstall")
                      .then(install => {
                        ok(install, "Created install");
                        return new Promise(resolve => {
@@ -115,18 +118,22 @@ function* interactiveUpdateTest(autoUpda
       });
     }
 
     checkFn(win, addon);
 
     if (manualUpdatePromise) {
       yield manualUpdatePromise;
 
-      let item = win.document.getElementById("addon-list")
-                    .children.find(_item => _item.value == ID);
+      let list = win.document.getElementById("addon-list");
+
+      // Make sure we have XBL bindings
+      list.clientHeight;
+
+      let item = list.children.find(_item => _item.value == ID);
       EventUtils.synthesizeMouseAtCenter(item._updateBtn, {}, win);
     }
   }
 
   // Install version 1.0 of the test extension
   let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
   ok(addon, "Addon was installed");
   is(addon.version, "1.0", "Version 1 of the addon is installed");
@@ -193,8 +200,48 @@ add_task(() => interactiveUpdateTest(fal
 // Invoke an invidual extension's "Find Updates" menu item
 function checkOne(win, addon) {
   win.gViewController.doCommand("cmd_findItemUpdates", addon);
 }
 
 // Test "Find Updates" with both auto-update settings
 add_task(() => interactiveUpdateTest(true, checkOne));
 add_task(() => interactiveUpdateTest(false, checkOne));
+
+// Check that an update from a legacy extension to a webextensino
+// does not display a prompt
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({set: [
+    // Point updates to the local mochitest server
+    ["extensions.update.url", `${URL_BASE}/browser_webext_update.json`],
+  ]});
+
+  // Navigate away to ensure that BrowserOpenAddonMgr() opens a new tab
+  gBrowser.selectedBrowser.loadURI("about:robots");
+  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+
+  // Install initial version of the test extension
+  let addon = await promiseInstallAddon(`${URL_BASE}/browser_legacy.xpi`);
+  ok(addon, "Addon was installed");
+  is(addon.version, "1.1", "Version 1 of the addon is installed");
+
+  // Go to Extensions in about:addons
+  let win = await BrowserOpenAddonsMgr("addons://list/extension");
+
+  let sawPopup = false;
+  PopupNotifications.panel.addEventListener("popupshown",
+                                            () => sawPopup = true,
+                                            {once: true});
+
+  // Trigger an update check, we should see the update get applied
+  let updatePromise = promiseInstallEvent(addon, "onInstallEnded");
+  win.gViewController.doCommand("cmd_findAllUpdates");
+  await updatePromise;
+
+  addon = await AddonManager.getAddonByID(ID_LEGACY);
+  is(addon.version, "2.0", "Should have upgraded");
+
+  ok(!sawPopup, "Should not have seen a permission notification");
+
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  addon.uninstall();
+  await SpecialPowers.popPrefEnv();
+});
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..da4b3062796c180155819f83f6514ad6738827ab
GIT binary patch
literal 4705
zc$|$`bx;&s*I&Aql~7s)gr)N#lqDntC6-16Nr9!7TDp-CSQ?a+?hYxbMS-Q46%mk-
zkZzEMSHGF>d%o9s-kJB@xqtlbob$VL=6CL$IS2X_51$4A01yLyG((?Ei<uw)BnJRY
zKmfq+Q*|X>d45$5WkE-{56r>AT)@@MeAd85H)*p@gS}yld|ZL(6X=wRSVgN$Wmg?P
zfhx-E5lJK%S3aLjNlxAj+vbjsL$I!71&4yxO<;N2_=PNE^-ULB7faWzn^lNw|51sR
zthB!lhZurTK0uAYkTUgID_rcOaqfy%K$spGE}<noj*lcoIz8$4TpXO3&g8Cj8pS(u
zx$6M%*tk0wpFFs$Tl|5xW<8*&v!N@7NIBP0HdBt@%$egOihPi0j|rEMV?2mvZD)O*
zwM714pY2RCepXenLM%usS%mG=O>!AM9SOrleCAaqkQRUVo5|}PMFV-!khsn^YMf|-
z0!oUU{-qaz+~$6PJm#591x%S)s*0wigR<?a34X@4b%32}(0VtMMTe}(nT5)Wj<t8^
zvfFw$4X$h^gr`zzH3tA7Z$uJ~2CgKd1}D=FVDW|`7)pAZq~?k|`@2EcO`0@OR{*hX
zf*K)*EZbCf7T9)OqdDZIRLOK)Ue`pxhnr$rW~#j0a0oE+ra!@?D`{b4!F^(GbZ{zI
z4SJv24}FusAQw!0(|&En07}L+$6>y-34*ekL3fe`6B;Y@ACq*%Uey2?D*9D@zQW4K
zp0=kN7B(DLW?~7$>ozH5s<s>a!~eRbLr&{Kk!QomZ)%i3W9$fDCn;Z1FWU$T|0JX;
zf6jY9Kw#BI{4nEbB}aQ9hAWYzw-TCXRJbmegZf<i0Vd8|$~QR~C0^Pzpo6%x|5N65
z&gc9PKyu-FldhW)W_8rVv?>|&_He?2-3m{i;V;+wb*tNsx@cp1tvu#~>JRscAO0o4
ztpt>u)BjOpWV5pYb~g}r+2K@K_b-}=tm$QMW|h}LiXsC%7J^+GcEgm{E45!fh)B##
zRW0kX`Dh$q%;c2VVW&oS#k^Qlnca}&s<%HNZT#alT}njn`nH(z5yD-|Gj^1mM^kvi
zlTkua^Y+IR3AH8#7!wES{D@wmhQk^cIAt$(B(9Ky_J<skV)oWt6sGP0<6;yh%B|+e
zP**+YZa@#;Qh<(;Sg&_h`a+0#7A?J3<H3jhL8Tq_T4tti>miQiGMd-7Xi5_YM{$Hr
zEM`&m&KMhNzbOu*gYvLV*z1Lk3*CLu_-Jv7MFO-^B$@Fp{r&n)RTO{uX?o*H5lL=a
zaObnOf}<h3heC8+6ha=W4Sq<3KpG@19vd?%)V@UT+vX^uIlY(s+=#fv2q9W9p?^UK
z>lw_?^0o6d!TAck9zr)#=Nsxmn(FX_L*y3%)xbwJ!rxfn++&q{_@|>TJazk9#4AI@
z^FkA4Y<3(Kd-OBH0}jJOly%Z!yBQz798&htaeG{Bs0pIRvpdrBcSIV&7IX^q7feb7
zd|0G`%D8@F--r!2@5v`b^#1PS`^#aBJCFJuaXlX0bwPD}KlQjy0(C|T4j`km?dE#g
z#gilQvK5WQk}_6TJ0RB!*T(0@Qa_q;5g;BCA^*Gdc`*UNP!^1+2z$EqXdJ9osycDT
z7MWi|y-9)RjeS79F|k5gIKimpP_L;R=IfOe)l@_+%AUHDw2a$!A+;|q!7^mzAyNA{
zU@{}wZ0)s8V=Z5=uC%~Yb?Gm{JCS<<wauOs=Ud|_yj}IrDYLAg36NEug%3k8Xkf{|
zHH__}r`rtKpwGh0*#OeP>PHCFSMM+n9twYKG$X#UeHJ5~*$EWFpH}G?_kQ}JH2QMv
zw(^mW6=RL)%&4U%MD%Jp$EKwJ+@;n)25}Vks=If1x_9$OOfZe_AzBP>B{nvyY%zCN
zHoM&3qPQ%pDg7FhP;N(xZ4=Tfi+=p1jNmjvG$?(Kv$O_<;W#K?c~3`KEO&salZlXz
zoo_xY%%_JVh;Y&LwWyfO#mk@u`$qXQ-f|;mOLI&zhjH2Uop13kNWUmmoVMC8Z$RTF
z3nQ=8#fO^&jk(p=-S#M-OYIcjhD|Twkoqy^bXf;zOC{6dpZUIF1$s%Z-6Z!C06uFn
ztnS@Q=qLJ!JY&pyXv}$)V=B!Wn$m|}aH*4BI{^(`GhR<tq}?F9GumE;XCE-9S%6A4
z7d-~jB-)0TF61okUt31oNBtZ@?mUPcIlmCds3R!xq;5=6dPB})Dpt`c^2Px=9dqq3
zcgkYH;iCXSIQ89AmBK-(q}-WwtO9Vuv8R@UBvfndm%zk+aR(ZyNZTP1Q|d)etJ*<^
zW-9s=mXh`myVWoCD)I=9PVgRUiU635&|SH5&Od=fpyl4P2k@d@hR5!8<gUJX&t!K=
zQ`8SP`#wF9wbX3(kk6QLg9lrs*K-PZX+Bv5+I+8_++-A8Y7&3`7(r}_q$)f1g*QZ~
zJE`~}0#Ko?7%ofW27IO5;=^ZrxwTdA1{HC6yH{=DggXq+F366<KTxKy1ejn`Y_A^P
zJ4(CQTM!6z7M%yY*=H^Y>l)r2)i_RA)xNX2wbE&3@wFP0yB|!|uqmUGkZI8}fP5$u
z3NL-p{z`Jz>W(kEFo*7kJqogPsAMDii5=BP+vF3%63*R1lbT&#a&A=3EZAOXvsK9z
zSg5qVc<jQJ9gDpc5?7#dZ*Fz_<zaF=-{`cAlSKjc2@{<em2T&Z6M%n`Hm+PFZqHQ6
zp7DU1z5Zy}gOFEqmQ13uilt1j1h%bvq#zPR(PQCJKX%>m<=BD%Za96`I&T~(W+~{E
zC;39akJU56qG{ThGgOb$+0ondC$pNHj<exgAS1i8b*VZTUNDM(xgKmr5QN){7a3Zl
zc+Xr6Ktn!9^p^70VD}wz520S?pe0Z=vju(^pje@&TTHA0x)!IUAlxHqwza(Zb4v;<
zRV%6~&~pgST^5rf*a768MY!HFw0!_dr$Sv9IAPL_C6&k}wX{w%hyy#4tR5khZEZdF
zZba`xyUgSWH3AdLD2f7fh<eNxnev>h>fYov9NlCp!?=iGTz56O1yL)3pqaR1gUhOW
zw$4`cyo}EIqOBu-A_2O)&Buo2@k%Q6M1xmXPc`O!2&9*l)QX96jCg?P;!_zy<v{50
z4gS>XmVQg={xXF?oF|g9x`Y}q!N)E2%y!z~8i*D5)=l9k<92`c(#|RUn4_N5^NkOI
z&0wAtXUt~Up5yz<T!EMFd6<c^4GTjn*vXaNM(pbGIW_ot*E)Ro`UBU+v)lrJOOLLl
z?pzyvPl_9C{0+;6W@;eBw45$mWXvzwHl{@+LpzaR^wC3lt-6_(aBhPhx)C1+M;-6_
z{3$S@Nr5IsV1GeOd<S{q>n7<Ex=}QB^z@W+&0>n$h_2mCb<>=!TwoBFhC<k`j%c>_
zOhb0cQsB}n$JsVHXKp6(B*S!G<u1=OLsLc0>NeOD;A25MR#ZpO-O0~EgNb%=S?S9w
z85vbKL-m=K6V{XdO^N%ohg!AGic)Yy-(lu#(F$u*iT$|48BQea``x6spN1|Yq)IiN
zd?PWHE~GN6Prua+4Wm<++rMPs?#0>DBFn9k6~B2Ji>7al>22Hfe73qx{BitUxt;D-
zHaKCXV3ZD+XICJ<O55S6IiGu`fR>fiE1239%NXb%ge%;|#;OqdXZ0EG$-?XHO*S3K
zoL4#112;(+kC)V`aZ8pOi@$pdd0b}Xq!SKlAGAVFVTda2&v-o|WbjkgRNTe~%~nG|
z4Hnjhlg^iEmx#ox*6-unfGX42oUfhl+yV1~BowJDcTa$<2Tw_RSeze6dA*#O+IX?N
z)sV)uFVptvqS^mIR{u^6E>0GRKZL$wLR5{f$&D9wr196K%y-?r2L)^$(-9hl(TFi$
zT>6`cZr-?Em_x-FkM+zOthc-{irq>5w96yv`&u6Bp$XM+Xk_1OQ%HR?;e_ePPNudr
z>ay0@dRYTssfHRdb+|CV6-tZYct3dR)i;mmIcPf_ynrRnjI0&cMWYMa4SPw+n>z{0
zZMPvz2fnh~?R>{SzrQF^=gI2#rhIdZ2KfnjY3m%G`uSGXpG{*lG$2dci!?Ob<W{>F
zs}-l@+(+4twzcTja24OIRyBg;O@0~?>=P;2$D$#RGJE0X9)B`5gA|~!Lo5IA*ZAS@
z21f^t6y2WAB$b`aX=!<V*HR_J#fufDHP20HPe>1j_t|J^aDe?-L*k}Qm$Y|_V=wJ}
z`QyzOZDpM(;N1Gg8(ZZHuV~u71?|t5Ka>z>K)jKLK3u?aoplKu*)*vBl5WaDA4VsN
z6xPhqGT3BvHz_Z!`_kYXIzE(yr25cX#pNVD^VoW^HzZ9^e>bOSg`gRc8iVUF*b&EJ
z=1rdY{Jm^P+lJw!>DK)!CXZ~X1IxZhl}~43Lmkn6AI{&Yz+WdFjmWGoXF^rv7kD5;
z81QYzHvOZJ+|wi|rE9=>h4y39J&>E|iR(VeCuh|Fo>&uAh*xkvdyygS3U#;bW;R^!
zjY*0yaBvJQ%Xs%{%@q27ebS^C^ojPstpq3z0MLO80FVLb05B&vcXI~^0aq()JzW9-
zUbs+^A;OCcfP;Vh%M1Tk8~~v`qY)}Z*RjgY;9;X}1jNYGVXgKDc=PAdfddZXx5sZ!
ziFLG9#3T__4S1g41i)G$q`<k-@rp#o$&;G4VJ0sBZ?mDX2|i05^g3L`jgFg*xdx?p
z2TPL5l5+9@J*`LniQ!DSpLr*7R!14Ad2YYZ@)sI;O+hRa3!=3-8QAW{iQg$1p)FT$
zB4oWN6J~7E5@~6s;%vN^sUXk6TPmKlDa~ETX0Ft`9=u#UStJmd+f=l4?DrzdXUewa
zo5Uex-nFMwl!xP%nD0hNSLiU|sQ647zXVS-Z2yMb4K0Ssv`Z(@H;ZGht>(!`vLUQ>
zv0Q3nLz<t1)7x5q@NxOScMQa=1R?eO$Ni8B#&ccLUHHnAS|m%5qA^NU6$^&>SDS10
zn6C@B)jEu$qG}>4t;6{nmn9br+MRL3|H4#5E<~<dCn;T`T8Yx7=KmJNt4i~U2vuX!
z^Ixd@*CziZhy@((?&j`l?kr&E_CJaoK??s=1Usp%QVH%C<WW`E0t=|}@o@8xX@N(;
z{HkcdQLI+~5FY7|<u!2f5BW4wqjO_pSfMxie%hJ0kT){<W1=E+Sl;K4i<i>sVH-xT
zNK0~0b>$p;^6{M>7BK*z|JGG+by?l*`h{q4{_LvFonY24-P{Ent&zj>WFbO;@b4D^
zpLP=Nr<N?3Hw?uny_9-TrQP(^xJ7B9x;pZK`^L}i6O@7>d)fHc0Im1RYn&lfqWETL
zU3z&8-C%+Hv<^W{i79<zqf^+*fMWcD@Q}`O$m&|>4KZg+Q)g*qH#i}UB{sFAFBHKP
zYH(HwvQZL#J4?h2nTBQ>xYxZMKdMXMyj~-!tgPj4le9UMcvt^y<J}X3<GKZ&R@%;E
zaLdg>(xa((qUVcPF|@nH$)gHS;-s>G1ImBTxI2?<!tNLH{<W$9X^!A#O*W!7`jn5G
z|H%Z!Q+}S2q0xG6q0gcV2MFN_1X@j#UjTEgJ~XI?CsILEmRKJ%*n=(SIh=*)kfB5q
z-G+7C@EjdmLLf{O!YL8YtyjI5i8yKL!cq+tTa<jxQ{Hk~+oTmKwoJRa;QSH;2bTu#
zKMy(n*Me{W|6%0un}G%IKR^H9_B*&B7)$~9Z$5+nPxw#e`!`_=5b$3W^PfrnS#y6U
msZaUO75FE^pBnznpaS|wMWIg#2!A^e{93lZu3ZBCM*jjf(uH^c
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a3bdf6f8325851964f6d61b6f3bc716b645ab87e
GIT binary patch
literal 4243
zc$|$_Wl$7s*WQJtSp=6@x+Rv7Mo9@}X`}=srMnxE=Al!%yIVS?myi-rx)xXv1Oy~M
z{bt_poyU2eneW_leq8t5=bG!>=lr=;70^M%0000Rpw_G^ukXiTbA=B8=o10}zi*Xf
zHJ)+8m7jClIe1&y+8T2?yBIHPdkN3pp9@9Sag$$nnNq(IfAfI8uns||@R{d*LY;$I
zhAC$~q}~ESp~HY$i@*&bUaTicW@d6LyS_c|T)iH;MP1xvBkwM}{3cGsk)p_s2^RoE
z0dtf*11_XV0V^MXgpj)d843iUAgLLE#$#uY7-$TxjQQzC3vC->g#~2rl8_9Z-IDI1
zdkn>_pC<Ck#`$zxhLQh-H-hfg6^$pTZ_S90Bb{<h6m!<G1xUXzzPub@n7DQbKXyNM
zWAw~b1Dw2)M<@F!?LF8NzPgaHwiPC#wKmk&>e=Gea{T7|%ALKiC<Qn-22AJ3GbxcA
zC*GYDCyd30#ljL87CW14xKfC{!eyx=c{NE`CRy1h-u?O~9Zt4*xZzdz!Q6dqA5k@+
z!`9UjLz04%o0HQ+UiWiEw6g9M_UJ7h8j%b?mGF0%=l&}2-shweaLSYWZibxjM%0&j
zN%V4j`50&!T2Ow=fqO7Ed2wHx9~>kwnL7Z8&w0Xo+PbMv<@n6~ZMHH&m#3RyN9<8|
z5hh`8pnsJ_cqYCSh%b)%wgeN>OINS9duRVwJY6fY7t6165vrE4Ao$vDwH=M3ao72+
zbcE#5!!#?|=*bMYUv;u$*_~y5Y%<1YtZ3H<_5!>1)-H6I&uWS67R!8kD+cmDj1jA(
zwqd$&l+M|e+6$Y2$#s~DJn&vi&9&@RC9#Z=%TVT6eQ9{wA(E7w$PQh6sHQ+u7E}+?
z{Gl$<F(nKWiri>(yWfu<sCovAM$n2t+NwKExRn=S-yF|Xn5OoWSxqwZtQU=D-6O?E
z>KKYh#5nO{lUiW!epIsWxf9n$4Dd;ijATt1!HM2b>uC27TYcglbb_?ej>qpof5beX
zEjMjX{=zLVOw6h;?*CJ1><Z*l{0KENUu`hD7sB>%oF=R_pTJG_BgZzWO3vUUjc6|`
zPpm)X0Qsf}CdKRWoH|1z4SPBKjqLX1GdA`|$C5k=S@Z=4PCsW`J#UdKq-M?)XSOpW
z&-V6Pw(%}A*v`y)_3v_390TF%(_g!6ov^Bzzt@_Vei!{>WA49`uD$+P_FNCVhT(5f
zW!HE&p+hmK#Qi+yM8U9G2#x8eBw|Hi*sF3MoaT$s^0hGYLvkUiWwVVjomJ~JAiAb<
zaH_*+TjZ4O!mD|)B~?JR!O;Fq*Ex1yXI46FZ}|&S6X>xZkPN0T7Hd0-U*rEeIEx`;
zk}X@Trwp5lvFA8Drp<BKcAaJlpr_;gwEBqHc?n9Nzi)qwl@Ye3A5q{SYvgfcSbcfs
zTBL#gnfH;FL)M3*UA`CZ{hq37gBQaS;08ns)+6=xP0E=5%xuSkwW%|?+F_gMLDUdj
zG6Db0Cg?XBpPV_<6Djn-{t#!~suBm%6R05hx;o>GMVh;*c2&cGTS<kZib8YCA==8c
zxNNzi$Q+L=-oDK%UE|b*r5e?U0f+2#RM4sw-|4y-4s?aT*)QjMz%*t9znDh7rI40I
z0Zg1sXZooxbMc9EB<o6}T$_4`{wv$Pyciqh9kdcw!5;RPucmy#yC#Db%TPho7S(*B
z7Hhl5+W-g7oUgCIF<`N1u38D}mXzWvoij9u=neL!3@7phFy8!vM({vXoR7%I{iX<~
z?6t?>4}JNW4W*hxj&=HZrqH*YG0V>t2|~Es<XwLVee}s-BWm>yR6aOi7Bt~IYwW#h
zf1s^wai$n%O<-iAGb?_fpK5a3NSWfR{qE{G*X}Szv<6o@_NsrDONRl!6~!x=J=Y<0
zE<hGRq?+w#T-~4Lcw4)kXBIb9R5TWx<(ef|PGWXmA#bjWNfu_vixJ^eApbE`LjGxn
z1w*O~vDfzum097Lz9HeL==N`>HsgH~Q=ez^Oz<o0jc6>qQa4&b2nuZ`Ur3i(lJMh~
z!_!ZEKA~m4Vcc<OBMI8rs!%j|K6g@ra0JLK040ODZt|V68Obke^rd|IH|g(7_$RQA
zZ}OeA9MF0%!dv3~^IXDU!8vCztl?aeLbI(xv!d9&3+U(_-rZzn+Wp|#vNUfEjWgf4
z8eY@tdQWntQNa_xmje<d{<}^i1HO3F)^DgSyAL~HdJ}x#KNPLsF$dxLd^~@*Z5xzs
z-+$a<BzlPI=ce#T>3*`LHUgNDVGG}NpHD2Kin}gA9dCpmncaf&(03^>nd~g$_ud0)
z7=x6iJbS8wZ;Zw)vc(C+8eV7L9Sm)!;0RQ(-vprJWYoptJ)6r*5P-e%OCf%c=@pJb
zuR13o3%AW)#oII0fVm86>fwi7UuXuxN8V%mcO-e@>lN(|pQnVRQMPwFxE{}BZR{Ve
zzTi<uN;8XL32qIC5=Ou$zTB|Tmz&}5#F?hVHc9n@=@cS{*Q6Qo`~#5dM+~a!jN?H%
zq#rNCwVKODq*rR~zd5;YLR-iUx89=mcviRDZS34}bK>A*+H6G5y&J?|R~+*a*nfs{
z^Q<It7l`I;s=GF19Wt$kF-;kg_zE^S-!DCXYPS?MJ#Rs8HJ_XXv*AEddVJy8E3NS!
zH1Ng+ijAJnn>%LSmJ9vlE5cqk`0QR^#Gbu_<kE7TQnj{-j-B~#(|=Abx$i3bGX2Ei
zs>TFfa}(@GK1hnyr?IoKshFN_(3B!{LBRU6x9nNyd?G<4;hi|?Vc&KEzTc8`!8|Y7
z!6kF~^!Dc9kssq1TlY*(qa>Ua-Ftcw;M|w(oQH)nEZ?*IRHmN?(m&=4VPM!LmIRFe
z8(%1mpob-(6B6!XN%}`YW4dxb74U=tD~DcPLTAusAIpRAWt5eH0XI|vG~TSRq{WML
zYL^jT_(@+7thSy~^hDRzmq$`&vF<fCMf?Mqtx!G*OTC8Us?1aDq|%x(q7BZEaC3eV
z85!cRTM2bEA`qIXk;b_iRCP8R6C0PrSZr?}g(?e{x<&HmI})&FHn%6S+>Ne=A0n?R
z(s!}bd^=;_gYl{5Z27X3{0msjd6r>Dqy~sl$Lk^Hr4^Sh1g_Mh(@jD<fv|&_CE7qp
z$}B>#M`9s#mO+3vN<PKe&I!S`2a;+muONXb4emdRE7T!%>w|L!z^q*mr%@4S)>J_h
zKWxQp5<*nZhXSWqy1FPdo|%$k2ncFEbC|<lUWvNed0V9)80JB1vv4?BbQwq_g-y)U
zVKb)ww9e>qk@taHS)cz0`=K^jHZG5jjDe3(^4e5AeGp7)Eh>D#??Amj7d4AqiYCZ!
ziGZjOf-!L@sa4FJ4I$#4qmVs#RSp{)iSnhpB!acX_s$l5(7s)$5xowCNDsqaiDCDI
z%1+x?B_fQ2-bObMh-fPt53F))PxTS(L1|Y<SKQ|kGUZ>-6n5U>XiP2Pmv_!EczeH(
z^zrJtWkh<9U?hyI+M<s`yh+!SIJ~&QFKj;0CqCt!53VSMm<2TEs2of(%;O@a<-duC
zcX24T4h-Ihi=ZNZb|#p>t1Bj5fGgyO*L>C(b3zUx_T=N|sPYF1apNJ|z9Sm%qqF3t
z9V)gx%5aON^7&8cU?t}SJ8y<_*ssHuN%qgx#q!RtXnktLw|L;82bFZ=p0x3{-p!bD
z0rpfh#XghO-QPDyA%J{yT@B+RRrA)VxE+G6>jOKHPalhYMsdnQo6HSr;rv9->$?fP
zG+CmO{ipZIW+zDa`E@9w8Mxpt5}14MJq=vyX5O+4?Oqmn_sM~c*sq_OB^PB`z)^nZ
zC=}~Vf}3AqJsKO{UB$&pq`2diZwcCdC>&|st;1!{{^4CUnNvk?`JS9@YntGvJxiRZ
z{sKEz^>a9L&%l$1atd?qE+c(c&&a71j9(%Y;NSa<iUgLkjm_?R%4()puYA~ta)m5e
zpDHTNxu-)n*^N@CO}6X7%h8ZGvOf?xB&n4K2VT>{Hb=#dCxWSJLYz)=lY?x@l1qwr
z*00j0D3eZu9jvUv)fj3TtAtTf+T~_-`vaJX98Kn`BWoBMgH(Q9xhWRutx0TB0_jOq
zz=^YYp5p9PZp$*T(M%;g<IIo0qod;Gz&2CFE{Sdy1>PxOptdQou1uotEHYUhswrdg
zUg8`2q;kD}<&hhe&!9I<uyjLBXeezFtIICYM6BpJQF1S3In$t)bh(y=TIFUeckT5{
z%qQ~-Q4A1jK5pbPA^9EE+m>a3kQiC|MU0$FDbrdk&n^|-scqW$0mbRf!@q1bg%WyL
z#~1SDCXT3dhgiGPho|IDLV%jaHd_dT2ZtF#jyrNYR%yA>!ix%2J3Ct_u_KHLESByX
zf|`=bOjM+z{sAQ2=z}mG=qY>1?!n>V!?&fllX5+$icK;iIRh``FNX`?OkY%`73@bA
zz$bsCZ8Y}qDXL9WM?f>3ZRxeM3>5`*xMPCbb8{1pgQ+#E*W0Qb)+UhyuDZ(G6Dys0
zq|#dPWO=VCjf?L=s{iMe=BriY_keDW#lO(~uLJ|206SxQD|0g!S1xN82YXEo3;=qO
zc+o32Pdoq+^aBV0{BNmSbT4k-WBkw~pNP0qXnjHWC13-t!oY|!VRP5BYWpjuv;FXq
z=gIq<)f{eh%y*n_I};Df<2zHkK5`~`p;0$ck%_**e(|}`z|jeeF&HAmF-l4HIitHN
zhZJD~X&_E;`OGTc{BQ(Rsv~yg#;?TwRaO%mxlfp{AMwGT(xz;;^LeVVfmw{7jZ9ZH
z46_`=rH5sng(BKurO@cbXRV;-`XN0*3tH8`nL+^?evhDEC<XARDf~~zcIKx_V+chB
zPOd4i0w?>#*kr@Y$FqEE8z%~5N=+|^L*)0(NRUXUR@34j;-@=>NHVkcrdy<XY?p1R
z>>wXu3TGgB8=)5rqx(2qHK@Fn<M6D~v>~e8NFC9~x+<xs#}y~vQjQz6B}e9hJTOgU
zYA7AOF^SD|J(L+bmdT;g*5l#={9Uw7BMYAOFXZ(rA^+rLUCi+&6ekrp*f{0q{wDgw
zjc3knL{a5eGe3TJh0a?^9Efm1A3Is1y2Z;3y6L;E|AC(H5i(<E*BU5B1?EZ4XiM`V
z_uPV-A&b+rO_R36D6m)_?WbIZ6harAoq)fj0nv!j|J`Q)C*A=8|6;m-Gti*_`||yc
z-^GOi9ALozZx#O(_2-!XD~g2RpNIY@!=HctUxrbFf1H4-0tV*q2cTaE`b(GDgWu?X
E0J%P}egFUf
--- a/browser/base/content/test/general/browser_offlineQuotaNotification.js
+++ b/browser/base/content/test/general/browser_offlineQuotaNotification.js
@@ -1,13 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+/* eslint-env mozilla/frame-script */
+
 // Test offline quota warnings - must be run as a mochitest-browser test or
 // else the test runner gets in the way of notifications due to bug 857897.
 
 const URL = "http://mochi.test:8888/browser/browser/base/content/test/general/offlineQuotaNotification.html";
 
 registerCleanupFunction(function() {
   // Clean up after ourself
   let uri = Services.io.newURI(URL);
--- a/browser/base/content/test/general/browser_restore_isAppTab.js
+++ b/browser/base/content/test/general/browser_restore_isAppTab.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 
 const DUMMY = "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
 
 function getMinidumpDirectory() {
   let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
   dir.append("minidumps");
   return dir;
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -102,16 +102,25 @@ const PAGECONTENT_COLORS_ON_SELECT =
   "</style>" +
   "<body><select id='one'>" +
   '  <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
   '  <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
   '  <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
   '  <option value="Four" selected="true">{"end": "true"}</option>' +
   "</select></body></html>";
 
+const TRANSPARENT_SELECT =
+  "<html><head><style>" +
+  "  #one { background-color: transparent; }" +
+  "</style>" +
+  "<body><select id='one'>" +
+  '  <option value="One">{"unstyled": "true"}</option>' +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
 function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) {
   let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
 
   if (mode == "click" || mode == "mousedown") {
     let mousePromise;
     if (mode == "click") {
       mousePromise = BrowserTestUtils.synthesizeMouseAtCenter(selector, { }, win.gBrowser.selectedBrowser);
     } else {
@@ -156,26 +165,32 @@ function getChangeEvents() {
 }
 
 function getClickEvents() {
   return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     return content.wrappedJSObject.gClickEvents;
   });
 }
 
+function getSystemColor(color) {
+  // Need to convert system color to RGB color.
+  let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
+  textarea.style.color = color;
+  return getComputedStyle(textarea).color;
+}
+
 function testOptionColors(index, item, menulist) {
+  // The label contains a JSON string of the expected colors for
+  // `color` and `background-color`.
   let expected = JSON.parse(item.label);
 
   for (let color of Object.keys(expected)) {
     if (color.toLowerCase().includes("color") &&
         !expected[color].startsWith("rgb")) {
-      // Need to convert system color to RGB color.
-      let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
-      textarea.style.color = expected[color];
-      expected[color] = getComputedStyle(textarea).color;
+      expected[color] = getSystemColor(expected[color]);
     }
   }
 
   // Press Down to move the selected item to the next item in the
   // list and check the colors of this item when it's not selected.
   EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
 
   if (expected.end) {
@@ -287,16 +302,17 @@ function* doSelectTests(contentType, dtd
 
   yield BrowserTestUtils.removeTab(tab);
 }
 
 add_task(function* setup() {
   yield SpecialPowers.pushPrefEnv({
     "set": [
       ["dom.select_popup_in_parent.enabled", true],
+      ["dom.forms.select.customstyling", true]
     ]
   });
 });
 
 add_task(function*() {
   yield doSelectTests("text/html", "");
 });
 
@@ -824,18 +840,16 @@ add_task(function* test_colors_applied_t
 
   let menulist = document.getElementById("ContentSelectDropdown");
   let selectPopup = menulist.menupopup;
 
   let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
   yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
   yield popupShownPromise;
 
-  // The label contains a JSON string of the expected colors for
-  // `color` and `background-color`.
   is(selectPopup.parentNode.itemCount, 4, "Correct number of items");
   let child = selectPopup.firstChild;
   let idx = 1;
 
   is(getComputedStyle(selectPopup).color, "rgb(255, 255, 255)",
     "popup has expected foreground color");
   is(getComputedStyle(selectPopup).backgroundColor, "rgb(126, 58, 58)",
     "popup has expected background color");
@@ -843,16 +857,50 @@ add_task(function* test_colors_applied_t
   ok(!child.selected, "The first child should not be selected");
   while (child) {
     testOptionColors(idx, child, menulist);
     idx++;
     child = child.nextSibling;
   }
 
   yield hideSelectPopup(selectPopup, "escape");
+
+  yield BrowserTestUtils.removeTab(tab);
+});
+
+// This test checks when a <select> element has a transparent background applied to itself.
+add_task(function* test_transparent_applied_to_popup() {
+  const pageUrl = "data:text/html," + escape(TRANSPARENT_SELECT);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+  let menulist = document.getElementById("ContentSelectDropdown");
+  let selectPopup = menulist.menupopup;
+
+  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
+  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
+  yield popupShownPromise;
+
+  is(selectPopup.parentNode.itemCount, 2, "Correct number of items");
+  let child = selectPopup.firstChild;
+  let idx = 1;
+
+  is(getComputedStyle(selectPopup).color, getSystemColor("-moz-ComboboxText"),
+    "popup has expected foreground color");
+  is(getComputedStyle(selectPopup).backgroundColor, getSystemColor("-moz-Combobox"),
+    "popup has expected background color");
+
+  ok(!child.selected, "The first child should not be selected");
+  while (child) {
+    testOptionColors(idx, child, menulist);
+    idx++;
+    child = child.nextSibling;
+  }
+
+  yield hideSelectPopup(selectPopup, "escape");
+
   yield BrowserTestUtils.removeTab(tab);
 });
 
 // This test checks that the popup is closed when the select element is blurred.
 add_task(function* test_blur_hides_popup() {
   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_storagePressure_notification.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function notifyStoragePressure(usage = 100) {
+  let notifyPromise = TestUtils.topicObserved("QuotaManager::StoragePressure", () => true);
+  Services.obs.notifyObservers(null, "QuotaManager::StoragePressure", usage);
+  return notifyPromise;
+}
+
+function advancedAboutPrefPromise() {
+  let promises = [
+    BrowserTestUtils.waitForLocationChange(gBrowser, "about:preferences#advanced"),
+    TestUtils.topicObserved("advanced-pane-loaded", () => true)
+  ];
+  return Promise.all(promises);
+}
+
+// Test only displaying notification once within the given interval
+add_task(function* () {
+  const TEST_NOTIFICATION_INTERVAL_MS = 2000;
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]});
+
+  yield notifyStoragePressure();
+  let notificationbox = document.getElementById("high-priority-global-notificationbox");
+  let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  ok(notification instanceof XULElement, "Should display storage pressure notification");
+  notification.close();
+
+  yield notifyStoragePressure();
+  notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  is(notification, null, "Should not display storage pressure notification more than once within the given interval");
+
+  yield new Promise(resolve => setTimeout(resolve, TEST_NOTIFICATION_INTERVAL_MS + 1));
+  yield notifyStoragePressure();
+  notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  ok(notification instanceof XULElement, "Should display storage pressure notification after the given interval");
+  notification.close();
+});
+
+// Test guiding user to about:preferences when usage exceeds the given threshold
+add_task(function* () {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", 0]]});
+
+  const BYTES_IN_GIGABYTE = 1073741824;
+  const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
+    Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
+  yield notifyStoragePressure(USAGE_THRESHOLD_BYTES);
+  let notificationbox = document.getElementById("high-priority-global-notificationbox");
+  let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  ok(notification instanceof XULElement, "Should display storage pressure notification");
+
+  let prefBtn = notification.getElementsByTagName("button")[1];
+  let aboutPrefPromise = advancedAboutPrefPromise();
+  prefBtn.doCommand();
+  yield aboutPrefPromise;
+  let prefDoc = gBrowser.selectedBrowser.contentDocument;
+  let advancedPrefs = prefDoc.getElementById("advancedPrefs");
+  is(advancedPrefs.selectedIndex, 2, "Should open the Network tab in about:preferences#advanced");
+});
--- a/browser/base/content/test/general/browser_tab_dragdrop.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 function swapTabsAndCloseOther(a, b) {
   gBrowser.swapBrowsersAndCloseOther(gBrowser.tabs[b], gBrowser.tabs[a]);
 }
 
 var getClicks = function(tab) {
   return ContentTask.spawn(tab.linkedBrowser, {}, function() {
     return content.wrappedJSObject.clicks;
   });
--- a/browser/base/content/test/general/browser_tabfocus.js
+++ b/browser/base/content/test/general/browser_tabfocus.js
@@ -1,12 +1,14 @@
 /*
  * This test checks that focus is adjusted properly when switching tabs.
  */
 
+/* eslint-env mozilla/frame-script */
+
 var testPage1 = "<html id='html1'><body id='body1'><button id='button1'>Tab 1</button></body></html>";
 var testPage2 = "<html id='html2'><body id='body2'><button id='button2'>Tab 2</button></body></html>";
 var testPage3 = "<html id='html3'><body id='body3'><button id='button3'>Tab 3</button></body></html>";
 
 const fm = Services.focus;
 
 function EventStore() {
   this["main-window"] = [];
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_webext_search.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="2">
+  <addon>
+    <name>permissions test</name>
+    <type id='1'>Extension</type>
+    <guid>permissions@tests.mozilla.org</guid>
+    <version>1.1</version>
+    <authors>
+      <author>
+        <name>Test Creator</name>
+        <link>http://example.com/creator.html</link>
+      </author>
+    </authors>
+    <status id='4'>Public</status>
+    <compatible_applications>
+      <application>
+        <name>Firefox</name>
+        <appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
+        <min_version>0</min_version>
+        <max_version>*</max_version>
+      </application>
+    </compatible_applications>
+    <compatible_os>ALL</compatible_os>
+    <install size="1">https://example.com/browser/browser/base/content/test/general/browser_webext_permissions.xpi</install>
+  </addon>
+
+  <addon>
+    <name>no permissions</name>
+    <type id='1'>Extension</type>
+    <guid>nopermissions@tests.mozilla.org</guid>
+    <version>1.0</version>
+    <authors>
+      <author>
+        <name>Test Creator</name>
+        <link>http://example.com/creator.html</link>
+      </author>
+    </authors>
+    <status id='4'>Public</status>
+    <compatible_applications>
+      <application>
+        <name>Firefox</name>
+        <appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
+        <min_version>0</min_version>
+        <max_version>*</max_version>
+      </application>
+    </compatible_applications>
+    <compatible_os>ALL</compatible_os>
+    <install size="1">https://example.com/browser/browser/base/content/test/general/browser_webext_nopermissions.xpi</install>
+  </addon>
+</searchresults>
+
--- a/browser/base/content/test/general/browser_webext_update.json
+++ b/browser/base/content/test/general/browser_webext_update.json
@@ -36,11 +36,25 @@
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "55.0"
             }
           }
         }
       ]
+    },
+    "legacy_update@tests.mozilla.org": {
+      "updates": [
+        {
+          "version": "2.0",
+          "update_link": "https://example.com/browser/browser/base/content/test/general/browser_legacy_webext.xpi",
+          "applications": {
+            "gecko": {
+              "strict_min_version": "1",
+              "advisory_max_version": "*"
+            }
+          }
+        }
+      ]
     }
   }
 }
--- a/browser/base/content/test/general/browser_windowactivation.js
+++ b/browser/base/content/test/general/browser_windowactivation.js
@@ -1,12 +1,14 @@
 /*
  * This test checks that window activation state is set properly with multiple tabs.
  */
 
+/* eslint-env mozilla/frame-script */
+
 var testPage = "data:text/html,<body><style>:-moz-window-inactive { background-color: red; }</style><div id='area'></div></body>";
 
 var colorChangeNotifications = 0;
 var otherWindow;
 
 var browser1, browser2;
 
 function test() {
--- a/browser/base/content/test/general/contentSearchUI.js
+++ b/browser/base/content/test/general/contentSearchUI.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 "use strict";
 
 (function() {
 
 const TEST_MSG = "ContentSearchUIControllerTest";
 const ENGINE_NAME = "browser_searchSuggestionEngine searchSuggestionEngine.xml";
 var gController;
 
--- a/browser/base/content/test/general/content_aboutAccounts.js
+++ b/browser/base/content/test/general/content_aboutAccounts.js
@@ -1,15 +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/. */
 
 // This file is loaded as a "content script" for browser_aboutAccounts tests
 "use strict";
 
+/* eslint-env mozilla/frame-script */
+
 var {interfaces: Ci, utils: Cu} = Components;
 
 addEventListener("load", function load(event) {
   if (event.target != content.document) {
     return;
   }
 //  content.document.removeEventListener("load", load, true);
   sendAsyncMessage("test:document:load");
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
--- a/browser/base/content/test/newtab/browser_newtab_bug734043.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug734043.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 add_task(function* () {
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield* addNewTabPageTab();
   yield* checkGrid("0,1,2,3,4,5,6,7,8");
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
--- a/browser/base/content/test/newtab/browser_newtab_bug991111.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug991111.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 add_task(function* () {
   // set max rows to 1, to avoid scroll events by clicking middle button
   yield pushPrefs(["browser.newtabpage.rows", 1]);
   yield setLinks("-1");
   yield* addNewTabPageTab();
   // we need a second newtab to honor max rows
   yield* addNewTabPageTab();
 
--- a/browser/base/content/test/newtab/browser_newtab_bug998387.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug998387.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 add_task(function* () {
   // set max rows to 1, to avoid scroll events by clicking middle button
   yield pushPrefs(["browser.newtabpage.rows", 1]);
   yield setLinks("0");
   yield* addNewTabPageTab();
   // we need a second newtab to honor max rows
   yield* addNewTabPageTab();
 
--- a/browser/base/content/test/newtab/browser_newtab_search.js
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 // See browser/components/search/test/browser_*_behavior.js for tests of actual
 // searches.
 
 Cu.import("resource://gre/modules/Task.jsm");
 
 const ENGINE_NO_LOGO = {
   name: "searchEngineNoLogo.xml",
   numLogos: 0,
--- a/browser/base/content/test/newtab/content-reflows.js
+++ b/browser/base/content/test/newtab/content-reflows.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* eslint-env mozilla/frame-script */
+
 (function() {
   "use strict";
 
   const Ci = Components.interfaces;
 
   docShell.addWeakReflowObserver({
     reflow() {
       // Gather information about the current code path.
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_5.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_5.js
@@ -334,16 +334,18 @@ var tests = [
   // Test clicking the anchor icon.
   // Clicking the anchor of an already visible persistent notification should
   // focus the main action button, but not cause additional showing/shown event
   // callback calls.
   // Clicking the anchor of a dismissed notification should show it, even when
   // the currently displayed notification is a persistent one.
   { id: "Test#11",
     *run() {
+      yield SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
+
       function clickAnchor(notifyObj) {
         let anchor = document.getElementById(notifyObj.anchorID);
         EventUtils.synthesizeMouseAtCenter(anchor, {});
       }
 
       let popup = PopupNotifications.panel;
 
       let notifyObj1 = new BasicNotification(this.id);
@@ -354,20 +356,21 @@ var tests = [
       let notification1 = showNotification(notifyObj1);
       yield shown;
       checkPopup(popup, notifyObj1);
       ok(!notifyObj1.dismissalCallbackTriggered,
          "Should not have dismissed the notification");
       notifyObj1.shownCallbackTriggered = false;
       notifyObj1.showingCallbackTriggered = false;
 
-      // Click the anchor. This should focus the primary button, but
-      // not call event callbacks on the notification object.
+      // Click the anchor. This should focus the closebutton
+      // (because it's the first focusable element), but not
+      // call event callbacks on the notification object.
       clickAnchor(notifyObj1);
-      is(document.activeElement, popup.childNodes[0].button);
+      is(document.activeElement, popup.childNodes[0].closebutton);
       ok(!notifyObj1.dismissalCallbackTriggered,
          "Should not have dismissed the notification");
       ok(!notifyObj1.shownCallbackTriggered,
          "Should have triggered the shown event again");
       ok(!notifyObj1.showingCallbackTriggered,
          "Should have triggered the showing event again");
 
       // Add another notification.
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_keyboard.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_keyboard.js
@@ -48,27 +48,88 @@ var tests = [
       ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
       ok(!this.notifyObj.removedCallbackTriggered, "removed callback was not triggered");
       this.notification.remove();
     }
   },
   // Test that the space key on an anchor element focuses an active notification
   { id: "Test#3",
     *run() {
+      yield SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.anchorID = "geo-notification-icon";
       this.notifyObj.addOptions({
-        persistent: true
+        persistent: true,
       });
       this.notification = showNotification(this.notifyObj);
     },
     *onShown(popup) {
       checkPopup(popup, this.notifyObj);
       let anchor = document.getElementById(this.notifyObj.anchorID);
       anchor.focus();
       is(document.activeElement, anchor);
       EventUtils.synthesizeKey(" ", {});
-      is(document.activeElement, popup.childNodes[0].button);
+      is(document.activeElement, popup.childNodes[0].closebutton);
       this.notification.remove();
     },
     onHidden(popup) { }
   },
+  // Test that you can switch between active notifications with the space key
+  // and that the notification is focused on selection.
+  { id: "Test#4",
+    *run() {
+      yield SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
+
+      let notifyObj1 = new BasicNotification(this.id);
+      notifyObj1.id += "_1";
+      notifyObj1.anchorID = "default-notification-icon";
+      notifyObj1.addOptions({
+        hideClose: true,
+        checkbox: {
+          label: "Test that elements inside the panel can be focused",
+        },
+        persistent: true,
+      });
+      let opened = waitForNotificationPanel();
+      let notification1 = showNotification(notifyObj1);
+      yield opened;
+
+      let notifyObj2 = new BasicNotification(this.id);
+      notifyObj2.id += "_2";
+      notifyObj2.anchorID = "geo-notification-icon";
+      notifyObj2.addOptions({
+        persistent: true,
+      });
+      opened = waitForNotificationPanel();
+      let notification2 = showNotification(notifyObj2);
+      let popup = yield opened;
+
+      // Make sure notification 2 is visible
+      checkPopup(popup, notifyObj2);
+
+      // Activate the anchor for notification 1 and wait until it's shown.
+      let anchor = document.getElementById(notifyObj1.anchorID);
+      anchor.focus();
+      is(document.activeElement, anchor);
+      opened = waitForNotificationPanel();
+      EventUtils.synthesizeKey(" ", {});
+      popup = yield opened;
+      checkPopup(popup, notifyObj1);
+
+      is(document.activeElement, popup.childNodes[0].checkbox);
+
+      // Activate the anchor for notification 2 and wait until it's shown.
+      anchor = document.getElementById(notifyObj2.anchorID);
+      anchor.focus();
+      is(document.activeElement, anchor);
+      opened = waitForNotificationPanel();
+      EventUtils.synthesizeKey(" ", {});
+      popup = yield opened;
+      checkPopup(popup, notifyObj2);
+
+      is(document.activeElement, popup.childNodes[0].closebutton);
+
+      notification1.remove();
+      notification2.remove();
+      goNext();
+    },
+  },
 ];
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -1,8 +1,9 @@
+/* eslint-env mozilla/frame-script */
 
 var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
 
 var baseURL = "https://example.com/browser/browser/base/content/test/social/";
 
 var manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
--- a/browser/base/content/test/social/social_crash_content_helper.js
+++ b/browser/base/content/test/social/social_crash_content_helper.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 var Cu = Components.utils;
 
 // Ideally we would use CrashTestUtils.jsm, but that's only available for
 // xpcshell tests - so we just copy a ctypes crasher from it.
 Cu.import("resource://gre/modules/ctypes.jsm");
 var crash = function() { // this will crash when called.
   let zero = new ctypes.intptr_t(8);
   let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
--- a/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js
+++ b/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js
@@ -10,40 +10,107 @@ add_task(function* setup() {
     Services.prefs.clearUserPref(PREF_AUTOFILL);
     yield PlacesTestUtils.clearHistory();
     gURLBar.handleRevert();
   });
   Services.prefs.setBoolPref(PREF_TRIMURL, true);
   Services.prefs.setBoolPref(PREF_AUTOFILL, true);
 
   // Adding a tab would hit switch-to-tab, so it's safer to just add a visit.
-  yield PlacesTestUtils.addVisits({
+  yield PlacesTestUtils.addVisits([{
     uri: "http://www.autofilltrimurl.com/whatever",
     transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
-  });
+  }, {
+    uri: "https://www.secureautofillurl.com/whatever",
+    transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }]);
 });
 
 function* promiseSearch(searchtext) {
   gURLBar.focus();
   gURLBar.inputField.value = searchtext.substr(0, searchtext.length - 1);
   EventUtils.synthesizeKey(searchtext.substr(-1, 1), {});
   yield promiseSearchComplete();
 }
 
-add_task(function* () {
-  yield promiseSearch("http://");
-  is(gURLBar.inputField.value, "http://", "Autofilled value is as expected");
+function* promiseTestResult(test) {
+  info("Searching for '${test.search}'");
+
+  yield promiseSearch(test.search);
+
+  is(gURLBar.inputField.value, test.autofilledValue,
+     `Autofilled value is as expected for search '${test.search}'`);
+
+  let result = gURLBar.popup.richlistbox.getItemAtIndex(0);
+
+  is(result._titleText.textContent, test.resultListDisplayTitle,
+     `Autocomplete result should have displayed title as expected for search '${test.search}'`);
+
+  is(result._actionText.textContent, test.resultListActionText,
+     `Autocomplete action text should be as expected for search '${test.search}'`);
+
+  is(result.getAttribute("type"), test.resultListType,
+     `Autocomplete result should have searchengine for the type for search '${test.search}'`);
+
+  is(gURLBar.mController.getFinalCompleteValueAt(0), test.finalCompleteValue,
+     `Autocomplete item should go to the expected final value for search '${test.search}'`);
+}
+
+const tests = [{
+    search: "http://",
+    autofilledValue: "http://",
+    resultListDisplayTitle: "http://",
+    resultListActionText: "Search with Google",
+    resultListType: "searchengine",
+    finalCompleteValue: 'moz-action:searchengine,{"engineName":"Google","input":"http%3A%2F%2F","searchQuery":"http%3A%2F%2F"}'
+  }, {
+    search: "https://",
+    autofilledValue: "https://",
+    resultListDisplayTitle: "https://",
+    resultListActionText: "Search with Google",
+    resultListType: "searchengine",
+    finalCompleteValue: 'moz-action:searchengine,{"engineName":"Google","input":"https%3A%2F%2F","searchQuery":"https%3A%2F%2F"}'
+  }, {
+    search: "au",
+    autofilledValue: "autofilltrimurl.com/",
+    resultListDisplayTitle: "www.autofilltrimurl.com",
+    resultListActionText: "Visit",
+    resultListType: "",
+    finalCompleteValue: "www.autofilltrimurl.com/"
+  }, {
+    search: "http://au",
+    autofilledValue: "http://autofilltrimurl.com/",
+    resultListDisplayTitle: "autofilltrimurl.com",
+    resultListActionText: "Visit",
+    resultListType: "",
+    finalCompleteValue: "http://autofilltrimurl.com/"
+  }, {
+    search: "sec",
+    autofilledValue: "secureautofillurl.com/",
+    resultListDisplayTitle: "https://www.secureautofillurl.com",
+    resultListActionText: "Visit",
+    resultListType: "",
+    finalCompleteValue: "https://www.secureautofillurl.com/"
+  }, {
+    search: "https://sec",
+    autofilledValue: "https://secureautofillurl.com/",
+    resultListDisplayTitle: "https://secureautofillurl.com",
+    resultListActionText: "Visit",
+    resultListType: "",
+    finalCompleteValue: "https://secureautofillurl.com/"
+  },
+];
+
+add_task(function* autofill_tests() {
+  for (let test of tests) {
+    yield promiseTestResult(test);
+  }
 });
 
-add_task(function* () {
-  yield promiseSearch("http://au");
-  is(gURLBar.inputField.value, "http://autofilltrimurl.com/", "Autofilled value is as expected");
-});
-
-add_task(function* () {
+add_task(function* autofill_complete_domain() {
   yield promiseSearch("http://www.autofilltrimurl.com");
   is(gURLBar.inputField.value, "http://www.autofilltrimurl.com/", "Autofilled value is as expected");
 
   // Now ensure selecting from the popup correctly trims.
   is(gURLBar.controller.matchCount, 2, "Found the expected number of matches");
   EventUtils.synthesizeKey("VK_DOWN", {});
   is(gURLBar.inputField.value, "www.autofilltrimurl.com/whatever", "trim was applied correctly");
 });
--- a/browser/base/content/test/urlbar/head.js
+++ b/browser/base/content/test/urlbar/head.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
@@ -196,9 +198,8 @@ function promiseNewSearchEngine(basename
       },
       onError(errCode) {
         Assert.ok(false, "addEngine failed with error code " + errCode);
         reject();
       },
     });
   });
 }
-
--- a/browser/base/content/test/urlbar/urlbarAddonIframeContentScript.js
+++ b/browser/base/content/test/urlbar/urlbarAddonIframeContentScript.js
@@ -1,8 +1,10 @@
+/* eslint-env mozilla/frame-script */
+
 // Forward messages from the test to the iframe as events.
 addMessageListener("TestMessage", msg => {
   content.dispatchEvent(new content.CustomEvent("TestEvent", {
     detail: Components.utils.cloneInto(msg.data, content),
   }));
 });
 
 // Forward events from the iframe to the test as messages.
--- a/browser/base/content/test/webrtc/get_user_media_content_script.js
+++ b/browser/base/content/test/webrtc/get_user_media_content_script.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint-env mozilla/frame-script */
+
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
                                    "@mozilla.org/mediaManagerService;1",
                                    "nsIMediaManagerService");
 
 const kObservedTopics = [
   "getUserMedia:response:allow",
   "getUserMedia:revoke",
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -470,17 +470,17 @@ function createUserContextMenu(event, {
     // isContextMenu will be true.
 
     docfrag.appendChild(menuitem);
 
     let menuseparator = document.createElement("menuseparator");
     docfrag.appendChild(menuseparator);
   }
 
-  ContextualIdentityService.getIdentities().forEach(identity => {
+  ContextualIdentityService.getPublicIdentities().forEach(identity => {
     if (identity.userContextId == excludeUserContextId) {
       return;
     }
 
     let menuitem = document.createElement("menuitem");
     menuitem.setAttribute("data-usercontextid", identity.userContextId);
     menuitem.setAttribute("label", ContextualIdentityService.getUserContextLabel(identity.userContextId));
 
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -29,17 +29,17 @@ struct RedirEntry {
 /*
   Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome
   privileges. This is potentially dangerous. Please use
   URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below
   unless your about: page really needs chrome privileges. Security review is
   required before adding new map entries without
   URI_SAFE_FOR_UNTRUSTED_CONTENT.
 */
-static RedirEntry kRedirMap[] = {
+static const RedirEntry kRedirMap[] = {
   { "blocked", "chrome://browser/content/blockedSite.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
   { "certerror", "chrome://browser/content/aboutNetError.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -1,15 +1,16 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <panel id="identity-popup"
        type="arrow"
        hidden="true"
+       role="alertdialog"
        onpopupshown="gIdentityHandler.onPopupShown(event);"
        onpopuphidden="gIdentityHandler.onPopupHidden(event);"
        orient="vertical">
 
   <broadcasterset>
     <broadcaster id="identity-popup-mcb-learn-more" class="text-link plain" value="&identity.learnMore;"/>
     <broadcaster id="identity-popup-insecure-login-forms-learn-more" class="text-link plain" value="&identity.learnMore;"/>
   </broadcasterset>
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -1135,17 +1135,17 @@ const CustomizableWidgets = [
 
       while (items.firstChild) {
         items.firstChild.remove();
       }
 
       let fragment = doc.createDocumentFragment();
       let bundle = doc.getElementById("bundle_browser");
 
-      ContextualIdentityService.getIdentities().forEach(identity => {
+      ContextualIdentityService.getPublicIdentities().forEach(identity => {
         let label = ContextualIdentityService.getUserContextLabel(identity.userContextId);
 
         let item = doc.createElementNS(kNSXUL, "toolbarbutton");
         item.setAttribute("label", label);
         item.setAttribute("usercontextid", identity.userContextId);
         item.setAttribute("class", "subviewbutton");
         item.setAttribute("data-identity-color", identity.color);
         item.setAttribute("data-identity-icon", identity.icon);
--- a/browser/components/extensions/.eslintrc.js
+++ b/browser/components/extensions/.eslintrc.js
@@ -8,14 +8,16 @@ module.exports = {  // eslint-disable-li
     "IconDetails": true,
     "Tab": true,
     "TabContext": true,
     "Window": true,
     "WindowEventManager": true,
     "browserActionFor": true,
     "getCookieStoreIdForTab": true,
     "getDevToolsTargetForContext": true,
+    "getTargetTabIdForToolbox": true,
     "makeWidgetId": true,
     "pageActionFor": true,
+    "sidebarActionFor": true,
     "tabTracker": true,
     "windowTracker": true,
   },
 };
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-c-devtools-panels.js
@@ -0,0 +1,148 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+
+const {
+  promiseDocumentLoaded,
+  SingletonEventManager,
+} = ExtensionUtils;
+
+const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
+
+/**
+ * Represents an addon devtools panel in the child process.
+ *
+ * @param {DevtoolsExtensionContext}
+ *   A devtools extension context running in a child process.
+ * @param {object} panelOptions
+ * @param {string} panelOptions.id
+ *   The id of the addon devtools panel registered in the main process.
+ */
+class ChildDevToolsPanel extends EventEmitter {
+  constructor(context, {id}) {
+    super();
+
+    this.context = context;
+    this.context.callOnClose(this);
+
+    this.id = id;
+    this._panelContext = null;
+
+    this.mm = context.messageManager;
+    this.mm.addMessageListener("Extension:DevToolsPanelShown", this);
+    this.mm.addMessageListener("Extension:DevToolsPanelHidden", this);
+  }
+
+  get panelContext() {
+    if (this._panelContext) {
+      return this._panelContext;
+    }
+
+    for (let view of this.context.extension.devtoolsViews) {
+      if (view.viewType === "devtools_panel" &&
+          view.devtoolsToolboxInfo.toolboxPanelId === this.id) {
+        this._panelContext = view;
+        return view;
+      }
+    }
+
+    return null;
+  }
+
+  receiveMessage({name, data}) {
+    // Filter out any message received while the panel context do not yet
+    // exist.
+    if (!this.panelContext || !this.panelContext.contentWindow) {
+      return;
+    }
+
+    // Filter out any message that is not related to the id of this
+    // toolbox panel.
+    if (!data || data.toolboxPanelId !== this.id) {
+      return;
+    }
+
+    switch (name) {
+      case "Extension:DevToolsPanelShown":
+        this.onParentPanelShown();
+        break;
+      case "Extension:DevToolsPanelHidden":
+        this.onParentPanelHidden();
+        break;
+    }
+  }
+
+  onParentPanelShown() {
+    const {document} = this.panelContext.contentWindow;
+
+    // Ensure that the onShown event is fired when the panel document has
+    // been fully loaded.
+    promiseDocumentLoaded(document).then(() => {
+      this.emit("shown", this.panelContext.contentWindow);
+    });
+  }
+
+  onParentPanelHidden() {
+    this.emit("hidden");
+  }
+
+  api() {
+    return {
+      onShown: new SingletonEventManager(
+        this.context, "devtoolsPanel.onShown", fire => {
+          const listener = (eventName, panelContentWindow) => {
+            fire.asyncWithoutClone(panelContentWindow);
+          };
+          this.on("shown", listener);
+          return () => {
+            this.off("shown", listener);
+          };
+        }).api(),
+
+      onHidden: new SingletonEventManager(
+        this.context, "devtoolsPanel.onHidden", fire => {
+          const listener = () => {
+            fire.async();
+          };
+          this.on("hidden", listener);
+          return () => {
+            this.off("hidden", listener);
+          };
+        }).api(),
+
+      // TODO(rpl): onSearch event and createStatusBarButton method
+    };
+  }
+
+  close() {
+    this.mm.removeMessageListener("Extension:DevToolsPanelShown", this);
+    this.mm.removeMessageListener("Extension:DevToolsPanelHidden", this);
+
+    this._panelContext = null;
+    this.context = null;
+  }
+}
+
+extensions.registerSchemaAPI("devtools.panels", "devtools_child", context => {
+  return {
+    devtools: {
+      panels: {
+        create(title, icon, url) {
+          return context.cloneScope.Promise.resolve().then(async () => {
+            const panelId = await context.childManager.callParentAsyncFunction(
+              "devtools.panels.create", [title, icon, url]);
+
+            const devtoolsPanel = new ChildDevToolsPanel(context, {id: panelId});
+
+            const devtoolsPanelAPI = Cu.cloneInto(devtoolsPanel.api(),
+                                                  context.cloneScope,
+                                                  {cloneFunctions: true});
+            return devtoolsPanelAPI;
+          });
+        },
+      },
+    },
+  };
+});
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/ext-commands.js
@@ -120,26 +120,32 @@ CommandList.prototype = {
     // We need to have the attribute "oncommand" for the "command" listener to fire,
     // and it is currently ignored when set to the empty string.
     keyElement.setAttribute("oncommand", "//");
 
     /* eslint-disable mozilla/balanced-listeners */
     // We remove all references to the key elements when the extension is shutdown,
     // therefore the listeners for these elements will be garbage collected.
     keyElement.addEventListener("command", (event) => {
+      let action;
       if (name == "_execute_page_action") {
-        let win = event.target.ownerGlobal;
-        pageActionFor(this.extension).triggerAction(win);
+        action = pageActionFor(this.extension);
       } else if (name == "_execute_browser_action") {
-        let win = event.target.ownerGlobal;
-        browserActionFor(this.extension).triggerAction(win);
+        action = browserActionFor(this.extension);
+      } else if (name == "_execute_sidebar_action") {
+        action = sidebarActionFor(this.extension);
       } else {
         this.extension.tabManager
             .addActiveTabPermission();
         this.emit("command", name);
+        return;
+      }
+      if (action) {
+        let win = event.target.ownerGlobal;
+        action.triggerAction(win);
       }
     });
     /* eslint-enable mozilla/balanced-listeners */
 
     return keyElement;
   },
 
   /**
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-devtools-panels.js
@@ -0,0 +1,255 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/ExtensionParent.jsm");
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
+                                  "resource:///modules/E10SUtils.jsm");
+
+const {
+  watchExtensionProxyContextLoad,
+} = ExtensionParent;
+
+const {
+  IconDetails,
+  promiseEvent,
+} = ExtensionUtils;
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+/**
+ * Represents an addon devtools panel in the main process.
+ *
+ * @param {ExtensionChildProxyContext} context
+ *        A devtools extension proxy context running in a main process.
+ * @param {object} options
+ * @param {string} options.id
+ *        The id of the addon devtools panel.
+ * @param {string} options.icon
+ *        The icon of the addon devtools panel.
+ * @param {string} options.title
+ *        The title of the addon devtools panel.
+ * @param {string} options.url
+ *        The url of the addon devtools panel, relative to the extension base URL.
+ */
+class ParentDevToolsPanel {
+  constructor(context, panelOptions) {
+    const toolbox = context.devToolsToolbox;
+    if (!toolbox) {
+      // This should never happen when this constructor is called with a valid
+      // devtools extension context.
+      throw Error("Missing mandatory toolbox");
+    }
+
+    this.extension = context.extension;
+    this.viewType = "devtools_panel";
+
+    this.visible = false;
+    this.toolbox = toolbox;
+
+    this.context = context;
+
+    this.panelOptions = panelOptions;
+
+    this.context.callOnClose(this);
+
+    this.id = this.panelOptions.id;
+
+    this.onToolboxPanelSelect = this.onToolboxPanelSelect.bind(this);
+    this.onToolboxReady = this.onToolboxReady.bind(this);
+
+    this.panelAdded = false;
+
+    if (this.toolbox.isReady) {
+      this.onToolboxReady();
+    } else {
+      this.toolbox.once("ready", this.onToolboxReady);
+    }
+
+    this.waitTopLevelContext = new Promise(resolve => {
+      this._resolveTopLevelContext = resolve;
+    });
+  }
+
+  addPanel() {
+    const {icon, title} = this.panelOptions;
+    const extensionName = this.context.extension.name;
+
+    this.toolbox.addAdditionalTool({
+      id: this.id,
+      url: "about:blank",
+      icon: icon,
+      label: title,
+      tooltip: `DevTools Panel added by "${extensionName}" add-on.`,
+      invertIconForLightTheme: true,
+      visibilityswitch:  `devtools.webext-${this.id}.enabled`,
+      isTargetSupported: target => target.isLocalTab,
+      build: (window, toolbox) => {
+        if (toolbox !== this.toolbox) {
+          throw new Error("Unexpected toolbox received on addAdditionalTool build property");
+        }
+
+        const destroy = this.buildPanel(window, toolbox);
+
+        return {toolbox, destroy};
+      },
+    });
+  }
+
+  buildPanel(window, toolbox) {
+    const {url} = this.panelOptions;
+    const {document} = window;
+
+    const browser = document.createElementNS(XUL_NS, "browser");
+    browser.setAttribute("type", "content");
+    browser.setAttribute("disableglobalhistory", "true");
+    browser.setAttribute("style", "width: 100%; height: 100%;");
+    browser.setAttribute("transparent", "true");
+    browser.setAttribute("class", "webextension-devtoolsPanel-browser");
+    browser.setAttribute("webextension-view-type", "devtools_panel");
+    browser.setAttribute("flex", "1");
+
+    this.browser = browser;
+
+    const {extension} = this.context;
+
+    let awaitFrameLoader = Promise.resolve();
+    if (extension.remote) {
+      browser.setAttribute("remote", "true");
+      browser.setAttribute("remoteType", E10SUtils.EXTENSION_REMOTE_TYPE);
+      awaitFrameLoader = promiseEvent(browser, "XULFrameLoaderCreated");
+    } else if (!AppConstants.RELEASE_OR_BETA) {
+      // NOTE: Using a content iframe here breaks the devtools panel
+      // switching between docked and undocked mode,
+      // because of a swapFrameLoader exception (see bug 1075490).
+      browser.setAttribute("type", "chrome");
+      browser.setAttribute("forcemessagemanager", true);
+    }
+
+    let hasTopLevelContext = false;
+
+    // Listening to new proxy contexts.
+    const unwatchExtensionProxyContextLoad = watchExtensionProxyContextLoad(this, context => {
+      // Keep track of the toolbox and target associated to the context, which is
+      // needed by the API methods implementation.
+      context.devToolsToolbox = toolbox;
+
+      if (!hasTopLevelContext) {
+        hasTopLevelContext = true;
+
+        // Resolve the promise when the root devtools_panel context has been created.
+        awaitFrameLoader.then(() => this._resolveTopLevelContext(context));
+      }
+    });
+
+    document.body.setAttribute("style", "margin: 0; padding: 0;");
+    document.body.appendChild(browser);
+
+    extensions.emit("extension-browser-inserted", browser, {
+      devtoolsToolboxInfo: {
+        toolboxPanelId: this.id,
+        inspectedWindowTabId: getTargetTabIdForToolbox(toolbox),
+      },
+    });
+
+    browser.loadURI(url);
+
+    toolbox.on("select", this.onToolboxPanelSelect);
+
+    // Return a cleanup method that is when the panel is destroyed, e.g.
+    // - when addon devtool panel has been disabled by the user from the toolbox preferences,
+    //   its ParentDevToolsPanel instance is still valid, but the built devtools panel is removed from
+    //   the toolbox (and re-built again if the user re-enable it from the toolbox preferences panel)
+    // - when the creator context has been destroyed, the ParentDevToolsPanel close method is called,
+    //   it remove the tool definition from the toolbox, which will call this destroy method.
+    return () => {
+      unwatchExtensionProxyContextLoad();
+      browser.remove();
+      toolbox.off("select", this.onToolboxPanelSelect);
+    };
+  }
+
+  onToolboxReady() {
+    if (!this.panelAdded) {
+      this.panelAdded = true;
+      this.addPanel();
+    }
+  }
+
+  onToolboxPanelSelect(what, id) {
+    if (!this.waitTopLevelContext || !this.panelAdded) {
+      return;
+    }
+
+    if (!this.visible && id === this.id) {
+      // Wait that the panel is fully loaded and emit show.
+      this.waitTopLevelContext.then(() => {
+        this.visible = true;
+        this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsPanelShown", {
+          toolboxPanelId: this.id,
+        });
+      });
+    } else if (this.visible && id !== this.id) {
+      this.visible = false;
+      this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsPanelHidden", {
+        toolboxPanelId: this.id,
+      });
+    }
+  }
+
+  close() {
+    const {toolbox} = this;
+
+    if (!toolbox) {
+      throw new Error("Unable to destroy a closed devtools panel");
+    }
+
+    toolbox.off("ready", this.onToolboxReady);
+
+    // Explicitly remove the panel if it is registered and the toolbox is not
+    // closing itself.
+    if (toolbox.isToolRegistered(this.id) && !toolbox._destroyer) {
+      toolbox.removeAdditionalTool(this.id);
+    }
+
+    this.context = null;
+    this.toolbox = null;
+  }
+}
+
+extensions.registerSchemaAPI("devtools.panels", "devtools_parent", context => {
+  // An incremental "per context" id used in the generated devtools panel id.
+  let nextPanelId = 0;
+
+  return {
+    devtools: {
+      panels: {
+        create(title, icon, url) {
+          // Get a fallback icon from the manifest data.
+          if (icon === "" && context.extension.manifest.icons) {
+            const iconInfo = IconDetails.getPreferredIcon(context.extension.manifest.icons,
+                                                          context.extension, 128);
+            icon = iconInfo ? iconInfo.icon : "";
+          }
+
+          icon = context.extension.baseURI.resolve(icon);
+          url = context.extension.baseURI.resolve(url);
+
+          const baseId = `${context.extension.id}-${context.contextId}-${nextPanelId++}`;
+          const id = `${makeWidgetId(baseId)}-devtools-panel`;
+
+          new ParentDevToolsPanel(context, {title, icon, url, id});
+
+          // Resolved to the devtools panel id into the child addon process,
+          // where it will be used to identify the messages related
+          // to the panel API onShown/onHidden events.
+          return Promise.resolve(id);
+        },
+      },
+    },
+  };
+});
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/ext-sidebarAction.js
@@ -108,22 +108,34 @@ class SidebarAction {
     // get the command id we pass to SidebarUI.
     broadcaster.setAttribute("oncommand", "SidebarUI.toggle(this.getAttribute('observes'))");
 
     let menuitem = document.createElementNS(XUL_NS, "menuitem");
     menuitem.setAttribute("id", this.menuId);
     menuitem.setAttribute("observes", this.id);
     menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
 
+    this.setMenuIcon(menuitem, details);
+
     document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
     document.getElementById("viewSidebarMenu").appendChild(menuitem);
 
     return menuitem;
   }
 
+  setMenuIcon(menuitem, details) {
+    let getIcon = size => IconDetails.escapeUrl(
+      IconDetails.getPreferredIcon(details.icon, this.extension, size).icon);
+
+    menuitem.setAttribute("style", `
+      --webextension-menuitem-image: url("${getIcon(16)}");
+      --webextension-menuitem-image-2x: url("${getIcon(32)}");
+    `);
+  }
+
   /**
    * Update the broadcaster and menuitem `node` with the tab context data
    * in `tabData`.
    *
    * @param {ChromeWindow} window
    *        Browser chrome window.
    * @param {object} tabData
    *        Tab specific sidebar configuration.
@@ -142,23 +154,17 @@ class SidebarAction {
     broadcaster.setAttribute("label", title);
 
     let url = this.sidebarUrl(tabData.panel);
     let urlChanged = url !== broadcaster.getAttribute("sidebarurl");
     if (urlChanged) {
       broadcaster.setAttribute("sidebarurl", url);
     }
 
-    let getIcon = size => IconDetails.escapeUrl(
-      IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
-
-    menu.setAttribute("style", `
-      --webextension-menuitem-image: url("${getIcon(16)}");
-      --webextension-menuitem-image-2x: url("${getIcon(32)}");
-    `);
+    this.setMenuIcon(menu, tabData);
 
     // Update the sidebar if this extension is the current sidebar.
     if (SidebarUI.currentID === this.id) {
       SidebarUI.title = title;
       if (SidebarUI.isOpen && urlChanged) {
         SidebarUI.show(this.id);
       }
     }
@@ -247,16 +253,29 @@ class SidebarAction {
       }
       let broadcaster = document.getElementById(this.id);
       if (broadcaster) {
         broadcaster.remove();
       }
     }
     windowTracker.removeOpenListener(this.windowOpenListener);
   }
+
+  /**
+   * Triggers this sidebar action for the given window, with the same effects as
+   * if it were toggled via menu or toolbarbutton by a user.
+   *
+   * @param {ChromeWindow} window
+   */
+  triggerAction(window) {
+    let {SidebarUI} = window;
+    if (SidebarUI) {
+      SidebarUI.toggle(this.id);
+    }
+  }
 }
 
 SidebarAction.for = (extension) => {
   return sidebarActionMap.get(extension);
 };
 
 global.sidebarActionFor = SidebarAction.for;
 
--- a/browser/components/extensions/ext-url-overrides.js
+++ b/browser/components/extensions/ext-url-overrides.js
@@ -3,47 +3,102 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
 
 // Bug 1320736 tracks creating a generic precedence manager for handling
 // multiple addons modifying the same properties, and bug 1330494 has been filed
 // to track utilizing this manager for chrome_url_overrides. Until those land,
 // the edge cases surrounding multiple addons using chrome_url_overrides will
 // be ignored and precedence will be first come, first serve.
 let overrides = {
   // A queue of extensions in line to override the newtab page (sorted oldest to newest).
   newtab: [],
+  // A queue of extensions in line to override the home page (sorted oldest to newest).
+  home: [],
 };
 
+/**
+ * Resets the specified page to its default value.
+ *
+ * @param {string} page The page to override. Accepted values are "newtab" and "home".
+ */
+function resetPage(page) {
+  switch (page) {
+    case "newtab":
+      aboutNewTabService.resetNewTabURL();
+      break;
+    case "home":
+      Preferences.reset("browser.startup.homepage");
+      break;
+    default:
+      throw new Error("Unrecognized override type");
+  }
+}
+
+/**
+ * Overrides the specified page to the specified URL.
+ *
+ * @param {string} page The page to override. Accepted values are "newtab" and "home".
+ * @param {string} url The resolved URL to use for the page override.
+ */
+function overridePage(page, url) {
+  switch (page) {
+    case "newtab":
+      aboutNewTabService.newTabURL = url;
+      break;
+    case "home":
+      Preferences.set("browser.startup.homepage", url);
+      break;
+    default:
+      throw new Error("Unrecognized override type");
+  }
+}
+
+/**
+ * Updates the page to the URL specified by the extension next in line. If no extensions
+ * are in line, the page is reset to its default value.
+ *
+ * @param {string} page The page to override.
+ */
+function updatePage(page) {
+  if (overrides[page].length) {
+    overridePage(page, overrides[page][0].url);
+  } else {
+    resetPage(page);
+  }
+}
+
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("manifest_chrome_url_overrides", (type, directive, extension, manifest) => {
-  if (manifest.chrome_url_overrides.newtab) {
-    let newtab = manifest.chrome_url_overrides.newtab;
-    let url = extension.baseURI.resolve(newtab);
+  if (Object.keys(overrides).length > 1) {
+    extension.manifestError("Extensions can override only one page.");
+  }
 
-    // Only set the newtab URL if no other extension is overriding it.
-    if (!overrides.newtab.length) {
-      aboutNewTabService.newTabURL = url;
+  for (let page of Object.keys(overrides)) {
+    if (manifest.chrome_url_overrides[page]) {
+      let relativeURL = manifest.chrome_url_overrides[page];
+      let url = extension.baseURI.resolve(relativeURL);
+      // Store the extension ID instead of a hard reference to the extension.
+      overrides[page].push({id: extension.id, url});
+      updatePage(page);
+      break;
     }
-
-    overrides.newtab.push({extension, url});
   }
 });
 
 extensions.on("shutdown", (type, extension) => {
-  let i = overrides.newtab.findIndex(o => o.extension === extension);
-  if (i !== -1) {
-    overrides.newtab.splice(i, 1);
-
-    if (overrides.newtab.length) {
-      aboutNewTabService.newTabURL = overrides.newtab[0].url;
-    } else {
-      aboutNewTabService.resetNewTabURL();
+  for (let page of Object.keys(overrides)) {
+    let i = overrides[page].findIndex(o => o.id === extension.id);
+    if (i !== -1) {
+      overrides[page].splice(i, 1);
+      updatePage(page);
     }
   }
 });
 /* eslint-enable mozilla/balanced-listeners */
--- a/browser/components/extensions/extensions-browser.manifest
+++ b/browser/components/extensions/extensions-browser.manifest
@@ -3,45 +3,48 @@ category webextension-scripts bookmarks 
 category webextension-scripts browserAction chrome://browser/content/ext-browserAction.js
 category webextension-scripts browsingData chrome://browser/content/ext-browsingData.js
 category webextension-scripts commands chrome://browser/content/ext-commands.js
 category webextension-scripts contextMenus chrome://browser/content/ext-contextMenus.js
 category webextension-scripts desktop-runtime chrome://browser/content/ext-desktop-runtime.js
 category webextension-scripts devtools chrome://browser/content/ext-devtools.js
 category webextension-scripts devtools-inspectedWindow chrome://browser/content/ext-devtools-inspectedWindow.js
 category webextension-scripts devtools-network chrome://browser/content/ext-devtools-network.js
+category webextension-scripts devtools-panels chrome://browser/content/ext-devtools-panels.js
 category webextension-scripts history chrome://browser/content/ext-history.js
 category webextension-scripts omnibox chrome://browser/content/ext-omnibox.js
 category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js
 category webextension-scripts sessions chrome://browser/content/ext-sessions.js
 category webextension-scripts sidebarAction chrome://browser/content/ext-sidebarAction.js
 category webextension-scripts tabs chrome://browser/content/ext-tabs.js
 category webextension-scripts theme chrome://browser/content/ext-theme.js
 category webextension-scripts url-overrides chrome://browser/content/ext-url-overrides.js
 category webextension-scripts utils chrome://browser/content/ext-utils.js
 category webextension-scripts windows chrome://browser/content/ext-windows.js
 
 # scripts specific for devtools extension contexts.
 category webextension-scripts-devtools devtools-inspectedWindow chrome://browser/content/ext-c-devtools-inspectedWindow.js
+category webextension-scripts-devtools devtools-panels chrome://browser/content/ext-c-devtools-panels.js
 
 # scripts that must run in the same process as addon code.
 category webextension-scripts-addon contextMenus chrome://browser/content/ext-c-contextMenus.js
 category webextension-scripts-addon omnibox chrome://browser/content/ext-c-omnibox.js
 category webextension-scripts-addon tabs chrome://browser/content/ext-c-tabs.js
 
 # schemas
 category webextension-schemas bookmarks chrome://browser/content/schemas/bookmarks.json
 category webextension-schemas browser_action chrome://browser/content/schemas/browser_action.json
 category webextension-schemas browsing_data chrome://browser/content/schemas/browsing_data.json
 category webextension-schemas commands chrome://browser/content/schemas/commands.json
 category webextension-schemas context_menus chrome://browser/content/schemas/context_menus.json
 category webextension-schemas context_menus_internal chrome://browser/content/schemas/context_menus_internal.json
 category webextension-schemas devtools chrome://browser/content/schemas/devtools.json
 category webextension-schemas devtools_inspected_window chrome://browser/content/schemas/devtools_inspected_window.json
 category webextension-schemas devtools_network chrome://browser/content/schemas/devtools_network.json
+category webextension-schemas devtools_panels chrome://browser/content/schemas/devtools_panels.json
 category webextension-schemas history chrome://browser/content/schemas/history.json
 category webextension-schemas omnibox chrome://browser/content/schemas/omnibox.json
 category webextension-schemas page_action chrome://browser/content/schemas/page_action.json
 category webextension-schemas sessions chrome://browser/content/schemas/sessions.json
 category webextension-schemas sidebar_action chrome://browser/content/schemas/sidebar_action.json
 category webextension-schemas tabs chrome://browser/content/schemas/tabs.json
 category webextension-schemas theme chrome://browser/content/schemas/theme.json
 category webextension-schemas url_overrides chrome://browser/content/schemas/url_overrides.json
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -16,22 +16,24 @@ browser.jar:
     content/browser/ext-browserAction.js
     content/browser/ext-browsingData.js
     content/browser/ext-commands.js
     content/browser/ext-contextMenus.js
     content/browser/ext-desktop-runtime.js
     content/browser/ext-devtools.js
     content/browser/ext-devtools-inspectedWindow.js
     content/browser/ext-devtools-network.js
+    content/browser/ext-devtools-panels.js
     content/browser/ext-history.js
     content/browser/ext-omnibox.js
     content/browser/ext-pageAction.js
     content/browser/ext-sessions.js
     content/browser/ext-sidebarAction.js
     content/browser/ext-tabs.js
     content/browser/ext-theme.js
     content/browser/ext-url-overrides.js
     content/browser/ext-utils.js
     content/browser/ext-windows.js
     content/browser/ext-c-contextMenus.js
     content/browser/ext-c-devtools-inspectedWindow.js
+    content/browser/ext-c-devtools-panels.js
     content/browser/ext-c-omnibox.js
     content/browser/ext-c-tabs.js
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/devtools_panels.json
@@ -0,0 +1,407 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+  {
+    "namespace": "devtools.panels",
+    "allowedContexts": ["devtools", "devtools_only"],
+    "defaultContexts": ["devtools", "devtools_only"],
+    "description": "Use the <code>chrome.devtools.panels</code> API to integrate your extension into Developer Tools window UI: create your own panels, access existing panels, and add sidebars.",
+    "nocompile": true,
+    "types": [
+      {
+        "id": "ElementsPanel",
+        "type": "object",
+        "description": "Represents the Elements panel.",
+        "events": [
+          {
+            "name": "onSelectionChanged",
+            "unsupported": true,
+            "description": "Fired when an object is selected in the panel."
+          }
+        ],
+        "functions": [
+          {
+            "name": "createSidebarPane",
+            "unsupported": true,
+            "type": "function",
+            "description": "Creates a pane within panel's sidebar.",
+            "parameters": [
+              {
+                "name": "title",
+                "type": "string",
+                "description": "Text that is displayed in sidebar caption."
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "A callback invoked when the sidebar is created.",
+                "optional": true,
+                "parameters": [
+                  {
+                    "name": "result",
+                    "description": "An ExtensionSidebarPane object for created sidebar pane.",
+                    "$ref": "ExtensionSidebarPane"
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "id": "SourcesPanel",
+        "type": "object",
+        "description": "Represents the Sources panel.",
+        "events": [
+          {
+            "name": "onSelectionChanged",
+            "unsupported": true,
+            "description": "Fired when an object is selected in the panel."
+          }
+        ],
+        "functions": [
+          {
+            "name": "createSidebarPane",
+            "unsupported": true,
+            "type": "function",
+            "description": "Creates a pane within panel's sidebar.",
+            "parameters": [
+              {
+                "name": "title",
+                "type": "string",
+                "description": "Text that is displayed in sidebar caption."
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "A callback invoked when the sidebar is created.",
+                "optional": true,
+                "parameters": [
+                  {
+                    "name": "result",
+                    "description": "An ExtensionSidebarPane object for created sidebar pane.",
+                    "$ref": "ExtensionSidebarPane"
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "id": "ExtensionPanel",
+        "type": "object",
+        "description": "Represents a panel created by extension.",
+        "functions": [
+          {
+            "name": "createStatusBarButton",
+            "unsupported": true,
+            "description": "Appends a button to the status bar of the panel.",
+            "type": "function",
+            "parameters": [
+              {
+                "name": "iconPath",
+                "type": "string",
+                "description": "Path to the icon of the button. The file should contain a 64x24-pixel image composed of two 32x24 icons. The left icon is used when the button is inactive; the right icon is displayed when the button is pressed."
+              },
+              {
+                "name": "tooltipText",
+                "type": "string",
+                "description": "Text shown as a tooltip when user hovers the mouse over the button."
+              },
+              {
+                "name": "disabled",
+                "type": "boolean",
+                "description": "Whether the button is disabled."
+              }
+            ],
+            "returns": { "$ref": "Button" }
+          }
+        ],
+        "events": [
+          {
+            "name": "onSearch",
+            "unsupported": true,
+            "description": "Fired upon a search action (start of a new search, search result navigation, or search being canceled).",
+            "parameters": [
+              {
+                "name": "action",
+                "type": "string",
+                "description": "Type of search action being performed."
+              },
+              {
+                "name": "queryString",
+                "type": "string",
+                "optional": true,
+                "description": "Query string (only for 'performSearch')."
+              }
+            ]
+          },
+          {
+            "name": "onShown",
+            "type": "function",
+            "description": "Fired when the user switches to the panel.",
+            "parameters": [
+              {
+                "name": "window",
+                "type": "object",
+                "isInstanceOf": "global",
+                "additionalProperties": { "type": "any" },
+                "description": "The JavaScript <code>window</code> object of panel's page."
+              }
+            ]
+          },
+          {
+            "name": "onHidden",
+            "type": "function",
+            "description": "Fired when the user switches away from the panel."
+          }
+        ]
+      },
+      {
+        "id": "ExtensionSidebarPane",
+        "type": "object",
+        "description": "A sidebar created by the extension.",
+        "functions": [
+          {
+            "name": "setHeight",
+            "unsupported": true,
+            "type": "function",
+            "description": "Sets the height of the sidebar.",
+            "parameters": [
+              {
+                "name": "height",
+                "type": "string",
+                "description": "A CSS-like size specification, such as <code>'100px'</code> or <code>'12ex'</code>."
+              }
+            ]
+          },
+          {
+            "name": "setExpression",
+            "unsupported": true,
+            "type": "function",
+            "description": "Sets an expression that is evaluated within the inspected page. The result is displayed in the sidebar pane.",
+            "parameters": [
+              {
+                "name": "expression",
+                "type": "string",
+                "description": "An expression to be evaluated in context of the inspected page. JavaScript objects and DOM nodes are displayed in an expandable tree similar to the console/watch."
+              },
+              {
+                "name": "rootTitle",
+                "type": "string",
+                "optional": true,
+                "description": "An optional title for the root of the expression tree."
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "optional": true,
+                "description": "A callback invoked after the sidebar pane is updated with the expression evaluation results."
+              }
+            ]
+          },
+          {
+            "name": "setObject",
+            "unsupported": true,
+            "type": "function",
+            "description": "Sets a JSON-compliant object to be displayed in the sidebar pane.",
+            "parameters": [
+              {
+                "name": "jsonObject",
+                "type": "string",
+                "description": "An object to be displayed in context of the inspected page. Evaluated in the context of the caller (API client)."
+              },
+              {
+                "name": "rootTitle",
+                "type": "string",
+                "optional": true,
+                "description": "An optional title for the root of the expression tree."
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "optional": true,
+                "description": "A callback invoked after the sidebar is updated with the object."
+              }
+            ]
+          },
+          {
+            "name": "setPage",
+            "unsupported": true,
+            "type": "function",
+            "description": "Sets an HTML page to be displayed in the sidebar pane.",
+            "parameters": [
+              {
+                "name": "path",
+                "type": "string",
+                "description": "Relative path of an extension page to display within the sidebar."
+              }
+            ]
+          }
+        ],
+        "events": [
+          {
+            "name": "onShown",
+            "unsupported": true,
+            "type": "function",
+            "description": "Fired when the sidebar pane becomes visible as a result of user switching to the panel that hosts it.",
+            "parameters": [
+              {
+                "name": "window",
+                "type": "object",
+                "isInstanceOf": "global",
+                "additionalProperties": { "type": "any" },
+                "description": "The JavaScript <code>window</code> object of the sidebar page, if one was set with the <code>setPage()</code> method."
+              }
+            ]
+          },
+          {
+            "name": "onHidden",
+            "unsupported": true,
+            "type": "function",
+            "description": "Fired when the sidebar pane becomes hidden as a result of the user switching away from the panel that hosts the sidebar pane."
+          }
+        ]
+      },
+      {
+        "id": "Button",
+        "type": "object",
+        "description": "A button created by the extension.",
+        "functions": [
+          {
+            "name": "update",
+            "unsupported": true,
+            "type": "function",
+            "description": "Updates the attributes of the button. If some of the arguments are omitted or <code>null</code>, the corresponding attributes are not updated.",
+            "parameters": [
+              {
+                "name": "iconPath",
+                "type": "string",
+                "optional": true,
+                "description": "Path to the new icon of the button."
+              },
+              {
+                "name": "tooltipText",
+                "type": "string",
+                "optional": true,
+                "description": "Text shown as a tooltip when user hovers the mouse over the button."
+              },
+              {
+                "name": "disabled",
+                "type": "boolean",
+                "optional": true,
+                "description": "Whether the button is disabled."
+              }
+            ]
+          }
+        ],
+        "events": [
+          {
+            "name": "onClicked",
+            "unsupported": true,
+            "type": "function",
+            "description": "Fired when the button is clicked."
+          }
+        ]
+      }
+    ],
+    "properties": {
+      "elements": {
+        "$ref": "ElementsPanel",
+        "description": "Elements panel."
+      },
+      "sources": {
+        "$ref": "SourcesPanel",
+        "description": "Sources panel."
+      }
+    },
+    "functions": [
+      {
+        "name": "create",
+        "type": "function",
+        "description": "Creates an extension panel.",
+        "async": "callback",
+        "parameters": [
+          {
+            "name": "title",
+            "type": "string",
+            "description": "Title that is displayed next to the extension icon in the Developer Tools toolbar."
+          },
+          {
+            "name": "iconPath",
+            "type": "string",
+            "description": "Path of the panel's icon relative to the extension directory."
+          },
+          {
+            "name": "pagePath",
+            "type": "string",
+            "description": "Path of the panel's HTML page relative to the extension directory."
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "optional": true,
+            "description": "A function that is called when the panel is created.",
+            "parameters": [
+              {
+                "name": "panel",
+                "description": "An ExtensionPanel object representing the created panel.",
+                "$ref": "ExtensionPanel"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "setOpenResourceHandler",
+        "unsupported": true,
+        "type": "function",
+        "description": "Specifies the function to be called when the user clicks a resource link in the Developer Tools window. To unset the handler, either call the method with no parameters or pass null as the parameter.",
+        "async": "callback",
+        "parameters": [
+          {
+            "name": "callback",
+            "type": "function",
+            "optional": true,
+            "description": "A function that is called when the user clicks on a valid resource link in Developer Tools window. Note that if the user clicks an invalid URL or an XHR, this function is not called.",
+            "parameters": [
+              {
+                "name": "resource",
+                "$ref": "devtools.inspectedWindow.Resource",
+                "description": "A $(ref:devtools.inspectedWindow.Resource) object for the resource that was clicked."
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "openResource",
+        "unsupported": true,
+        "type": "function",
+        "description": "Requests DevTools to open a URL in a Developer Tools panel.",
+        "async": "callback",
+        "parameters": [
+          {
+            "name": "url",
+            "type": "string",
+            "description": "The URL of the resource to open."
+          },
+          {
+            "name": "lineNumber",
+            "type": "integer",
+            "description": "Specifies the line number to scroll to when the resource is loaded."
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "optional": true,
+            "description": "A function that is called when the resource has been successfully loaded."
+          }
+        ]
+      }
+    ]
+  }
+]
--- a/browser/components/extensions/schemas/jar.mn
+++ b/browser/components/extensions/schemas/jar.mn
@@ -7,16 +7,17 @@ browser.jar:
     content/browser/schemas/browser_action.json
     content/browser/schemas/browsing_data.json
     content/browser/schemas/commands.json
     content/browser/schemas/context_menus.json
     content/browser/schemas/context_menus_internal.json
     content/browser/schemas/devtools.json
     content/browser/schemas/devtools_inspected_window.json
     content/browser/schemas/devtools_network.json
+    content/browser/schemas/devtools_panels.json
     content/browser/schemas/history.json
     content/browser/schemas/omnibox.json
     content/browser/schemas/page_action.json
     content/browser/schemas/sessions.json
     content/browser/schemas/sidebar_action.json
     content/browser/schemas/tabs.json
     content/browser/schemas/theme.json
     content/browser/schemas/url_overrides.json
--- a/browser/components/extensions/schemas/url_overrides.json
+++ b/browser/components/extensions/schemas/url_overrides.json
@@ -9,16 +9,21 @@
             "type": "object",
             "optional": true,
             "properties": {
               "newtab": {
                 "$ref": "ExtensionURL",
                 "optional": true,
                 "preprocess": "localize"
               },
+              "home": {
+                "$ref": "ExtensionURL",
+                "optional": true,
+                "preprocess": "localize"
+              },
               "bookmarks": {
                 "unsupported": true,
                 "$ref": "ExtensionURL",
                 "optional": true,
                 "preprocess": "localize"
               },
               "history": {
                 "unsupported": true,
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -33,32 +33,34 @@ support-files =
 [browser_ext_browserAction_popup_resize.js]
 [browser_ext_browserAction_simple.js]
 [browser_ext_browsingData_formData.js]
 [browser_ext_browsingData_history.js]
 [browser_ext_browsingData_pluginData.js]
 [browser_ext_browsingData_serviceWorkers.js]
 [browser_ext_commands_execute_browser_action.js]
 [browser_ext_commands_execute_page_action.js]
+[browser_ext_commands_execute_sidebar_action.js]
 [browser_ext_commands_getAll.js]
 [browser_ext_commands_onCommand.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_contextMenus.js]
 [browser_ext_contextMenus_checkboxes.js]
 [browser_ext_contextMenus_chrome.js]
 [browser_ext_contextMenus_icons.js]
 [browser_ext_contextMenus_onclick.js]
 [browser_ext_contextMenus_radioGroups.js]
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
 [browser_ext_currentWindow.js]
 [browser_ext_devtools_inspectedWindow.js]
 [browser_ext_devtools_inspectedWindow_reload.js]
 [browser_ext_devtools_network.js]
 [browser_ext_devtools_page.js]
+[browser_ext_devtools_panel.js]
 [browser_ext_getViews.js]
 [browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_omnibox.js]
 [browser_ext_optionsPage_privileges.js]
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_popup.js]
@@ -109,17 +111,19 @@ support-files =
 [browser_ext_tabs_cookieStoreId.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_themes_chromeparity.js]
 [browser_ext_themes_dynamic_updates.js]
 [browser_ext_themes_lwtsupport.js]
 [browser_ext_topwindowid.js]
-[browser_ext_url_overrides.js]
+[browser_ext_url_overrides_all.js]
+[browser_ext_url_overrides_home.js]
+[browser_ext_url_overrides_newtab.js]
 [browser_ext_webRequest.js]
 [browser_ext_webNavigation_frameId0.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_webNavigation_urlbar_transitions.js]
 [browser_ext_windows.js]
 [browser_ext_windows_create.js]
 tags = fullscreen
 [browser_ext_windows_create_params.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_commands_execute_sidebar_action.js
@@ -0,0 +1,52 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* test_execute_sidebar_action() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "commands": {
+        "_execute_sidebar_action": {
+          "suggested_key": {
+            "default": "Alt+Shift+J",
+          },
+        },
+      },
+      "sidebar_action": {
+        "default_panel": "sidebar.html",
+      },
+    },
+    files: {
+      "sidebar.html": `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="utf-8">
+            <script src="sidebar.js"></script>
+          </head>
+        </html>
+      `,
+
+      "sidebar.js": function() {
+        browser.runtime.sendMessage("from-sidebar-action");
+      },
+    },
+    background() {
+      browser.runtime.onMessage.addListener(msg => {
+        if (msg == "from-sidebar-action") {
+          browser.test.notifyPass("execute-sidebar-action-opened");
+        }
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield SimpleTest.promiseFocus(window);
+  // Since we didn't set useAddonManager, the sidebar will not be automatically
+  // opened for this test.
+  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
+  // Send the key to open the sidebar.
+  EventUtils.synthesizeKey("j", {altKey: true, shiftKey: true});
+  yield extension.awaitFinish("execute-sidebar-action-opened");
+  yield extension.unload();
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
@@ -0,0 +1,145 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "devtools",
+                                  "resource://devtools/shared/Loader.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
+                                  "resource://devtools/client/framework/gDevTools.jsm");
+
+/**
+ * This test file ensures that:
+ *
+ * - ensures that devtools.panel.create is able to create a devtools panel
+ */
+
+add_task(function* test_devtools_page_panels_create() {
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+
+  async function devtools_page() {
+    const result = {
+      devtoolsPageTabId: browser.devtools.inspectedWindow.tabId,
+      panelCreated: 0,
+      panelShown: 0,
+      panelHidden: 0,
+    };
+
+    try {
+      const panel = await browser.devtools.panels.create(
+        "Test Panel", "fake-icon.png", "devtools_panel.html"
+      );
+
+      result.panelCreated++;
+
+      panel.onShown.addListener(contentWindow => {
+        result.panelShown++;
+        browser.test.assertEq("complete", contentWindow.document.readyState,
+                              "Got the expected 'complete' panel document readyState");
+        browser.test.assertEq("test_panel_global", contentWindow.TEST_PANEL_GLOBAL,
+                              "Got the expected global in the panel contentWindow");
+        browser.test.sendMessage("devtools_panel_shown", result);
+      });
+
+      panel.onHidden.addListener(() => {
+        result.panelHidden++;
+
+        browser.test.sendMessage("devtools_panel_hidden", result);
+      });
+
+      browser.test.sendMessage("devtools_panel_created");
+    } catch (err) {
+      // Make the test able to fail fast when it is going to be a failure.
+      browser.test.sendMessage("devtools_panel_created");
+      throw err;
+    }
+  }
+
+  function devtools_panel() {
+    // Set a property in the global and check that it is defined
+    // and accessible from the devtools_page when the panel.onShown
+    // event has been received.
+    window.TEST_PANEL_GLOBAL = "test_panel_global";
+    browser.test.sendMessage("devtools_panel_inspectedWindow_tabId",
+                             browser.devtools.inspectedWindow.tabId);
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      devtools_page: "devtools_page.html",
+    },
+    files: {
+      "devtools_page.html": `<!DOCTYPE html>
+      <html>
+       <head>
+         <meta charset="utf-8">
+       </head>
+       <body>
+         <script src="devtools_page.js"></script>
+       </body>
+      </html>`,
+      "devtools_page.js": devtools_page,
+      "devtools_panel.html":  `<!DOCTYPE html>
+      <html>
+       <head>
+         <meta charset="utf-8">
+       </head>
+       <body>
+         DEVTOOLS PANEL
+         <script src="devtools_panel.js"></script>
+       </body>
+      </html>`,
+      "devtools_panel.js": devtools_panel,
+    },
+  });
+
+  yield extension.startup();
+
+  let target = devtools.TargetFactory.forTab(tab);
+
+  const toolbox = yield gDevTools.showToolbox(target, "webconsole");
+  info("developer toolbox opened");
+
+  yield extension.awaitMessage("devtools_panel_created");
+
+  const toolboxAdditionalTools = toolbox.getAdditionalTools();
+
+  is(toolboxAdditionalTools.length, 1,
+     "Got the expected number of toolbox specific panel registered.");
+
+  const panelId = toolboxAdditionalTools[0].id;
+
+  yield gDevTools.showToolbox(target, panelId);
+  const {devtoolsPageTabId} = yield extension.awaitMessage("devtools_panel_shown");
+  const devtoolsPanelTabId = yield extension.awaitMessage("devtools_panel_inspectedWindow_tabId");
+  is(devtoolsPanelTabId, devtoolsPageTabId,
+     "Got the same devtools.inspectedWindow.tabId from devtools page and panel");
+  info("Addon Devtools Panel shown");
+
+  yield gDevTools.showToolbox(target, "webconsole");
+  const results = yield extension.awaitMessage("devtools_panel_hidden");
+  info("Addon Devtools Panel hidden");
+
+  is(results.panelCreated, 1, "devtools.panel.create callback has been called once");
+  is(results.panelShown, 1, "panel.onShown listener has been called once");
+  is(results.panelHidden, 1, "panel.onHidden listener has been called once");
+
+  yield gDevTools.showToolbox(target, panelId);
+  yield extension.awaitMessage("devtools_panel_shown");
+  info("Addon Devtools Panel shown - second cycle");
+
+  yield gDevTools.showToolbox(target, "webconsole");
+  const secondCycleResults = yield extension.awaitMessage("devtools_panel_hidden");
+  info("Addon Devtools Panel hidden - second cycle");
+
+  is(secondCycleResults.panelCreated, 1, "devtools.panel.create callback has been called once");
+  is(secondCycleResults.panelShown, 2, "panel.onShown listener has been called twice");
+  is(secondCycleResults.panelHidden, 2, "panel.onHidden listener has been called twice");
+
+  yield gDevTools.closeToolbox(target);
+
+  yield target.destroy();
+
+  yield extension.unload();
+
+  yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
@@ -86,25 +86,35 @@ add_task(function* sidebar_two_sidebar_a
 });
 
 add_task(function* sidebar_windows() {
   let extension = ExtensionTestUtils.loadExtension(extData);
   yield extension.startup();
   // Test sidebar is opened on install
   yield extension.awaitMessage("sidebar");
   ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
+  // Check that the menuitem has our image styling.
+  let elements = document.getElementsByClassName("webextension-menuitem");
+  is(elements.length, 1, "have one menuitem");
+  let style = elements[0].getAttribute("style");
+  ok(style.includes("webextension-menuitem-image"), "this menu has style");
 
   let secondSidebar = extension.awaitMessage("sidebar");
 
   // SidebarUI relies on window.opener being set, which is normal behavior when
   // using menu or key commands to open a new browser window.
   let win = yield BrowserTestUtils.openNewBrowserWindow({opener: window});
 
   yield secondSidebar;
   ok(!win.document.getElementById("sidebar-box").hidden, "sidebar box is visible in second window");
+  // Check that the menuitem has our image styling.
+  elements = win.document.getElementsByClassName("webextension-menuitem");
+  is(elements.length, 1, "have one menuitem");
+  style = elements[0].getAttribute("style");
+  ok(style.includes("webextension-menuitem-image"), "this menu has style");
 
   yield extension.unload();
   yield BrowserTestUtils.closeWindow(win);
 });
 
 add_task(function* sidebar_empty_panel() {
   let extension = ExtensionTestUtils.loadExtension(extData);
   yield extension.startup();
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_all.js
@@ -0,0 +1,97 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
+                                   "@mozilla.org/browser/aboutnewtab-service;1",
+                                   "nsIAboutNewTabService");
+
+const NEWTAB_URI = "webext-newtab.html";
+const HOME_URI = "webext-home.html";
+
+add_task(function* test_extensions_overriding_different_pages() {
+  let defaultHomePage = Preferences.get("browser.startup.homepage");
+  let defaultNewtabPage = aboutNewTabService.newTabURL;
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Default newtab url should be ${defaultNewtabPage}`);
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {}},
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {newtab: NEWTAB_URI}},
+  });
+
+  let ext3 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI}},
+  });
+
+  yield ext1.startup();
+
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Default newtab url should still be ${defaultNewtabPage}`);
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+
+  yield ext2.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
+    "Newtab url should be overriden by the second extension.");
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+
+  yield ext1.unload();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
+    "Newtab url should still be overriden by the second extension.");
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Default home url should be ${defaultHomePage}`);
+
+  yield ext3.startup();
+
+  ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
+    "Newtab url should still be overriden by the second extension.");
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI),
+    "Home url should be overriden by the third extension.");
+
+  yield ext2.unload();
+
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Newtab url should be reset to ${defaultNewtabPage}`);
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI),
+    "Home url should still be overriden by the third extension.");
+
+  yield ext3.unload();
+
+  is(aboutNewTabService.newTabURL, defaultNewtabPage,
+    `Newtab url should be reset to ${defaultNewtabPage}`);
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+    `Home url should be reset to ${defaultHomePage}`);
+});
+
+add_task(function* test_extensions_with_multiple_overrides() {
+  let ext = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {
+      newtab: NEWTAB_URI,
+      home: HOME_URI,
+    }},
+  });
+
+  SimpleTest.waitForExplicitFinish();
+  let waitForConsole = new Promise(resolve => {
+    SimpleTest.monitorConsole(resolve, [{
+      message: /Extensions can override only one page./,
+    }]);
+  });
+
+  yield ext.startup();
+  yield ext.unload();
+
+  SimpleTest.endMonitorConsole();
+  yield waitForConsole;
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_home.js
@@ -0,0 +1,74 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
+
+const HOME_URI_1 = "webext-home-1.html";
+const HOME_URI_2 = "webext-home-2.html";
+const HOME_URI_3 = "webext-home-3.html";
+
+add_task(function* test_multiple_extensions_overriding_newtab_page() {
+  let defaultHomePage = Preferences.get("browser.startup.homepage");
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+     `Default home url should be ${defaultHomePage}`);
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {}},
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI_1}},
+  });
+
+  let ext3 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI_2}},
+  });
+
+  let ext4 = ExtensionTestUtils.loadExtension({
+    manifest: {"chrome_url_overrides": {home: HOME_URI_3}},
+  });
+
+  yield ext1.startup();
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+       `Default home url should still be ${defaultHomePage}`);
+
+  yield ext2.startup();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_1),
+     "Home url should be overriden by the second extension.");
+
+  yield ext1.unload();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_1),
+     "Home url should still be overriden by the second extension.");
+
+  yield ext3.startup();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_1),
+     "Home url should still be overriden by the second extension.");
+
+  yield ext2.unload();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
+     "Home url should be overriden by the third extension.");
+
+  yield ext4.startup();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
+     "Home url should be overriden by the third extension.");
+
+  yield ext4.unload();
+
+  ok(Preferences.get("browser.startup.homepage").endsWith(HOME_URI_2),
+     "Home url should be overriden by the third extension.");
+
+  yield ext3.unload();
+
+  is(Preferences.get("browser.startup.homepage"), defaultHomePage,
+     `Home url should be reset to ${defaultHomePage}`);
+});
rename from browser/components/extensions/test/browser/browser_ext_url_overrides.js
rename to browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
--- a/browser/components/extensions/test/browser/browser_ext_url_overrides.js
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
@@ -71,21 +71,21 @@ add_task(function* test_multiple_extensi
   is(aboutNewTabService.newTabURL, "about:newtab",
      "Newtab url should be reset to about:newtab");
 });
 
 add_task(function* test_sending_message_from_newtab_page() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "chrome_url_overrides": {
-        newtab: NEWTAB_URI_2,
+        newtab: NEWTAB_URI_1,
       },
     },
     files: {
-      [NEWTAB_URI_2]: `
+      [NEWTAB_URI_1]: `
         <!DOCTYPE html>
         <head>
           <meta charset="utf-8"/></head>
         <html>
           <body>
             <script src="newtab.js"></script>
           </body>
         </html>
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -64,16 +64,28 @@ function getDataFolder(subfoldersWin, su
  * @note    Google Chrome uses FILETIME / 10 as time.
  *          FILETIME is based on same structure of Windows.
  */
 function chromeTimeToDate(aTime) {
   return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970) / 10000);
 }
 
 /**
+ * Convert Date object to Chrome time format
+ *
+ * @param   aDate
+ *          Date object or integer equivalent
+ * @return  Chrome time
+ * @note    For details on Chrome time, see chromeTimeToDate.
+ */
+function dateToChromeTime(aDate) {
+  return (aDate * 10000 + S100NS_FROM1601TO1970) / S100NS_PER_MS;
+}
+
+/**
  * Insert bookmark items into specific folder.
  *
  * @param   parentGuid
  *          GUID of the folder where items will be inserted
  * @param   items
  *          bookmark items to be inserted
  * @param   errorAccumulator
  *          function that gets called with any errors thrown so we don't drop them on the floor.
@@ -305,18 +317,30 @@ function GetHistoryResource(aProfileFold
   if (!historyFile.exists())
     return null;
 
   return {
     type: MigrationUtils.resourceTypes.HISTORY,
 
     migrate(aCallback) {
       Task.spawn(function* () {
-        let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(historyFile.path, "Chrome history",
-          `SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0`);
+        const MAX_AGE_IN_DAYS = Services.prefs.getIntPref("browser.migrate.chrome.history.maxAgeInDays");
+        const LIMIT = Services.prefs.getIntPref("browser.migrate.chrome.history.limit");
+
+        let query = "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0";
+        if (MAX_AGE_IN_DAYS) {
+          let maxAge = dateToChromeTime(Date.now() - MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000);
+          query += " AND last_visit_time > " + maxAge;
+        }
+        if (LIMIT) {
+          query += " ORDER BY last_visit_time DESC LIMIT " + LIMIT;
+        }
+
+        let rows =
+          yield MigrationUtils.getRowsFromDBWithoutLocks(historyFile.path, "Chrome history", query);
         let places = [];
         for (let row of rows) {
           try {
             // if having typed_count, we changes transition type to typed.
             let transType = PlacesUtils.history.TRANSITION_LINK;
             if (row.getResultByName("typed_count") > 0)
               transType = PlacesUtils.history.TRANSITION_TYPED;
 
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -410,16 +410,17 @@ class TestFirefoxRefresh(MarionetteTestC
           let dirName = bundle.formatStringFromName("resetBackupDirectory", [Services.appinfo.name], 1);
           container.append(dirName);
           container.append(arguments[0]);
           return container.path;
         """, script_args = [profileLeafName])
 
         self.assertTrue(os.path.isdir(self.reset_profile_path), "Reset profile path should be present")
         self.assertTrue(os.path.isdir(self.desktop_backup_path), "Backup profile path should be present")
+        self.assertTrue(self.profileNameToRemove in self.reset_profile_path, "Reset profile path should contain profile name to remove")
 
     def testReset(self):
         self.checkProfile()
 
         self.doReset()
 
         # Now check that we're doing OK...
         self.checkProfile(hasMigrated=True)
--- a/browser/components/originattributes/test/browser/worker_deblobify.js
+++ b/browser/components/originattributes/test/browser/worker_deblobify.js
@@ -1,12 +1,14 @@
 // Wait for a blob URL to be posted to this worker.
 // Obtain the blob, and read the string contained in it.
 // Post back the string.
 
+/* eslint-env worker */
+
 var postStringInBlob = function(blobObject) {
   var fileReader = new FileReaderSync();
   var result = fileReader.readAsText(blobObject);
   postMessage(result);
 };
 
 self.addEventListener("message", function(message) {
   if ("error" in message.data) {
--- a/browser/components/preferences/SiteDataManager.jsm
+++ b/browser/components/preferences/SiteDataManager.jsm
@@ -245,11 +245,12 @@ this.SiteDataManager = {
     Services.cache2.clear();
     Services.cookies.removeAll();
     OfflineAppCacheHelper.clear();
     this.updateSites();
   },
 
   isPrivateCookie(cookie) {
     let { userContextId } = cookie.originAttributes;
-    return userContextId && !ContextualIdentityService.getIdentityFromId(userContextId).public;
+    // A private cookie is when its userContextId points to a private identity.
+    return userContextId && !ContextualIdentityService.getPublicIdentityFromId(userContextId);
   }
 };
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -872,19 +872,25 @@ var gCookiesWindow = {
       if (item && item.container && item.open)
         this._openIndices.push(i);
     }
   },
 
   _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() {
     let removeAllCookies = document.getElementById("removeAllCookies");
     removeAllCookies.disabled = this._view._rowCount == 0;
-    let label = this._view._filtered ?
-      this._bundle.getString("removeAllShownCookies.label") : this._bundle.getString("removeAllCookies.label");
-    removeAllCookies.setAttribute("label", label);
+
+    let labelStringID = "removeAllCookies.label";
+    let accessKeyStringID = "removeAllCookies.accesskey";
+    if (this._view._filtered) {
+      labelStringID = "removeAllShownCookies.label";
+      accessKeyStringID = "removeAllShownCookies.accesskey";
+    }
+    removeAllCookies.setAttribute("label", this._bundle.getString(labelStringID));
+    removeAllCookies.setAttribute("accesskey", this._bundle.getString(accessKeyStringID));
   },
 
   filter() {
     var filter = document.getElementById("filter").value;
     if (filter == "") {
       gCookiesWindow.clearFilter();
       return;
     }
--- a/browser/components/preferences/in-content/containers.js
+++ b/browser/components/preferences/in-content/containers.js
@@ -18,17 +18,17 @@ let gContainersPane = {
     document.getElementById("backContainersLink").addEventListener("click", function() {
       gotoPref("privacy");
     });
 
     this._rebuildView();
   },
 
   _rebuildView() {
-    const containers = ContextualIdentityService.getIdentities();
+    const containers = ContextualIdentityService.getPublicIdentities();
     while (this._list.firstChild) {
       this._list.firstChild.remove();
     }
     for (let container of containers) {
       let item = document.createElement("richlistitem");
       item.setAttribute("containerName", ContextualIdentityService.getUserContextLabel(container.userContextId));
       item.setAttribute("containerIcon", container.icon);
       item.setAttribute("containerColor", container.color);
@@ -78,17 +78,17 @@ let gContainersPane = {
   openPreferenceDialog(userContextId) {
     let identity = {
       name: "",
       icon: defaultContainerIcon,
       color: defaultContainerColor
     };
     let title;
     if (userContextId) {
-      identity = ContextualIdentityService.getIdentityFromId(userContextId);
+      identity = ContextualIdentityService.getPublicIdentityFromId(userContextId);
       // This is required to get the translation string from defaults
       identity.name = ContextualIdentityService.getUserContextLabel(identity.userContextId);
       title = containersBundle.formatStringFromName("containers.updateContainerTitle", [identity.name], 1);
     }
 
     const params = { userContextId, identity, windowTitle: title };
     gSubDialog.open("chrome://browser/content/preferences/containers.xul",
                      null, params);
--- a/browser/components/preferences/in-content/tests/browser_advanced_siteData.js
+++ b/browser/components/preferences/in-content/tests/browser_advanced_siteData.js
@@ -131,30 +131,44 @@ function getPersistentStoragePermStatus(
 function getQuotaUsage(origin) {
   return new Promise(resolve => {
     let uri = NetUtil.newURI(origin);
     let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
     Services.qms.getUsageForPrincipal(principal, request => resolve(request.usage));
   });
 }
 
-function getCacheUsage() {
-  return new Promise(resolve => {
-    let obs = {
-      onNetworkCacheDiskConsumption(usage) {
-        resolve(usage);
-      },
-      QueryInterface: XPCOMUtils.generateQI([
-        Components.interfaces.nsICacheStorageConsumptionObserver,
-        Components.interfaces.nsISupportsWeakReference
-      ]),
-    };
-    Services.cache2.asyncGetDiskConsumption(obs);
-  });
-}
+// XXX: The intermittent bug 1331851
+// The implementation of nsICacheStorageConsumptionObserver must be passed as weak referenced,
+// so we must hold this observer here well. If we didn't, there would be a chance that
+// in Linux debug test run the observer was released before the operation at gecko was completed
+// (may be because of a relatively quicker GC cycle or a relatively slower operation).
+// As a result of that, we would never get the cache usage we want so the test would fail from timeout.
+const cacheUsageGetter = {
+  _promise: null,
+  _resolve: null,
+  get() {
+    if (!this._promise) {
+      this._promise = new Promise(resolve => {
+        this._resolve = resolve;
+        Services.cache2.asyncGetDiskConsumption(this);
+      });
+    }
+    return this._promise;
+  },
+  // nsICacheStorageConsumptionObserver implementations
+  onNetworkCacheDiskConsumption(usage) {
+    cacheUsageGetter._promise = null;
+    cacheUsageGetter._resolve(usage);
+  },
+  QueryInterface: XPCOMUtils.generateQI([
+    Components.interfaces.nsICacheStorageConsumptionObserver,
+    Components.interfaces.nsISupportsWeakReference
+  ]),
+};
 
 function openSettingsDialog() {
   let doc = gBrowser.selectedBrowser.contentDocument;
   let settingsBtn = doc.getElementById("siteDataSettings");
   let dialogOverlay = doc.getElementById("dialogOverlay");
   let dialogLoadPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
   let dialogInitPromise = TestUtils.topicObserved("sitedata-settings-init", () => true);
   let fullyLoadPromise = Promise.all([ dialogLoadPromise, dialogInitPromise ]).then(() => {
@@ -217,17 +231,17 @@ add_task(function* () {
 
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_BASE_URL + "site_data_test.html");
   yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
 
   // Test the initial states
-  let cacheUsage = yield getCacheUsage();
+  let cacheUsage = yield cacheUsageGetter.get();
   let quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
   let totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
   Assert.greater(quotaUsage, 0, "The quota usage should not be 0");
   Assert.greater(totalUsage, 0, "The total usage should not be 0");
 
   // Test cancelling "Clear All Data"
   // Click "Clear All Data" button and then cancel
@@ -236,17 +250,17 @@ add_task(function* () {
   let clearBtn = doc.getElementById("clearSiteDataButton");
   clearBtn.doCommand();
   yield cancelPromise;
 
   // Test the items are not removed
   let status = getPersistentStoragePermStatus(TEST_ORIGIN);
   is(status, Ci.nsIPermissionManager.ALLOW_ACTION, "Should not remove permission");
 
-  cacheUsage = yield getCacheUsage();
+  cacheUsage = yield cacheUsageGetter.get();
   quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
   Assert.greater(quotaUsage, 0, "The quota usage should not be 0");
   Assert.greater(totalUsage, 0, "The total usage should not be 0");
   // Test cancelling "Clear All Data" ends
 
   // Test accepting "Clear All Data"
@@ -264,17 +278,17 @@ add_task(function* () {
   // Test all the items are removed
   yield cookiesClearedPromise;
 
   ok(mockOfflineAppCacheHelper.clear.calledOnce, "Should clear app cache");
 
   status = getPersistentStoragePermStatus(TEST_ORIGIN);
   is(status, Ci.nsIPermissionManager.UNKNOWN_ACTION, "Should remove permission");
 
-  cacheUsage = yield getCacheUsage();
+  cacheUsage = yield cacheUsageGetter.get();
   quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
   is(cacheUsage, 0, "The cahce usage should be removed");
   is(quotaUsage, 0, "The quota usage should be removed");
   is(totalUsage, 0, "The total usage should be removed");
   // Test accepting "Clear All Data" ends
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
--- a/browser/components/preferences/in-content/tests/site_data_test.html
+++ b/browser/components/preferences/in-content/tests/site_data_test.html
@@ -16,14 +16,14 @@
       request.onupgradeneeded = function(e) {
         let db = e.target.result;
         db.createObjectStore("TestStore", { keyPath: "id" });
       };
       request.onsuccess = function(e) {
         let db = e.target.result;
         let tx = db.transaction("TestStore", "readwrite");
         let store = tx.objectStore("TestStore");
+        tx.oncomplete = () => window.dispatchEvent(new Event("test-indexedDB-done"));
         store.put({ id: "test_id", description: "Site Data Test"});
-        window.dispatchEvent(new Event("test-indexedDB-done"));
       }
     </script>
   </body>
 </html>
--- a/browser/components/preferences/siteDataSettings.js
+++ b/browser/components/preferences/siteDataSettings.js
@@ -40,38 +40,41 @@ let gSiteDataSettings = {
     SiteDataManager.getSites().then(sites => {
       this._sites = sites;
       let sortCol = document.getElementById("hostCol");
       this._sortSites(this._sites, sortCol);
       this._buildSitesList(this._sites);
       Services.obs.notifyObservers(null, "sitedata-settings-init", null);
     });
 
-    let removeAllBtn = document.getElementById("removeAll");
-    removeAllBtn.setAttribute("accesskey", this._prefStrBundle.getString("removeAll.accesskey"));
-
     setEventListener("hostCol", "click", this.onClickTreeCol);
     setEventListener("usageCol", "click", this.onClickTreeCol);
     setEventListener("statusCol", "click", this.onClickTreeCol);
     setEventListener("cancel", "command", this.close);
     setEventListener("save", "command", this.saveChanges);
     setEventListener("searchBox", "command", this.onCommandSearch);
     setEventListener("removeAll", "command", this.onClickRemoveAll);
     setEventListener("removeSelected", "command", this.onClickRemoveSelected);
   },
 
   _updateButtonsState() {
     let items = this._list.getElementsByTagName("richlistitem");
     let removeSelectedBtn = document.getElementById("removeSelected");
     let removeAllBtn = document.getElementById("removeAll");
     removeSelectedBtn.disabled = items.length == 0;
     removeAllBtn.disabled = removeSelectedBtn.disabled;
-    let removeAllBtnLabel = this._searchBox.value ?
-      this._prefStrBundle.getString("removeAllShown.label") : this._prefStrBundle.getString("removeAll.label");
-    removeAllBtn.setAttribute("label", removeAllBtnLabel);
+
+    let removeAllBtnLabelStringID = "removeAllSiteData.label";
+    let removeAllBtnAccesskeyStringID = "removeAllSiteData.accesskey";
+    if (this._searchBox.value) {
+      removeAllBtnLabelStringID = "removeAllSiteDataShown.label";
+      removeAllBtnAccesskeyStringID = "removeAllSiteDataShown.accesskey";
+    }
+    removeAllBtn.setAttribute("label", this._prefStrBundle.getString(removeAllBtnLabelStringID));
+    removeAllBtn.setAttribute("accesskey", this._prefStrBundle.getString(removeAllBtnAccesskeyStringID));
   },
 
   /**
    * @param sites {Array}
    * @param col {XULElement} the <treecol> being sorted on
    */
   _sortSites(sites, col) {
     let isCurrentSortCol = col.getAttribute("data-isCurrentSortCol")
--- a/browser/components/safebrowsing/content/test/browser_bug400731.js
+++ b/browser/components/safebrowsing/content/test/browser_bug400731.js
@@ -1,10 +1,12 @@
 /* Check presence of the "Ignore this warning" button */
 
+/* eslint-env mozilla/frame-script */
+
 function onDOMContentLoaded(callback) {
   function complete({ data }) {
     mm.removeMessageListener("Test:DOMContentLoaded", complete);
     callback(data);
   }
 
   let mm = gBrowser.selectedBrowser.messageManager;
   mm.addMessageListener("Test:DOMContentLoaded", complete);
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -204,33 +204,59 @@ nsWindowsShellService::ShortcutMaintenan
     return NS_ERROR_UNEXPECTED;
 
   appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds");
 
   return LaunchHelper(appHelperPath);
 }
 
 static bool
-IsAARDefault(const RefPtr<IApplicationAssociationRegistration>& pAAR,
-             LPCWSTR aClassName)
+IsPathDefaultForClass(const RefPtr<IApplicationAssociationRegistration>& pAAR,
+                      wchar_t *exePath, LPCWSTR aClassName)
 {
   // Make sure the Prog ID matches what we have
   LPWSTR registeredApp;
   bool isProtocol = *aClassName != L'.';
   ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
   HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE,
                                          &registeredApp);
   if (FAILED(hr)) {
     return false;
   }
 
   LPCWSTR progID = isProtocol ? L"FirefoxURL" : L"FirefoxHTML";
   bool isDefault = !wcsnicmp(registeredApp, progID, wcslen(progID));
+
+  nsAutoString regAppName(registeredApp);
   CoTaskMemFree(registeredApp);
 
+  if (isDefault) {
+    // Make sure the application path for this progID is this installation.
+    regAppName.AppendLiteral("\\shell\\open\\command");
+    HKEY theKey;
+    nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, regAppName, &theKey);
+    if (NS_FAILED(rv)) {
+      return false;
+    }
+
+    wchar_t cmdFromReg[MAX_BUF] = L"";
+    DWORD len = sizeof(cmdFromReg);
+    DWORD res = ::RegQueryValueExW(theKey, nullptr, nullptr, nullptr,
+                                   (LPBYTE)cmdFromReg, &len);
+    ::RegCloseKey(theKey);
+    if (REG_FAILED(res)) {
+      return false;
+    }
+
+    wchar_t fullCmd[MAX_BUF] = L"";
+    _snwprintf(fullCmd, MAX_BUF, L"\"%s\" -osint -url \"%%1\"", exePath);
+
+    isDefault = _wcsicmp(fullCmd, cmdFromReg) == 0;
+  }
+
   return isDefault;
 }
 
 static nsresult
 GetAppRegName(nsAutoString &aAppRegName)
 {
   nsresult rv;
   nsCOMPtr<nsIProperties> dirSvc =
@@ -274,19 +300,29 @@ nsWindowsShellService::IsDefaultBrowser(
                                 nullptr,
                                 CLSCTX_INPROC,
                                 IID_IApplicationAssociationRegistration,
                                 getter_AddRefs(pAAR));
   if (FAILED(hr)) {
     return NS_OK;
   }
 
-  *aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
+  wchar_t exePath[MAX_BUF] = L"";
+  if (!::GetModuleFileNameW(0, exePath, MAX_BUF)) {
+    return NS_OK;
+  }
+  // Convert the path to a long path since GetModuleFileNameW returns the path
+  // that was used to launch Firefox which is not necessarily a long path.
+  if (!::GetLongPathNameW(exePath, exePath, MAX_BUF)) {
+    return NS_OK;
+  }
+
+  *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L"http");
   if (*aIsDefaultBrowser && aForAllTypes) {
-    *aIsDefaultBrowser = IsAARDefault(pAAR, L".html");
+    *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L".html");
   }
   return NS_OK;
 }
 
 nsresult
 nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
 {
   IApplicationAssociationRegistrationUI* pAARUI;
--- a/browser/components/uitour/content-UITour.js
+++ b/browser/components/uitour/content-UITour.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* eslint-env mozilla/frame-script */
+
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const PREF_TEST_WHITELIST = "browser.uitour.testingOrigins";
 const UITOUR_PERMISSION   = "uitour";
 
 var UITourListener = {
   handleEvent(event) {
     if (!Services.prefs.getBoolPref("browser.uitour.enabled")) {
--- a/browser/config/mozconfigs/macosx64/debug-asan
+++ b/browser/config/mozconfigs/macosx64/debug-asan
@@ -1,20 +1,24 @@
-. $topsrcdir/build/unix/mozconfig.asan
-
+# Use at least -O1 for optimization to avoid stack space
+# exhaustions caused by Clang function inlining.
 ac_add_options --enable-application=browser
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 
+. $topsrcdir/build/unix/mozconfig.asan
+
 # Enable Telemetry
 export MOZ_TELEMETRY_REPORTING=1
 
-# Package js shell.
-export MOZ_PACKAGE_JSSHELL=1
-
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
+
 # Need this to prevent name conflicts with the normal nightly build packages
+# Before mozconfig.common so we can test for asan builds there
 export MOZ_PKG_SPECIAL=asan
 
+. "$topsrcdir/build/macosx/mozconfig.common"
 . "$topsrcdir/build/mozconfig.common.override"
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64/nightly-asan
@@ -0,0 +1,23 @@
+ac_add_options --enable-application=browser
+# We still need to build with debug symbols
+ac_add_options --disable-debug
+ac_add_options --enable-optimize="-O2"
+
+. $topsrcdir/build/unix/mozconfig.asan
+
+# Enable Telemetry
+export MOZ_TELEMETRY_REPORTING=1
+
+if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
+ac_add_options --with-macbundlename-prefix=Firefox
+fi
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
+
+# Need this to prevent name conflicts with the normal nightly build packages
+# Before mozconfig.common so we can test for asan builds there
+export MOZ_PKG_SPECIAL=asan
+
+. "$topsrcdir/build/macosx/mozconfig.common"
+. "$topsrcdir/build/mozconfig.common.override"
rename from browser/extensions/formautofill/content/FormAutofillContent.js
rename to browser/extensions/formautofill/FormAutofillContent.jsm
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -1,181 +1,40 @@
 /* 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/. */
 
-/* eslint-disable no-use-before-define */
+/*
+ * Form Autofill content process module.
+ */
 
-/*
- * Form Autofill frame script.
- */
+/* eslint-disable no-use-before-define */
 
 "use strict";
 
+this.EXPORTED_SYMBOLS = ["FormAutofillContent"];
+
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
                                   "resource://formautofill/ProfileAutoCompleteResult.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHandler",
+                                  "resource://formautofill/FormAutofillHandler.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
                                   "resource://gre/modules/FormLikeFactory.jsm");
 
 const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
                              .getService(Ci.nsIFormFillController);
 
 const AUTOFILL_FIELDS_THRESHOLD = 3;
 
-/**
- * Returns the autocomplete information of fields according to heuristics.
- */
-let FormAutofillHeuristics = {
-  VALID_FIELDS: [
-    "organization",
-    "street-address",
-    "address-level2",
-    "address-level1",
-    "postal-code",
-    "country",
-    "tel",
-    "email",
-  ],
-
-  getInfo(element) {
-    if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
-      return null;
-    }
-
-    let info = element.getAutocompleteInfo();
-    if (!info || !info.fieldName ||
-        !this.VALID_FIELDS.includes(info.fieldName)) {
-      return null;
-    }
-
-    return info;
-  },
-};
-
-/**
- * Handles profile autofill for a DOM Form element.
- * @param {HTMLFormElement} form Form that need to be auto filled
- */
-function FormAutofillHandler(form) {
-  this.form = form;
-  this.fieldDetails = [];
-}
-
-FormAutofillHandler.prototype = {
-  /**
-   * DOM Form element to which this object is attached.
-   */
-  form: null,
-
-  /**
-   * Array of collected data about relevant form fields.  Each item is an object
-   * storing the identifying details of the field and a reference to the
-   * originally associated element from the form.
-   *
-   * The "section", "addressType", "contactType", and "fieldName" values are
-   * used to identify the exact field when the serializable data is received
-   * from the backend.  There cannot be multiple fields which have
-   * the same exact combination of these values.
-   *
-   * A direct reference to the associated element cannot be sent to the user
-   * interface because processing may be done in the parent process.
-   */
-  fieldDetails: null,
-
-  /**
-   * Returns information from the form about fields that can be autofilled, and
-   * populates the fieldDetails array on this object accordingly.
-   *
-   * @returns {Array<Object>} Serializable data structure that can be sent to the user
-   *          interface, or null if the operation failed because the constraints
-   *          on the allowed fields were not honored.
-   */
-  collectFormFields() {
-    let autofillData = [];
-
-    for (let element of this.form.elements) {
-      // Exclude elements to which no autocomplete field has been assigned.
-      let info = FormAutofillHeuristics.getInfo(element);
-      if (!info) {
-        continue;
-      }
-
-      // Store the association between the field metadata and the element.
-      if (this.fieldDetails.some(f => f.section == info.section &&
-                                      f.addressType == info.addressType &&
-                                      f.contactType == info.contactType &&
-                                      f.fieldName == info.fieldName)) {
-        // A field with the same identifier already exists.
-        return null;
-      }
-
-      let inputFormat = {
-        section: info.section,
-        addressType: info.addressType,
-        contactType: info.contactType,
-        fieldName: info.fieldName,
-      };
-      // Clone the inputFormat for caching the fields and elements together
-      let formatWithElement = Object.assign({}, inputFormat);
-
-      inputFormat.index = autofillData.length;
-      autofillData.push(inputFormat);
-
-      formatWithElement.element = element;
-      this.fieldDetails.push(formatWithElement);
-    }
-
-    return autofillData;
-  },
-
-  /**
-   * Processes form fields that can be autofilled, and populates them with the
-   * data provided by backend.
-   *
-   * @param {Array<Object>} autofillResult
-   *        Data returned by the user interface.
-   *        [{
-   *          section: Value originally provided to the user interface.
-   *          addressType: Value originally provided to the user interface.
-   *          contactType: Value originally provided to the user interface.
-   *          fieldName: Value originally provided to the user interface.
-   *          value: String with which the field should be updated.
-   *          index: Index to match the input in fieldDetails
-   *        }],
-   *        }
-   */
-  autofillFormFields(autofillResult) {
-    for (let field of autofillResult) {
-      // Get the field details, if it was processed by the user interface.
-      let fieldDetail = this.fieldDetails[field.index];
-
-      // Avoid the invalid value set
-      if (!fieldDetail || !field.value) {
-        continue;
-      }
-
-      let info = FormAutofillHeuristics.getInfo(fieldDetail.element);
-      if (!info ||
-          field.section != info.section ||
-          field.addressType != info.addressType ||
-          field.contactType != info.contactType ||
-          field.fieldName != info.fieldName) {
-        Cu.reportError("Autocomplete tokens mismatched");
-        continue;
-      }
-
-      fieldDetail.element.setUserInput(field.value);
-    }
-  },
-};
-
 // Register/unregister a constructor as a factory.
 function AutocompleteFactory() {}
 AutocompleteFactory.prototype = {
   register(targetConstructor) {
     let proto = targetConstructor.prototype;
     this._classID = proto.classID;
 
     let factory = XPCOMUtils._getFactory(targetConstructor);
@@ -204,17 +63,17 @@ AutocompleteFactory.prototype = {
 
 
 /**
  * @constructor
  *
  * @implements {nsIAutoCompleteSearch}
  */
 function AutofillProfileAutoCompleteSearch() {
-
+  FormAutofillUtils.defineLazyLogGetter(this, "AutofillProfileAutoCompleteSearch");
 }
 AutofillProfileAutoCompleteSearch.prototype = {
   classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
   contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
   classDescription: "AutofillProfileAutoCompleteSearch",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
 
   // Begin nsIAutoCompleteSearch implementation
@@ -224,230 +83,276 @@ AutofillProfileAutoCompleteSearch.protot
    * or asynchronously) of the result
    *
    * @param {string} searchString the string to search for
    * @param {string} searchParam
    * @param {Object} previousResult a previous result to use for faster searchinig
    * @param {Object} listener the listener to notify when the search is complete
    */
   startSearch(searchString, searchParam, previousResult, listener) {
+    this.log.debug("startSearch: for", searchString, "with input", formFillController.focusedInput);
+    let focusedInput = formFillController.focusedInput;
     this.forceStop = false;
-    let info = this.getInputDetails();
+    let info = this._serializeInfo(FormAutofillContent.getInputDetails(focusedInput));
 
-    this.getProfiles({info, searchString}).then((profiles) => {
+    this._getProfiles({info, searchString}).then((profiles) => {
       if (this.forceStop) {
         return;
       }
 
-      // TODO: Set formInfo for ProfileAutoCompleteResult
-      // let formInfo = this.getFormDetails();
-      let result = new ProfileAutoCompleteResult(searchString, info, profiles, {});
+      let allFieldNames = FormAutofillContent.getAllFieldNames(focusedInput);
+      let result = new ProfileAutoCompleteResult(searchString,
+                                                 info.fieldName,
+                                                 allFieldNames,
+                                                 profiles,
+                                                 {});
 
       listener.onSearchResult(this, result);
+      ProfileAutocomplete.setProfileAutoCompleteResult(result);
     });
   },
 
   /**
    * Stops an asynchronous search that is in progress
    */
   stopSearch() {
+    ProfileAutocomplete.setProfileAutoCompleteResult(null);
     this.forceStop = true;
   },
 
   /**
    * Get the profile data from parent process for AutoComplete result.
    *
    * @private
    * @param  {Object} data
    *         Parameters for querying the corresponding result.
    * @param  {string} data.searchString
    *         The typed string for filtering out the matched profile.
    * @param  {string} data.info
    *         The input autocomplete property's information.
    * @returns {Promise}
    *          Promise that resolves when profiles returned from parent process.
    */
-  getProfiles(data) {
+  _getProfiles(data) {
+    this.log.debug("_getProfiles with data:", data);
     return new Promise((resolve) => {
-      addMessageListener("FormAutofill:Profiles", function getResult(result) {
-        removeMessageListener("FormAutofill:Profiles", getResult);
+      Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
+        Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
         resolve(result.data);
       });
 
-      sendAsyncMessage("FormAutofill:GetProfiles", data);
+      Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", data);
     });
   },
 
-
-  /**
-   * Get the input's information from FormAutofillContent's cache.
-   *
-   * @returns {Object}
-   *          Target input's information that cached in FormAutofillContent.
-   */
-  getInputDetails() {
-    // TODO: Maybe we'll need to wait for cache ready if detail is empty.
-    return FormAutofillContent.getInputDetails(formFillController.focusedInput);
-  },
-
-  /**
-   * Get the form's information from FormAutofillContent's cache.
-   *
-   * @returns {Array<Object>}
-   *          Array of the inputs' information for the target form.
-   */
-  getFormDetails() {
-    // TODO: Maybe we'll need to wait for cache ready if details is empty.
-    return FormAutofillContent.getFormDetails(formFillController.focusedInput);
+  _serializeInfo(detail) {
+    let info = Object.assign({}, detail);
+    delete info.element;
+    return info;
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
 
 let ProfileAutocomplete = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  _lastAutoCompleteResult: null,
   _registered: false,
   _factory: null,
 
   ensureRegistered() {
     if (this._registered) {
       return;
     }
 
+    FormAutofillUtils.defineLazyLogGetter(this, "ProfileAutocomplete");
+    this.log.debug("ensureRegistered");
     this._factory = new AutocompleteFactory();
     this._factory.register(AutofillProfileAutoCompleteSearch);
     this._registered = true;
+
+    Services.obs.addObserver(this, "autocomplete-will-enter-text", false);
   },
 
   ensureUnregistered() {
     if (!this._registered) {
       return;
     }
 
+    this.log.debug("ensureUnregistered");
     this._factory.unregister();
     this._factory = null;
     this._registered = false;
+    this._lastAutoCompleteResult = null;
+
+    Services.obs.removeObserver(this, "autocomplete-will-enter-text");
+  },
+
+  setProfileAutoCompleteResult(result) {
+    this._lastAutoCompleteResult = result;
+  },
+
+  observe(subject, topic, data) {
+    switch (topic) {
+      case "autocomplete-will-enter-text": {
+        if (!formFillController.focusedInput) {
+          // The observer notification is for autocomplete in a different process.
+          break;
+        }
+        this._fillFromAutocompleteRow(formFillController.focusedInput);
+        break;
+      }
+    }
+  },
+
+  _frameMMFromWindow(contentWindow) {
+    return contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIDocShell)
+                        .QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIContentFrameMessageManager);
+  },
+
+  _fillFromAutocompleteRow(focusedInput) {
+    this.log.debug("_fillFromAutocompleteRow:", focusedInput);
+    let formDetails = FormAutofillContent.getFormDetails(focusedInput);
+    if (!formDetails) {
+      // The observer notification is for a different frame.
+      return;
+    }
+
+    let mm = this._frameMMFromWindow(focusedInput.ownerGlobal);
+    let selectedIndexResult = mm.sendSyncMessage("FormAutoComplete:GetSelectedIndex", {});
+    if (selectedIndexResult.length != 1 || !Number.isInteger(selectedIndexResult[0])) {
+      throw new Error("Invalid autocomplete selectedIndex");
+    }
+    let selectedIndex = selectedIndexResult[0];
+
+    if (selectedIndex == -1 ||
+        !this._lastAutoCompleteResult ||
+        this._lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile") {
+      return;
+    }
+
+    let profile = JSON.parse(this._lastAutoCompleteResult.getCommentAt(selectedIndex));
+
+    // TODO: FormAutofillHandler.autofillFormFields will be used for filling
+    // fields logic eventually.
+    for (let inputInfo of formDetails) {
+      // Skip filling the value of focused input which is filled in
+      // FormFillController.
+      if (inputInfo.element === focusedInput) {
+        continue;
+      }
+      let value = profile[inputInfo.fieldName];
+      if (value) {
+        inputInfo.element.setUserInput(value);
+      }
+    }
   },
 };
 
 /**
- * Handles content's interactions.
+ * Handles content's interactions for the process.
  *
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
 var FormAutofillContent = {
+  /**
+   * @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects.
+   */
+  _formsDetails: new WeakMap(),
+
   init() {
-    addEventListener("DOMContentLoaded", this);
+    FormAutofillUtils.defineLazyLogGetter(this, "FormAutofillContent");
 
-    addMessageListener("FormAutofill:enabledStatus", (result) => {
+    Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => {
       if (result.data) {
         ProfileAutocomplete.ensureRegistered();
       } else {
         ProfileAutocomplete.ensureUnregistered();
       }
     });
-    sendAsyncMessage("FormAutofill:getEnabledStatus");
-  },
-
-  handleEvent(evt) {
-    if (!evt.isTrusted) {
-      return;
-    }
-
-    switch (evt.type) {
-      case "DOMContentLoaded":
-        let doc = evt.target;
-        if (!(doc instanceof Ci.nsIDOMHTMLDocument)) {
-          return;
-        }
-        this._identifyAutofillFields(doc);
-        break;
-    }
+    Services.cpmm.sendAsyncMessage("FormAutofill:getEnabledStatus");
+    // TODO: use initialProcessData:
+    // Services.cpmm.initialProcessData.autofillEnabled
   },
 
   /**
    * Get the input's information from cache which is created after page identified.
    *
    * @param {HTMLInputElement} element Focused input which triggered profile searching
    * @returns {Object|null}
    *          Return target input's information that cloned from content cache
    *          (or return null if the information is not found in the cache).
    */
   getInputDetails(element) {
-    for (let formDetails of this._formsDetails) {
-      for (let detail of formDetails) {
-        if (element == detail.element) {
-          return this._serializeInfo(detail);
-        }
+    let formDetails = this.getFormDetails(element);
+    for (let detail of formDetails) {
+      if (element == detail.element) {
+        return detail;
       }
     }
     return null;
   },
 
   /**
    * Get the form's information from cache which is created after page identified.
    *
    * @param {HTMLInputElement} element Focused input which triggered profile searching
    * @returns {Array<Object>|null}
    *          Return target form's information that cloned from content cache
    *          (or return null if the information is not found in the cache).
    *
    */
   getFormDetails(element) {
-    for (let formDetails of this._formsDetails) {
-      if (formDetails.some((detail) => detail.element == element)) {
-        return formDetails.map((detail) => this._serializeInfo(detail));
-      }
-    }
-    return null;
+    let rootElement = FormLikeFactory.findRootForField(element);
+    let formDetails = this._formsDetails.get(rootElement);
+    return formDetails ? formDetails.fieldDetails : null;
   },
 
-  /**
-   * Create a clone the information object without element reference.
-   *
-   * @param {Object} detail Profile autofill information for specific input.
-   * @returns {Object}
-   *          Return a copy of cached information object without element reference
-   *          since it's not needed for creating result.
-   */
-  _serializeInfo(detail) {
-    let info = Object.assign({}, detail);
-    delete info.element;
-    return info;
+  getAllFieldNames(element) {
+    let formDetails = this.getFormDetails(element);
+    return formDetails.map(record => record.fieldName);
   },
 
   _identifyAutofillFields(doc) {
+    this.log.debug("_identifyAutofillFields:", "" + doc.location);
     let forms = [];
-    this._formsDetails = [];
 
     // Collects root forms from inputs.
     for (let field of doc.getElementsByTagName("input")) {
       // We only consider text-like fields for now until we support radio and
       // checkbox buttons in the future.
       if (!field.mozIsTextField(true)) {
         continue;
       }
 
       let formLike = FormLikeFactory.createFromField(field);
       if (!forms.some(form => form.rootElement === formLike.rootElement)) {
         forms.push(formLike);
       }
     }
 
+    this.log.debug("Found", forms.length, "forms");
+
     // Collects the fields that can be autofilled from each form and marks them
     // as autofill fields if the amount is above the threshold.
     forms.forEach(form => {
       let formHandler = new FormAutofillHandler(form);
       formHandler.collectFormFields();
       if (formHandler.fieldDetails.length < AUTOFILL_FIELDS_THRESHOLD) {
+        this.log.debug("Ignoring form since it has only", formHandler.fieldDetails.length,
+                       "field(s)");
         return;
       }
 
-      this._formsDetails.push(formHandler.fieldDetails);
-      formHandler.fieldDetails.forEach(
-        detail => this._markAsAutofillField(detail.element));
+      this._formsDetails.set(form.rootElement, formHandler);
+      this.log.debug("Adding form handler to _formsDetails:", formHandler);
+      formHandler.fieldDetails.forEach(detail => this._markAsAutofillField(detail.element));
     });
   },
 
   _markAsAutofillField(field) {
     formFillController.markAsAutofillField(field);
   },
 };
 
copy from browser/extensions/formautofill/content/FormAutofillContent.js
copy to browser/extensions/formautofill/FormAutofillHandler.jsm
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -1,67 +1,35 @@
 /* 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/. */
 
-/* eslint-disable no-use-before-define */
-
 /*
- * Form Autofill frame script.
+ * Defines a handler object to represent forms that autofill can handle.
  */
 
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
+this.EXPORTED_SYMBOLS = ["FormAutofillHandler"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
-                                  "resource://formautofill/ProfileAutoCompleteResult.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
-                                  "resource://gre/modules/FormLikeFactory.jsm");
-
-const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
-                             .getService(Ci.nsIFormFillController);
-
-const AUTOFILL_FIELDS_THRESHOLD = 3;
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
-/**
- * Returns the autocomplete information of fields according to heuristics.
- */
-let FormAutofillHeuristics = {
-  VALID_FIELDS: [
-    "organization",
-    "street-address",
-    "address-level2",
-    "address-level1",
-    "postal-code",
-    "country",
-    "tel",
-    "email",
-  ],
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHeuristics",
+                                  "resource://formautofill/FormAutofillHeuristics.jsm");
 
-  getInfo(element) {
-    if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
-      return null;
-    }
-
-    let info = element.getAutocompleteInfo();
-    if (!info || !info.fieldName ||
-        !this.VALID_FIELDS.includes(info.fieldName)) {
-      return null;
-    }
-
-    return info;
-  },
-};
+this.log = null;
+FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 /**
  * Handles profile autofill for a DOM Form element.
- * @param {HTMLFormElement} form Form that need to be auto filled
+ * @param {FormLike} form Form that need to be auto filled
  */
 function FormAutofillHandler(form) {
   this.form = form;
   this.fieldDetails = [];
 }
 
 FormAutofillHandler.prototype = {
   /**
@@ -103,16 +71,17 @@ FormAutofillHandler.prototype = {
       }
 
       // Store the association between the field metadata and the element.
       if (this.fieldDetails.some(f => f.section == info.section &&
                                       f.addressType == info.addressType &&
                                       f.contactType == info.contactType &&
                                       f.fieldName == info.fieldName)) {
         // A field with the same identifier already exists.
+        log.debug("Not collecting a field matching another with the same info:", info);
         return null;
       }
 
       let inputFormat = {
         section: info.section,
         addressType: info.addressType,
         contactType: info.contactType,
         fieldName: info.fieldName,
@@ -122,16 +91,18 @@ FormAutofillHandler.prototype = {
 
       inputFormat.index = autofillData.length;
       autofillData.push(inputFormat);
 
       formatWithElement.element = element;
       this.fieldDetails.push(formatWithElement);
     }
 
+    log.debug("Collected details on", autofillData.length, "fields");
+
     return autofillData;
   },
 
   /**
    * Processes form fields that can be autofilled, and populates them with the
    * data provided by backend.
    *
    * @param {Array<Object>} autofillResult
@@ -142,17 +113,21 @@ FormAutofillHandler.prototype = {
    *          contactType: Value originally provided to the user interface.
    *          fieldName: Value originally provided to the user interface.
    *          value: String with which the field should be updated.
    *          index: Index to match the input in fieldDetails
    *        }],
    *        }
    */
   autofillFormFields(autofillResult) {
+    log.debug("autofillFormFields:", autofillResult);
     for (let field of autofillResult) {
+      // TODO: Skip filling the value of focused input which is filled in
+      // FormFillController.
+
       // Get the field details, if it was processed by the user interface.
       let fieldDetail = this.fieldDetails[field.index];
 
       // Avoid the invalid value set
       if (!fieldDetail || !field.value) {
         continue;
       }
 
@@ -165,291 +140,8 @@ FormAutofillHandler.prototype = {
         Cu.reportError("Autocomplete tokens mismatched");
         continue;
       }
 
       fieldDetail.element.setUserInput(field.value);
     }
   },
 };
-
-// Register/unregister a constructor as a factory.
-function AutocompleteFactory() {}
-AutocompleteFactory.prototype = {
-  register(targetConstructor) {
-    let proto = targetConstructor.prototype;
-    this._classID = proto.classID;
-
-    let factory = XPCOMUtils._getFactory(targetConstructor);
-    this._factory = factory;
-
-    let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-    registrar.registerFactory(proto.classID, proto.classDescription,
-                              proto.contractID, factory);
-
-    if (proto.classID2) {
-      this._classID2 = proto.classID2;
-      registrar.registerFactory(proto.classID2, proto.classDescription,
-                                proto.contractID2, factory);
-    }
-  },
-
-  unregister() {
-    let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-    registrar.unregisterFactory(this._classID, this._factory);
-    if (this._classID2) {
-      registrar.unregisterFactory(this._classID2, this._factory);
-    }
-    this._factory = null;
-  },
-};
-
-
-/**
- * @constructor
- *
- * @implements {nsIAutoCompleteSearch}
- */
-function AutofillProfileAutoCompleteSearch() {
-
-}
-AutofillProfileAutoCompleteSearch.prototype = {
-  classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
-  contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
-  classDescription: "AutofillProfileAutoCompleteSearch",
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
-
-  // Begin nsIAutoCompleteSearch implementation
-
-  /**
-   * Searches for a given string and notifies a listener (either synchronously
-   * or asynchronously) of the result
-   *
-   * @param {string} searchString the string to search for
-   * @param {string} searchParam
-   * @param {Object} previousResult a previous result to use for faster searchinig
-   * @param {Object} listener the listener to notify when the search is complete
-   */
-  startSearch(searchString, searchParam, previousResult, listener) {
-    this.forceStop = false;
-    let info = this.getInputDetails();
-
-    this.getProfiles({info, searchString}).then((profiles) => {
-      if (this.forceStop) {
-        return;
-      }
-
-      // TODO: Set formInfo for ProfileAutoCompleteResult
-      // let formInfo = this.getFormDetails();
-      let result = new ProfileAutoCompleteResult(searchString, info, profiles, {});
-
-      listener.onSearchResult(this, result);
-    });
-  },
-
-  /**
-   * Stops an asynchronous search that is in progress
-   */
-  stopSearch() {
-    this.forceStop = true;
-  },
-
-  /**
-   * Get the profile data from parent process for AutoComplete result.
-   *
-   * @private
-   * @param  {Object} data
-   *         Parameters for querying the corresponding result.
-   * @param  {string} data.searchString
-   *         The typed string for filtering out the matched profile.
-   * @param  {string} data.info
-   *         The input autocomplete property's information.
-   * @returns {Promise}
-   *          Promise that resolves when profiles returned from parent process.
-   */
-  getProfiles(data) {
-    return new Promise((resolve) => {
-      addMessageListener("FormAutofill:Profiles", function getResult(result) {
-        removeMessageListener("FormAutofill:Profiles", getResult);
-        resolve(result.data);
-      });
-
-      sendAsyncMessage("FormAutofill:GetProfiles", data);
-    });
-  },
-
-
-  /**
-   * Get the input's information from FormAutofillContent's cache.
-   *
-   * @returns {Object}
-   *          Target input's information that cached in FormAutofillContent.
-   */
-  getInputDetails() {
-    // TODO: Maybe we'll need to wait for cache ready if detail is empty.
-    return FormAutofillContent.getInputDetails(formFillController.focusedInput);
-  },
-
-  /**
-   * Get the form's information from FormAutofillContent's cache.
-   *
-   * @returns {Array<Object>}
-   *          Array of the inputs' information for the target form.
-   */
-  getFormDetails() {
-    // TODO: Maybe we'll need to wait for cache ready if details is empty.
-    return FormAutofillContent.getFormDetails(formFillController.focusedInput);
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
-
-let ProfileAutocomplete = {
-  _registered: false,
-  _factory: null,
-
-  ensureRegistered() {
-    if (this._registered) {
-      return;
-    }
-
-    this._factory = new AutocompleteFactory();
-    this._factory.register(AutofillProfileAutoCompleteSearch);
-    this._registered = true;
-  },
-
-  ensureUnregistered() {
-    if (!this._registered) {
-      return;
-    }
-
-    this._factory.unregister();
-    this._factory = null;
-    this._registered = false;
-  },
-};
-
-/**
- * Handles content's interactions.
- *
- * NOTE: Declares it by "var" to make it accessible in unit tests.
- */
-var FormAutofillContent = {
-  init() {
-    addEventListener("DOMContentLoaded", this);
-
-    addMessageListener("FormAutofill:enabledStatus", (result) => {
-      if (result.data) {
-        ProfileAutocomplete.ensureRegistered();
-      } else {
-        ProfileAutocomplete.ensureUnregistered();
-      }
-    });
-    sendAsyncMessage("FormAutofill:getEnabledStatus");
-  },
-
-  handleEvent(evt) {
-    if (!evt.isTrusted) {
-      return;
-    }
-
-    switch (evt.type) {
-      case "DOMContentLoaded":
-        let doc = evt.target;
-        if (!(doc instanceof Ci.nsIDOMHTMLDocument)) {
-          return;