Merge mc -> pine draft
authorGregor Wagner <anygregor@gmail.com>
Tue, 07 Apr 2015 15:06:32 -0700
changeset 388204 9d0650a7d7676cecd3607df88c919209af2b6ccb
parent 388203 5398f05a4012f97e15bf1cbd4e0a2621e79f5bf4 (current diff)
parent 255909 078128c2600a0c695f8a16bf561b171a523daedd (diff)
child 388205 150835f3236eea65c143c1bcf9ac29eadc012767
push id23132
push userbmo:lissyx+mozillians@lissyx.dyndns.org
push dateFri, 15 Jul 2016 10:07:12 +0000
milestone40.0a1
Merge mc -> pine
b2g/app/b2g.js
b2g/config/gaia.json
b2g/confvars.sh
browser/components/loop/standalone/Gruntfile.js
browser/components/loop/standalone/bower.json
browser/components/loop/standalone/content/legal/fonts/latin/clearsans-regular.eot
browser/components/loop/standalone/content/legal/fonts/latin/clearsans-regular.svg
browser/components/loop/standalone/content/legal/fonts/latin/clearsans-regular.ttf
browser/components/loop/standalone/content/legal/fonts/latin/clearsans-regular.woff
browser/components/loop/standalone/content/legal/fonts/latin/firasans-light.eot
browser/components/loop/standalone/content/legal/fonts/latin/firasans-light.otf
browser/components/loop/standalone/content/legal/fonts/latin/firasans-light.svg
browser/components/loop/standalone/content/legal/fonts/latin/firasans-light.ttf
browser/components/loop/standalone/content/legal/fonts/latin/firasans-light.woff
browser/components/loop/standalone/content/legal/fonts/latin/firasans-regular.eot
browser/components/loop/standalone/content/legal/fonts/latin/firasans-regular.otf
browser/components/loop/standalone/content/legal/fonts/latin/firasans-regular.svg
browser/components/loop/standalone/content/legal/fonts/latin/firasans-regular.ttf
browser/components/loop/standalone/content/legal/fonts/latin/firasans-regular.woff
browser/components/loop/standalone/content/legal/images/firefox.png
browser/components/loop/standalone/content/legal/images/mozilla.png
browser/components/loop/standalone/content/legal/js/loader.js
browser/components/loop/standalone/content/legal/styles/main.scss
browser/components/loop/standalone/content/legal/terms/index.html
browser/components/loop/standalone/grunttasks/marked.js
browser/components/loop/standalone/grunttasks/project_vars.js
browser/components/loop/standalone/grunttasks/replace.js
browser/components/loop/standalone/grunttasks/sass.js
browser/devtools/shared/timeline/memory-overview.js
browser/devtools/shared/widgets/FlameGraph.jsm
browser/themes/linux/theme-switcher-icon.png
browser/themes/osx/theme-switcher-icon.png
browser/themes/osx/theme-switcher-icon@2x.png
browser/themes/windows/Metro_Glyph-aero.png
browser/themes/windows/customizableui/panelUIOverlay-aero.css
browser/themes/windows/devedition-aero.css
browser/themes/windows/downloads/allDownloadsViewOverlay-aero.css
browser/themes/windows/downloads/downloads-aero.css
browser/themes/windows/downloads/indicator-aero.css
browser/themes/windows/places/organizer-aero.css
browser/themes/windows/places/places-aero.css
browser/themes/windows/preferences/checkbox-8.png
browser/themes/windows/theme-switcher-icon-aero.png
browser/themes/windows/theme-switcher-icon.png
configure.in
dom/asmjscache/AsmJSCache.cpp
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/indexedDB/TransactionThreadPool.cpp
dom/indexedDB/TransactionThreadPool.h
dom/ipc/TabChild.cpp
dom/media/fmp4/AVCCDecoderModule.cpp
dom/media/fmp4/AVCCDecoderModule.h
dom/media/test/test_eme_playback.html
dom/plugins/ipc/COMMessageFilter.cpp
dom/plugins/ipc/COMMessageFilter.h
dom/telephony/ipc/TelephonyIPCService.h
dom/tests/mochitest/general/test_interfaces.html
dom/workers/RuntimeService.cpp
gfx/layers/YCbCrImageDataSerializer.cpp
gfx/thebes/gfxVR.cpp
gfx/thebes/gfxVR.h
gfx/thebes/ovr_capi_dynamic.h
js/src/gc/GCRuntime.h
js/src/gdb/lib-for-tests/prolog.py
js/src/jit-test/lib/prolog.js
js/src/jit/shared/Assembler-x86-shared.cpp
js/src/jit/shared/Assembler-x86-shared.h
js/src/jit/shared/AssemblerBuffer-x86-shared.cpp
js/src/jit/shared/AssemblerBuffer-x86-shared.h
js/src/jit/shared/BaseAssembler-x86-shared.h
js/src/jit/shared/BaselineCompiler-x86-shared.cpp
js/src/jit/shared/BaselineCompiler-x86-shared.h
js/src/jit/shared/BaselineIC-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
js/src/jit/shared/Constants-x86-shared.h
js/src/jit/shared/Disassembler-x86-shared.cpp
js/src/jit/shared/Encoding-x86-shared.h
js/src/jit/shared/LIR-x86-shared.h
js/src/jit/shared/Lowering-x86-shared.cpp
js/src/jit/shared/Lowering-x86-shared.h
js/src/jit/shared/MacroAssembler-x86-shared.cpp
js/src/jit/shared/MacroAssembler-x86-shared.h
js/src/jit/shared/MoveEmitter-x86-shared.cpp
js/src/jit/shared/MoveEmitter-x86-shared.h
js/src/jit/shared/Patching-x86-shared.h
js/src/jit/x64/Architecture-x64.h
js/src/jit/x86/Architecture-x86.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/tests/js1_8_5/extensions/reflect-parse-destructuring-__proto__.js
js/src/tests/js1_8_5/extensions/reflect-parse-proxy.js
js/src/tests/js1_8_5/extensions/reflect-parse.js
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
layout/analysis/pixel-conversion.js
layout/analysis/simple-match.js
media/libstagefright/ports/win32/include/stdbool.h
media/libyuv/libyuv.gyp
media/libyuv/source/mjpeg_decoder.cc
mobile/android/base/reading/ReadingListConstants.java
mobile/android/base/resources/drawable-hdpi/favicon_search.png
mobile/android/base/resources/drawable-hdpi/fxaccount_checkbox.png
mobile/android/base/resources/drawable-hdpi/fxaccount_mail.png
mobile/android/base/resources/drawable-hdpi/share_overlay_background.9.png
mobile/android/base/resources/drawable-hdpi/share_plane.png
mobile/android/base/resources/drawable-hdpi/suggestion_item_search.png
mobile/android/base/resources/drawable-mdpi/favicon_search.png
mobile/android/base/resources/drawable-mdpi/share_plane.png
mobile/android/base/resources/drawable-mdpi/suggestion_item_search.png
mobile/android/base/resources/drawable-xhdpi/favicon_search.png
mobile/android/base/resources/drawable-xhdpi/share_plane.png
mobile/android/base/resources/drawable-xhdpi/suggestion_item_search.png
mobile/android/base/resources/drawable-xxhdpi/favicon_search.png
mobile/android/base/resources/drawable-xxhdpi/share_plane.png
mobile/android/base/resources/drawable-xxhdpi/suggestion_item_search.png
mobile/android/branding/aurora/res/drawable-hdpi/large_icon.png
mobile/android/branding/aurora/res/drawable-mdpi/large_icon.png
mobile/android/branding/aurora/res/drawable-xhdpi/large_icon.png
mobile/android/branding/beta/res/drawable-hdpi/large_icon.png
mobile/android/branding/beta/res/drawable-mdpi/large_icon.png
mobile/android/branding/beta/res/drawable-xhdpi/large_icon.png
mobile/android/branding/nightly/res/drawable-hdpi/large_icon.png
mobile/android/branding/nightly/res/drawable-mdpi/large_icon.png
mobile/android/branding/nightly/res/drawable-xhdpi/large_icon.png
mobile/android/branding/official/res/drawable-hdpi/large_icon.png
mobile/android/branding/official/res/drawable-mdpi/large_icon.png
mobile/android/branding/official/res/drawable-xhdpi/large_icon.png
mobile/android/branding/unofficial/res/drawable-hdpi/large_icon.png
mobile/android/branding/unofficial/res/drawable-mdpi/large_icon.png
mobile/android/branding/unofficial/res/drawable-xhdpi/large_icon.png
mobile/android/branding/unofficial/res/drawable-xxhdpi/large_icon.png
security/manager/ssl/public/nsISSLErrorListener.idl
security/pkix/include/pkix/stdkeywords.h
services/common/storageservice.js
services/common/tests/unit/test_storageservice_bso.js
services/common/tests/unit/test_storageservice_client.js
services/datareporting/sessions.jsm
services/datareporting/tests/xpcshell/test_client_id.js
services/datareporting/tests/xpcshell/test_session_recorder.js
testing/mozbase/mozrunner/mozrunner/base/device.py
testing/taskcluster/tasks/builds/b2g_dolphin_user.yml
testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.front.html.ini
testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.top.html.ini
testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.inside2.html.ini
testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.inside3.html.ini
testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.outside1.html.ini
testing/web-platform/meta/2dcontext/line-styles/2d.line.cap.closed.html.ini
testing/web-platform/meta/IndexedDB/idbcursor_update_index5.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_update_objectstore6.htm.ini
testing/web-platform/meta/IndexedDB/idbfactory_open12.htm.ini
testing/web-platform/meta/IndexedDB/idbfactory_open2.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_get7.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_getKey7.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_openCursor2.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_openKeyCursor3.htm.ini
testing/web-platform/meta/IndexedDB/idbobjectstore_get.htm.ini
testing/web-platform/meta/IndexedDB/interfaces.html.ini
testing/web-platform/meta/XMLHttpRequest/xmlhttprequest-timeout-twice.html.ini
testing/web-platform/meta/custom-elements/concepts/custom-elements-type-allowed-chars-first-char.html.ini
testing/web-platform/meta/custom-elements/concepts/custom-elements-type-allowed-chars.html.ini
testing/web-platform/meta/custom-elements/custom-element-lifecycle/types-of-callbacks/created-callback-element-prototype-test.html.ini
testing/web-platform/meta/encoding/single-byte-decoder.html.ini
testing/web-platform/meta/html/dom/elements/global-attributes/dir_auto-textarea-N-between-Rs.html.ini
testing/web-platform/meta/html/dom/elements/global-attributes/dir_auto-textarea-script-N-between-Rs.html.ini
testing/web-platform/meta/html/infrastructure/common-dom-interfaces/collections/radionodelist.html.ini
testing/web-platform/meta/html/semantics/embedded-content/media-elements/event_timeupdate.html.ini
testing/web-platform/meta/html/semantics/embedded-content/the-img-element/srcset/select-an-image-source.html.ini
testing/web-platform/meta/html/semantics/grouping-content/the-blockquote-element/grouping-blockquote.sub.html.ini
testing/web-platform/meta/websockets/Create-Secure-blocked-port.htm.ini
testing/web-platform/meta/websockets/Create-Secure-verify-url-set-non-default-port.htm.ini
testing/web-platform/meta/websockets/constructor/002.html.ini
testing/web-platform/meta/websockets/constructor/009.html.ini
testing/web-platform/meta/websockets/cookies/001.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/constants/002.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/constants/003.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/constants/004.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/constants/005.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/constants/006.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/002.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/003.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/011.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/012.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/014.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/015.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/events/016.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/send/005.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/url/001.html.ini
testing/web-platform/meta/workers/WorkerGlobalScope_ErrorEvent_filename.htm.ini
testing/web-platform/meta/workers/WorkerGlobalScope_ErrorEvent_lineno.htm.ini
testing/web-platform/meta/workers/WorkerLocation_port.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_filename.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_lineno.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_message.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_type.htm.ini
testing/web-platform/meta/workers/Worker_dispatchEvent_ErrorEvent.htm.ini
testing/web-platform/tests/html/semantics/grouping-content/the-blockquote-element/grouping-blockquote.sub.html
toolkit/components/aboutcompartments/content/aboutCompartments.js
toolkit/components/aboutcompartments/content/aboutCompartments.xhtml
toolkit/components/aboutcompartments/jar.mn
toolkit/components/aboutcompartments/moz.build
toolkit/components/aboutcompartments/nsCompartmentInfo.cpp
toolkit/components/aboutcompartments/nsCompartmentInfo.h
toolkit/components/aboutcompartments/nsICompartmentInfo.idl
toolkit/mozapps/update/UpdaterHealthProvider.jsm
toolkit/mozapps/update/tests/unit_aus_update/updateHealthReport.js
toolkit/themes/linux/mozapps/extensions/category-dictionaries.png
toolkit/themes/linux/mozapps/extensions/category-experiments.png
toolkit/themes/linux/mozapps/extensions/experimentGeneric.png
toolkit/themes/shared/reader/RM-Close-hover-24x24.svg
toolkit/themes/windows/global/autocomplete-aero.css
toolkit/themes/windows/global/console/console-aero.css
toolkit/themes/windows/global/console/console-toolbar-aero.png
toolkit/themes/windows/global/dirListing/folder-aero.png
toolkit/themes/windows/global/dirListing/local-aero.png
toolkit/themes/windows/global/dirListing/remote-aero.png
toolkit/themes/windows/global/dirListing/up-aero.png
toolkit/themes/windows/global/icons/Error-aero.png
toolkit/themes/windows/global/icons/Landscape-aero.png
toolkit/themes/windows/global/icons/Portrait-aero.png
toolkit/themes/windows/global/icons/Print-preview-aero.png
toolkit/themes/windows/global/icons/Question-aero.png
toolkit/themes/windows/global/icons/Search-close-aero.png
toolkit/themes/windows/global/icons/Search-glass-aero.png
toolkit/themes/windows/global/icons/Warning-aero.png
toolkit/themes/windows/global/icons/autoscroll-aero.png
toolkit/themes/windows/global/icons/blacklist_favicon-aero.png
toolkit/themes/windows/global/icons/blacklist_large-aero.png
toolkit/themes/windows/global/icons/error-16-aero.png
toolkit/themes/windows/global/icons/error-24-aero.png
toolkit/themes/windows/global/icons/error-48-aero.png
toolkit/themes/windows/global/icons/error-64-aero.png
toolkit/themes/windows/global/icons/find-aero.png
toolkit/themes/windows/global/icons/folder-item-aero.png
toolkit/themes/windows/global/icons/information-16-aero.png
toolkit/themes/windows/global/icons/information-24-aero.png
toolkit/themes/windows/global/icons/information-32-aero.png
toolkit/themes/windows/global/icons/information-48-aero.png
toolkit/themes/windows/global/icons/information-64-aero.png
toolkit/themes/windows/global/icons/question-16-aero.png
toolkit/themes/windows/global/icons/question-24-aero.png
toolkit/themes/windows/global/icons/question-48-aero.png
toolkit/themes/windows/global/icons/question-64-aero.png
toolkit/themes/windows/global/icons/sslWarning-aero.png
toolkit/themes/windows/global/icons/warning-16-aero.png
toolkit/themes/windows/global/icons/warning-24-aero.png
toolkit/themes/windows/global/icons/warning-64-aero.png
toolkit/themes/windows/global/icons/warning-large-aero.png
toolkit/themes/windows/global/icons/windowControls-aero.png
toolkit/themes/windows/global/icons/wrap-aero.png
toolkit/themes/windows/global/inContentUI-aero.css
toolkit/themes/windows/global/listbox-aero.css
toolkit/themes/windows/global/menu-aero.css
toolkit/themes/windows/global/menulist-aero.css
toolkit/themes/windows/global/notification-aero.css
toolkit/themes/windows/global/popup-aero.css
toolkit/themes/windows/global/printpreview/arrow-left-aero.png
toolkit/themes/windows/global/printpreview/arrow-left-end-aero.png
toolkit/themes/windows/global/printpreview/arrow-right-aero.png
toolkit/themes/windows/global/printpreview/arrow-right-end-aero.png
toolkit/themes/windows/global/textbox-aero.css
toolkit/themes/windows/global/toolbar/spring-aero.png
toolkit/themes/windows/global/toolbarbutton-aero.css
toolkit/themes/windows/global/tree-aero.css
toolkit/themes/windows/global/tree/sort-asc-aero.png
toolkit/themes/windows/global/tree/sort-dsc-aero.png
toolkit/themes/windows/global/tree/twisty-clsd-aero.png
toolkit/themes/windows/global/tree/twisty-clsd-hover-aero.png
toolkit/themes/windows/global/tree/twisty-clsd-hover-rtl-aero.png
toolkit/themes/windows/global/tree/twisty-clsd-rtl-aero.png
toolkit/themes/windows/global/tree/twisty-open-aero.png
toolkit/themes/windows/global/tree/twisty-open-hover-aero.png
toolkit/themes/windows/global/tree/twisty-open-hover-rtl-aero.png
toolkit/themes/windows/global/tree/twisty-open-rtl-aero.png
toolkit/themes/windows/mozapps/downloads/downloadButtons-aero.png
toolkit/themes/windows/mozapps/downloads/downloadIcon-aero.png
toolkit/themes/windows/mozapps/downloads/downloads-aero.css
toolkit/themes/windows/mozapps/extensions/category-available-aero.png
toolkit/themes/windows/mozapps/extensions/category-dictionaries-aero.png
toolkit/themes/windows/mozapps/extensions/category-dictionaries.png
toolkit/themes/windows/mozapps/extensions/category-discover-aero.png
toolkit/themes/windows/mozapps/extensions/category-experiments-aero.png
toolkit/themes/windows/mozapps/extensions/category-experiments.png
toolkit/themes/windows/mozapps/extensions/category-plugins-aero.png
toolkit/themes/windows/mozapps/extensions/category-recent-aero.png
toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-16-aero.png
toolkit/themes/windows/mozapps/extensions/dictionaryGeneric-aero.png
toolkit/themes/windows/mozapps/extensions/experimentGeneric-aero.png
toolkit/themes/windows/mozapps/extensions/extensionGeneric-16-aero.png
toolkit/themes/windows/mozapps/extensions/extensionGeneric-aero.png
toolkit/themes/windows/mozapps/extensions/extensions-aero.css
toolkit/themes/windows/mozapps/extensions/localeGeneric-aero.png
toolkit/themes/windows/mozapps/extensions/newaddon-aero.css
toolkit/themes/windows/mozapps/extensions/selectAddons-aero.css
toolkit/themes/windows/mozapps/extensions/themeGeneric-16-aero.png
toolkit/themes/windows/mozapps/extensions/themeGeneric-aero.png
toolkit/themes/windows/mozapps/plugins/pluginBlocked-aero.png
toolkit/themes/windows/mozapps/plugins/pluginGeneric-16-aero.png
toolkit/themes/windows/mozapps/plugins/pluginGeneric-aero.png
toolkit/themes/windows/mozapps/profile/profileicon-aero.png
toolkit/themes/windows/mozapps/update/downloadButtons-aero.png
widget/gonk/HwcComposer2D.cpp
--- a/.gitignore
+++ b/.gitignore
@@ -3,16 +3,17 @@
 # Filenames that should be ignored wherever they appear
 *~
 *.pyc
 *.pyo
 TAGS
 tags
 ID
 .DS_Store*
+*.pdb
 
 # Vim swap files.
 .*.sw[a-z]
 
 # Emacs directory variable files.
 **/.dir-locals.el
 
 # User files that may appear at the root
--- a/.hgignore
+++ b/.hgignore
@@ -77,22 +77,10 @@ GPATH
 
 # Unit tests for Loop
 ^browser/components/loop/standalone/content/config\.js$
 ^browser/components/loop/standalone/node_modules/
 
 # Git clone directory for updating web-platform-tests
 ^testing/web-platform/sync/
 
-# Loop web client build/deploy dependencies
-^browser/components/loop/standalone/bower_components
-
-# Loop legal content build/deploy artifacts
-
-# XXX Once a grunt contrib-clean command has been added (bug 1066491), or
-# once legal has centralized their ToS and PP hosting infrastructure,
-# (expected Q4 2014) the legal doc build stuff for Loop can be removed,
-# including the following three lines
-^browser/components/loop/standalone/content/legal/styles/.*\.css$
-^browser/components/loop/standalone/content/legal/terms/en_US\.html$
-
 # Android Gradle artifacts.
 ^mobile/android/gradle/.gradle
--- a/.hgtags
+++ b/.hgtags
@@ -111,8 +111,9 @@ ca89fe55717059e4e43040d16d260765ffa9dca7
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 b297a6727acfd21e757ddd38cd61894812666265 FIREFOX_AURORA_36_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 2c951493eef5b50b8085ef78ffe0d7902ff3d593 FIREFOX_AURORA_37_BASE
 98086da94ccdc88f6de86774ce3d1fa258dc7c44 FIREFOX_AURORA_38_BASE
+1b6bf6612c0f4d4fee81d18bf18016e692f874e1 FIREFOX_AURORA_39_BASE
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # 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.
 
-Bug 1137470 NSS update required a clobber to fully rebuild
+Hitting failures with Taskcluster-based jobs that seem like needs-clobber.
+
--- a/README.txt
+++ b/README.txt
@@ -22,8 +22,9 @@ You can download nightly development bui
 Keep in mind that nightly builds, which are used by Mozilla developers for
 testing, may be buggy. Firefox nightlies, for example, can be found at:
 
     ftp://ftp.mozilla.org/pub/firefox/nightly/latest-trunk/
             - or -
     http://nightly.mozilla.org/
 
 
+>>
--- a/accessible/atk/nsMaiInterfaceDocument.cpp
+++ b/accessible/atk/nsMaiInterfaceDocument.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "AccessibleWrap.h"
 #include "DocAccessible.h"
 #include "nsMai.h"
+#include "ProxyAccessible.h"
 #include "mozilla/Likely.h"
 
 using namespace mozilla::a11y;
 
 static const char* const kDocTypeName = "W3C-doctype";
 static const char* const kDocUrlName = "DocURL";
 static const char* const kMimeTypeName = "MimeType";
 
@@ -41,22 +42,24 @@ documentInterfaceInitCB(AtkDocumentIface
     aIface->get_document_attributes = getDocumentAttributesCB;
     aIface->get_document_attribute_value = getDocumentAttributeValueCB;
     aIface->get_document_locale = getDocumentLocaleCB;
 }
 
 const gchar *
 getDocumentLocaleCB(AtkDocument *aDocument)
 {
+  nsAutoString locale;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-  if (!accWrap)
-    return nullptr;
+  if (accWrap) {
+    accWrap->Language(locale);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
+    proxy->Language(locale);
+  }
 
-  nsAutoString locale;
-  accWrap->Language(locale);
   return locale.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(locale);
 }
 
 static inline GSList *
 prependToList(GSList *aList, const char *const aName, const nsAutoString &aValue)
 {
   if (aValue.IsEmpty())
     return aList;
@@ -66,52 +69,82 @@ prependToList(GSList *aList, const char 
     atkAttr->name = g_strdup(aName);
     atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get());
     return g_slist_prepend(aList, atkAttr);
 }
 
 AtkAttributeSet *
 getDocumentAttributesCB(AtkDocument *aDocument)
 {
+  nsAutoString url;
+  nsAutoString w3cDocType;
+  nsAutoString mimeType;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-  if (!accWrap || !accWrap->IsDoc())
+  if (accWrap) {
+    if (!accWrap->IsDoc()) {
+      return nullptr;
+    }
+
+    DocAccessible* document = accWrap->AsDoc();
+    document->URL(url);
+    document->DocType(w3cDocType);
+    document->MimeType(mimeType);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
+    proxy->URLDocTypeMimeType(url, w3cDocType, mimeType);
+  } else {
     return nullptr;
+  }
 
   // according to atkobject.h, AtkAttributeSet is a GSList
   GSList* attributes = nullptr;
-  DocAccessible* document = accWrap->AsDoc();
-  nsAutoString aURL;
-  document->URL(aURL);
-  attributes = prependToList(attributes, kDocUrlName, aURL);
-
-  nsAutoString aW3CDocType;
-  document->DocType(aW3CDocType);
-  attributes = prependToList(attributes, kDocTypeName, aW3CDocType);
-
-  nsAutoString aMimeType;
-  document->MimeType(aMimeType);
-  attributes = prependToList(attributes, kMimeTypeName, aMimeType);
+  attributes = prependToList(attributes, kDocUrlName, url);
+  attributes = prependToList(attributes, kDocTypeName, w3cDocType);
+  attributes = prependToList(attributes, kMimeTypeName, mimeType);
 
   return attributes;
 }
 
 const gchar *
 getDocumentAttributeValueCB(AtkDocument *aDocument,
                             const gchar *aAttrName)
 {
+  ProxyAccessible* proxy = nullptr;
+  DocAccessible* document = nullptr;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-  if (!accWrap || !accWrap->IsDoc())
-    return nullptr;
+  if (accWrap) {
+    if (!accWrap->IsDoc()) {
+      return nullptr;
+    }
 
-  DocAccessible* document = accWrap->AsDoc();
+    document = accWrap->AsDoc();
+  } else {
+    proxy = GetProxy(ATK_OBJECT(aDocument));
+    if (!proxy) {
+      return nullptr;
+    }
+  }
+
   nsAutoString attrValue;
-  if (!strcasecmp(aAttrName, kDocTypeName))
-    document->DocType(attrValue);
-  else if (!strcasecmp(aAttrName, kDocUrlName))
-    document->URL(attrValue);
-  else if (!strcasecmp(aAttrName, kMimeTypeName))
-    document->MimeType(attrValue);
-  else
+  if (!strcasecmp(aAttrName, kDocTypeName)) {
+    if (document) {
+      document->DocType(attrValue);
+    } else {
+      proxy->DocType(attrValue);
+    }
+  } else if (!strcasecmp(aAttrName, kDocUrlName)) {
+    if (document) {
+      document->URL(attrValue);
+    } else {
+      proxy->URL(attrValue);
+    }
+  } else if (!strcasecmp(aAttrName, kMimeTypeName)) {
+    if (document) {
+      document->MimeType(attrValue);
+    } else {
+      proxy->MimeType(attrValue);
+    }
+  } else {
     return nullptr;
+  }
 
   return attrValue.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(attrValue);
 }
 }
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -473,17 +473,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     eSelectAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIACheckableBool
   },
   { // radiogroup
     &nsGkAtoms::radiogroup,
-    roles::GROUPING,
+    roles::RADIO_GROUP,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIAOrientation
   },
--- a/accessible/base/NotificationController.h
+++ b/accessible/base/NotificationController.h
@@ -81,17 +81,17 @@ private:
   Callback mCallback;
   nsRefPtr<Arg> mArg;
 };
 
 /**
  * Used to process notifications from core for the document accessible.
  */
 class NotificationController final : public EventQueue,
-                                         public nsARefreshObserver
+                                     public nsARefreshObserver
 {
 public:
   NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell);
 
   NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
   NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
 
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
--- a/accessible/base/Role.h
+++ b/accessible/base/Role.h
@@ -971,17 +971,22 @@ enum Role {
    */
   MATHML_STACK_CARRY = 166,
 
   /**
    * A MathML line in a stack (msline in MathML).
    */
   MATHML_STACK_LINE = 167,
 
-  LAST_ROLE = MATHML_STACK_LINE
+  /**
+   * A group containing radio buttons
+   */
+  RADIO_GROUP = 168,
+
+  LAST_ROLE = RADIO_GROUP
 };
 
 } // namespace role
 
 typedef enum mozilla::a11y::roles::Role role;
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -1354,8 +1354,16 @@ ROLE(MATHML_STACK_CARRY,
 
 ROLE(MATHML_STACK_LINE,
      "mathml stack line",
      ATK_ROLE_UNKNOWN,
      NSAccessibilityUnknownRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
+
+ROLE(RADIO_GROUP,
+     "grouping",
+     ATK_ROLE_PANEL,
+     NSAccessibilityRadioGroupRole,
+     ROLE_SYSTEM_GROUPING,
+     ROLE_SYSTEM_GROUPING,
+     eNoNameRule)
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -59,20 +59,20 @@ struct MarkupMapInfo {
   a11y::role role;
   MarkupAttrInfo attrs[4];
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 class nsAccessibilityService final : public mozilla::a11y::DocManager,
-                                         public mozilla::a11y::FocusManager,
-                                         public mozilla::a11y::SelectionManager,
-                                         public nsIAccessibilityService,
-                                         public nsIObserver
+                                     public mozilla::a11y::FocusManager,
+                                     public mozilla::a11y::SelectionManager,
+                                     public nsIAccessibilityService,
+                                     public nsIObserver
 {
 public:
   typedef mozilla::a11y::Accessible Accessible;
   typedef mozilla::a11y::DocAccessible DocAccessible;
 
 protected:
   virtual ~nsAccessibilityService();
 
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -19,17 +19,17 @@ using namespace mozilla::a11y;
  */
 class RuleCache
 {
 public:
   explicit RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
                                                           mAcceptRoles(nullptr) { }
   ~RuleCache () {
     if (mAcceptRoles)
-      nsMemory::Free(mAcceptRoles);
+      free(mAcceptRoles);
   }
 
   nsresult ApplyFilter(Accessible* aAccessible, uint16_t* aResult);
 
 private:
   nsCOMPtr<nsIAccessibleTraversalRule> mRule;
   uint32_t* mAcceptRoles;
   uint32_t mAcceptRolesLength;
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1284,20 +1284,22 @@ DocAccessible::ContentInserted(nsIConten
 {
   // Ignore content insertions until we constructed accessible tree. Otherwise
   // schedule tree update on content insertion after layout.
   if (mNotificationController && HasLoadState(eTreeConstructed)) {
     // Update the whole tree of this document accessible when the container is
     // null (document element is inserted or removed).
     Accessible* container = aContainerNode ?
       GetAccessibleOrContainer(aContainerNode) : this;
-
-    mNotificationController->ScheduleContentInsertion(container,
-                                                      aStartChildNode,
-                                                      aEndChildNode);
+    if (container) {
+      // Ignore notification if the container node is no longer in the DOM tree.
+      mNotificationController->ScheduleContentInsertion(container,
+                                                        aStartChildNode,
+                                                        aEndChildNode);
+    }
   }
 }
 
 void
 DocAccessible::RecreateAccessible(nsIContent* aContent)
 {
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eTree)) {
--- a/accessible/interfaces/nsIAccessibleRole.idl
+++ b/accessible/interfaces/nsIAccessibleRole.idl
@@ -3,17 +3,17 @@
  * 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 "nsISupports.idl"
 
 /**
  * Defines cross platform (Gecko) roles.
  */
-[scriptable, uuid(55581ec3-ba6e-4805-8108-260ed34cdcbb)]
+[scriptable, uuid(00f9e831-3198-40b7-9186-5251474d4d7a)]
 interface nsIAccessibleRole : nsISupports
 {
   /**
    * Used when accessible hans't strong defined role.
    */
   const unsigned long ROLE_NOTHING = 0;
 
   /**
@@ -964,9 +964,14 @@ interface nsIAccessibleRole : nsISupport
    * A MathML carry, borrow, or crossout for a column (mscarry in MathML).
    */
   const unsigned long ROLE_MATHML_STACK_CARRY = 166;
 
   /**
    * A MathML line in a stack (msline in MathML).
    */
   const unsigned long ROLE_MATHML_STACK_LINE = 167;
+
+  /**
+   * A group containing radio buttons
+   */
+  const unsigned long ROLE_RADIO_GROUP = 168;
 };
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -1626,10 +1626,75 @@ DocAccessibleChild::RecvBounds(const uin
   Accessible* acc = IdToAccessible(aID);
   if (acc && !acc->IsDefunct()) {
     *aRect = acc->Bounds();
   }
 
   return false;
 }
 
+bool
+DocAccessibleChild::RecvLanguage(const uint64_t& aID,
+                                 nsString* aLocale)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc) {
+    acc->Language(*aLocale);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvDocType(const uint64_t& aID,
+                                nsString* aType)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    acc->AsDoc()->DocType(*aType);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvURL(const uint64_t& aID,
+                            nsString* aURL)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    acc->AsDoc()->URL(*aURL);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvMimeType(const uint64_t& aID,
+                                 nsString* aMime)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    acc->AsDoc()->MimeType(*aMime);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvURLDocTypeMimeType(const uint64_t& aID,
+                                           nsString* aURL,
+                                           nsString* aDocType,
+                                           nsString* aMimeType)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    DocAccessible* doc = acc->AsDoc();
+    doc->URL(*aURL);
+    doc->DocType(*aDocType);
+    doc->MimeType(*aMimeType);
+  }
+
+  return true;
+}
+
 }
 }
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -371,41 +371,50 @@ public:
                              uint32_t* aKey,
                              uint32_t* aModifierMask) override;
 
   virtual bool RecvKeyboardShortcut(const uint64_t& aID,
                                     uint32_t* aKey,
                                     uint32_t* aModifierMask) override;
 
   virtual bool RecvCurValue(const uint64_t& aID,
-                            double* aValue);
+                            double* aValue) override;
 
   virtual bool RecvSetCurValue(const uint64_t& aID,
                                const double& aValue,
-                               bool* aRetVal);
+                               bool* aRetVal) override;
 
   virtual bool RecvMinValue(const uint64_t& aID,
-                            double* aValue);
+                            double* aValue) override;
 
   virtual bool RecvMaxValue(const uint64_t& aID,
-                            double* aValue);
+                            double* aValue) override;
 
   virtual bool RecvStep(const uint64_t& aID,
-                        double* aStep);
+                        double* aStep) override;
 
   virtual bool RecvTakeFocus(const uint64_t& aID) override;
 
   virtual bool RecvChildAtPoint(const uint64_t& aID,
                                 const int32_t& aX,
                                 const int32_t& aY,
                                 const uint32_t& aWhich,
                                 uint64_t* aChild,
                                 bool* aOk) override;
 
   virtual bool RecvBounds(const uint64_t& aID, nsIntRect* aRect) override;
+
+  virtual bool RecvLanguage(const uint64_t& aID, nsString* aLocale) override;
+  virtual bool RecvDocType(const uint64_t& aID, nsString* aType) override;
+  virtual bool RecvURL(const uint64_t& aID, nsString* aURL) override;
+  virtual bool RecvMimeType(const uint64_t& aID, nsString* aMime) override;
+  virtual bool RecvURLDocTypeMimeType(const uint64_t& aID,
+                                      nsString* aURL,
+                                      nsString* aDocType,
+                                      nsString* aMimeType) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
   Accessible* IdToAccessibleLink(const uint64_t& aID) const;
   Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
   TableCellAccessible* IdToTableCellAccessible(const uint64_t& aID) const;
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -4,18 +4,18 @@
  * 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 protocol PContent;
 
 include "mozilla/GfxMessageUtils.h";
 
 using struct nsIntPoint from "nsRect.h";
-using struct nsIntSize from "nsRect.h";
 using struct nsIntRect from "nsRect.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   uint32_t Role;
@@ -126,17 +126,17 @@ child:
   prio(high) sync ReplaceText(uint64_t aID, nsString aText);
   prio(high) sync InsertText(uint64_t aID, nsString aText, int32_t aPosition);
   prio(high) sync CopyText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
   prio(high) sync CutText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
   prio(high) sync DeleteText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
   prio(high) sync PasteText(uint64_t aID, int32_t aPosition);
 
   prio(high) sync ImagePosition(uint64_t aID, uint32_t aCoordType) returns(nsIntPoint aRetVal);
-  prio(high) sync ImageSize(uint64_t aID) returns(nsIntSize aRetVal);
+  prio(high) sync ImageSize(uint64_t aID) returns(IntSize aRetVal);
 
   prio(high) sync StartOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
   prio(high) sync EndOffset(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
   prio(high) sync IsLinkValid(uint64_t aID) returns(bool aRetVal);
   prio(high) sync AnchorCount(uint64_t aID) returns(uint32_t aRetVal, bool aOk);
   prio(high) sync AnchorURIAt(uint64_t aID, uint32_t aIndex) returns(nsCString aURI, bool aOk);
   prio(high) sync AnchorAt(uint64_t aID, uint32_t aIndex) returns(uint64_t aIDOfAnchor, bool aOk);
 
@@ -204,12 +204,18 @@ child:
   prio(high) sync MinValue(uint64_t aID) returns(double aValue);
   prio(high) sync MaxValue(uint64_t aID) returns(double aValue);
   prio(high) sync Step(uint64_t aID) returns(double aStep);
 
   prio(high) sync TakeFocus(uint64_t aID);
   prio(high) sync ChildAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aWhich)
     returns(uint64_t aChild, bool aOk);
   prio(high) sync Bounds(uint64_t aID) returns(nsIntRect aRect);
+
+  prio(high) sync Language(uint64_t aID) returns(nsString aLocale);
+  prio(high) sync DocType(uint64_t aID) returns(nsString aType);
+  prio(high) sync URL(uint64_t aID) returns(nsString aURL);
+  prio(high) sync MimeType(uint64_t aID) returns(nsString aMime);
+  prio(high) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -906,10 +906,41 @@ ProxyAccessible::ChildAtPoint(int32_t aX
 nsIntRect
 ProxyAccessible::Bounds()
 {
   nsIntRect rect;
   unused << mDoc->SendBounds(mID, &rect);
   return rect;
 }
 
+void
+ProxyAccessible::Language(nsString& aLocale)
+{
+  unused << mDoc->SendLanguage(mID, &aLocale);
+}
+
+void
+ProxyAccessible::DocType(nsString& aType)
+{
+  unused << mDoc->SendDocType(mID, &aType);
+}
+
+void
+ProxyAccessible::URL(nsString& aURL)
+{
+  unused << mDoc->SendURL(mID, &aURL);
+}
+
+void
+ProxyAccessible::MimeType(nsString aMime)
+{
+  unused << mDoc->SendMimeType(mID, &aMime);
+}
+
+void
+ProxyAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
+                                    nsString& aMimeType)
+{
+  unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType);
+}
+
 }
 }
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -271,16 +271,23 @@ public:
   double MaxValue();
   double Step();
 
   void TakeFocus();
   ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
                                 Accessible::EWhichChildAtPoint aWhichChild);
   nsIntRect Bounds();
 
+  void Language(nsString& aLocale);
+  void DocType(nsString& aType);
+  void URL(nsString& aURL);
+  void MimeType(nsString aMime);
+  void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
+                          nsString& aMimeType);
+
   /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -535,18 +535,17 @@ struct RoleDescrComparator
 }
 
 - (id)value
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   nsAutoString value;
   mGeckoAccessible->Value(value);
-  return value.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.BeginReading())
-                                                         length:value.Length()];
+  return nsCocoaUtils::ToNSString(value);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)valueDidChange
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
@@ -580,18 +579,17 @@ struct RoleDescrComparator
 }
 
 - (NSString*)help
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   nsAutoString helpText;
   mGeckoAccessible->Help(helpText);
-  return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(helpText.BeginReading())
-                                                            length:helpText.Length()];
+  return nsCocoaUtils::ToNSString(helpText);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 // objc-style description (from NSObject); not to be confused with the accessible description above.
 - (NSString*)description
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -91,16 +91,17 @@ const ROLE_PAGETABLIST = nsIAccessibleRo
 const ROLE_PANE = nsIAccessibleRole.ROLE_PANE;
 const ROLE_PARAGRAPH = nsIAccessibleRole.ROLE_PARAGRAPH;
 const ROLE_PARENT_MENUITEM = nsIAccessibleRole.ROLE_PARENT_MENUITEM;
 const ROLE_PASSWORD_TEXT = nsIAccessibleRole.ROLE_PASSWORD_TEXT;
 const ROLE_PROGRESSBAR = nsIAccessibleRole.ROLE_PROGRESSBAR;
 const ROLE_PROPERTYPAGE = nsIAccessibleRole.ROLE_PROPERTYPAGE;
 const ROLE_PUSHBUTTON = nsIAccessibleRole.ROLE_PUSHBUTTON;
 const ROLE_RADIOBUTTON = nsIAccessibleRole.ROLE_RADIOBUTTON;
+const ROLE_RADIO_GROUP = nsIAccessibleRole.ROLE_RADIO_GROUP;
 const ROLE_RADIO_MENU_ITEM = nsIAccessibleRole.ROLE_RADIO_MENU_ITEM;
 const ROLE_RICH_OPTION = nsIAccessibleRole.ROLE_RICH_OPTION;
 const ROLE_ROW = nsIAccessibleRole.ROLE_ROW;
 const ROLE_ROWHEADER = nsIAccessibleRole.ROLE_ROWHEADER;
 const ROLE_SCROLLBAR = nsIAccessibleRole.ROLE_SCROLLBAR;
 const ROLE_SECTION = nsIAccessibleRole.ROLE_SECTION;
 const ROLE_SEPARATOR = nsIAccessibleRole.ROLE_SEPARATOR;
 const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
--- a/accessible/tests/mochitest/role/test_aria.html
+++ b/accessible/tests/mochitest/role/test_aria.html
@@ -46,17 +46,17 @@
       testRole("aria_menubar", ROLE_MENUBAR);
       testRole("aria_menuitem", ROLE_MENUITEM);
       testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM);
       testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM);
       testRole("aria_note", ROLE_NOTE);
       testRole("aria_presentation", ROLE_TEXT_CONTAINER); // weak role
       testRole("aria_progressbar", ROLE_PROGRESSBAR);
       testRole("aria_radio", ROLE_RADIOBUTTON);
-      testRole("aria_radiogroup", ROLE_GROUPING);
+      testRole("aria_radiogroup", ROLE_RADIO_GROUP);
       testRole("aria_region", ROLE_PANE);
       testRole("aria_row", ROLE_ROW);
       testRole("aria_rowheader", ROLE_ROWHEADER);
       testRole("aria_scrollbar", ROLE_SCROLLBAR);
       testRole("aria_searchbox", ROLE_ENTRY);
       testRole("aria_separator", ROLE_SEPARATOR);
       testRole("aria_slider", ROLE_SLIDER);
       testRole("aria_spinbutton", ROLE_SPINBUTTON);
--- a/accessible/tests/mochitest/tree/test_formctrl.xul
+++ b/accessible/tests/mochitest/tree/test_formctrl.xul
@@ -34,17 +34,17 @@
         role: ROLE_CHECKBUTTON,
         children: [ ]
       };
 
       testAccessibleTree("checkbox", accTree);
 
       // radiogroup
       accTree = {
-        role: ROLE_GROUPING,
+        role: ROLE_RADIO_GROUP,
         children: [
           {
             role: ROLE_RADIOBUTTON,
             children: [ ]
           },
           {
             role: ROLE_RADIOBUTTON,
             children: [ ]
--- a/accessible/windows/msaa/IUnknownImpl.h
+++ b/accessible/windows/msaa/IUnknownImpl.h
@@ -46,23 +46,23 @@ private:
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #define DECL_IUNKNOWN                                                          \
 public:                                                                        \
   virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**);            \
-  virtual ULONG STDMETHODCALLTYPE AddRef() final                           \
+  virtual ULONG STDMETHODCALLTYPE AddRef() final                               \
   {                                                                            \
     MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");                       \
     ++mRefCnt;                                                                 \
     return mRefCnt;                                                            \
   }                                                                            \
-  virtual ULONG STDMETHODCALLTYPE Release() final                          \
+  virtual ULONG STDMETHODCALLTYPE Release() final                              \
   {                                                                            \
      MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                          \
      --mRefCnt;                                                                \
      if (mRefCnt)                                                              \
        return mRefCnt;                                                         \
                                                                                \
      delete this;                                                              \
      return 0;                                                                 \
--- a/accessible/windows/msaa/nsWinUtils.cpp
+++ b/accessible/windows/msaa/nsWinUtils.cpp
@@ -4,16 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWinUtils.h"
 
 #include "Compatibility.h"
 #include "DocAccessible.h"
+#include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 
 #include "mozilla/Preferences.h"
 #include "nsArrayUtils.h"
 #include "nsIArray.h"
 #include "nsIDocument.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMElement.h"
@@ -53,16 +54,19 @@ nsWinUtils::GetComputedStyleDeclaration(
   return cssDecl.forget();
 }
 
 bool
 nsWinUtils::MaybeStartWindowEmulation()
 {
   // Register window class that'll be used for document accessibles associated
   // with tabs.
+  if (IPCAccessibilityActive())
+    return false;
+
   if (Compatibility::IsJAWS() || Compatibility::IsWE() ||
       Compatibility::IsDolphin() ||
       XRE_GetProcessType() == GeckoProcessType_Content) {
     RegisterNativeWindow(kClassNameTabContent);
     sHWNDCache = new nsRefPtrHashtable<nsPtrHashKey<void>, DocAccessible>(2);
     return true;
   }
 
--- a/accessible/windows/uia/uiaRawElmProvider.h
+++ b/accessible/windows/uia/uiaRawElmProvider.h
@@ -16,17 +16,17 @@ namespace mozilla {
 namespace a11y {
 
 class AccessibleWrap;
 
 /**
  * IRawElementProviderSimple implementation (maintains IAccessibleEx approach).
  */
 class uiaRawElmProvider final : public IAccessibleEx,
-                                    public IRawElementProviderSimple
+                                public IRawElementProviderSimple
 {
 public:
   uiaRawElmProvider(AccessibleWrap* aAcc) : mAcc(aAcc) { }
 
   // IUnknown
   DECL_IUNKNOWN
 
   // IAccessibleEx
--- a/accessible/xul/XULFormControlAccessible.cpp
+++ b/accessible/xul/XULFormControlAccessible.cpp
@@ -467,17 +467,17 @@ XULRadioGroupAccessible::
   XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULSelectControlAccessible(aContent, aDoc)
 { 
 }
 
 role
 XULRadioGroupAccessible::NativeRole()
 {
-  return roles::GROUPING;
+  return roles::RADIO_GROUP;
 }
 
 uint64_t
 XULRadioGroupAccessible::NativeInteractiveState() const
 {
   // The radio group is not focusable. Sometimes the focus controller will
   // report that it is focused. That means that the actual selected radio button
   // should be considered focused.
--- a/addon-sdk/source/lib/sdk/system/events.js
+++ b/addon-sdk/source/lib/sdk/system/events.js
@@ -24,17 +24,17 @@ const Subject = Class({
     // subjects that are one of our wrappers (which we should unwrap
     // when notifying our observers) and those that are real JS XPCOM
     // components (which we should pass through unaltered).
     this.wrappedJSObject = {
       observersModuleSubjectWrapper: true,
       object: object
     };
   },
-  getHelperForLanguage: function() {},
+  getScriptableHelper: function() {},
   getInterfaces: function() {}
 });
 
 function emit(type, event) {
   // From bug 910599
   // We must test to see if 'subject' or 'data' is a defined property
   // of the event object, but also allow primitives to be passed in,
   // which the `in` operator breaks, yet `null` is an object, hence
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -24,16 +24,17 @@ pref("devtools.toolbox.host", "side");
 pref("devtools.toolbox.sidebar.width", 800);
 // Disable session store to ensure having only one tab opened
 pref("browser.sessionstore.max_tabs_undo", 0);
 pref("browser.sessionstore.max_windows_undo", 0);
 pref("browser.sessionstore.restore_on_demand", false);
 pref("browser.sessionstore.resume_from_crash", false);
 // No e10s on mulet
 pref("browser.tabs.remote.autostart.1", false);
+pref("browser.tabs.remote.autostart.2", false);
 #endif
 
 // Bug 945235: Prevent all bars to be considered visible:
 pref("toolkit.defaultChromeFeatures", "chrome,dialog=no,close,resizable,scrollbars,extrachrome");
 
 // Disable focus rings
 pref("browser.display.focus_ring_width", 0);
 
@@ -318,17 +319,17 @@ pref("media.cache_readahead_limit", 30);
 pref("media.fragmented-mp4.gonk.enabled", true);
 #endif
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.decode-only-on-draw.enabled", true);
-pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
+pref("image.mem.allow_locking_in_content_processes", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
 pref("image.mem.surfacecache.discard_factor", 2);  // Discard 1/2 of the surface cache at a time.
 pref("image.mem.surfacecache.min_expiration_ms", 86400000); // 24h, we rely on the out of memory hook
@@ -412,19 +413,19 @@ pref("content.ime.strict_policy", true);
 //
 // $ adb shell stop
 // $ adb shell setprop log.redirect-stdio true
 // $ adb shell start
 pref("browser.dom.window.dump.enabled", false);
 
 // Default Content Security Policy to apply to certified apps.
 // If you change this CSP, make sure to update the fast path in nsCSPService.cpp
-pref("security.apps.certified.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline' app://theme.gaiamobile.org");
+pref("security.apps.certified.CSP.default", "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline' app://theme.gaiamobile.org");
 // Default Content Security Policy to apply to trusted apps.
-pref("security.apps.trusted.CSP.default", "default-src *; object-src 'none'; frame-src 'none'");
+pref("security.apps.trusted.CSP.default", "default-src * data: blob:; object-src 'none'; frame-src 'none'");
 
 // Temporarily force-enable GL compositing.  This is default-disabled
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
 
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
@@ -1032,16 +1033,17 @@ pref("apz.pan_repaint_interval", 16);
 // APZ physics settings, tuned by UX designers
 pref("apz.fling_curve_function_x1", "0.41");
 pref("apz.fling_curve_function_y1", "0.0");
 pref("apz.fling_curve_function_x2", "0.80");
 pref("apz.fling_curve_function_y2", "1.0");
 pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
 pref("apz.fling_friction", "0.0019");
 pref("apz.max_velocity_inches_per_ms", "0.07");
+pref("apz.touch_start_tolerance", "0.1");
 
 // Tweak default displayport values to reduce the risk of running out of
 // memory when zooming in
 pref("apz.x_skate_size_multiplier", "1.25");
 pref("apz.y_skate_size_multiplier", "1.5");
 pref("apz.x_stationary_size_multiplier", "1.5");
 pref("apz.y_stationary_size_multiplier", "1.8");
 pref("apz.enlarge_displayport_when_clipped", true);
--- a/b2g/app/moz.build
+++ b/b2g/app/moz.build
@@ -53,17 +53,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
     OS_LIBS += [
         'ui',
         'EGL',
         'hardware_legacy',
         'hardware',
         'cutils',
     ]
     OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
-    if CONFIG['ANDROID_VERSION'] in ('17', '18', '19', '21'):
+    if CONFIG['ANDROID_VERSION'] in ('17', '18', '19', '21', '22'):
         OS_LIBS += [
             'gui',
             'suspend',
         ]
     OS_LIBS += [
         'binder',
         'utils',
     ]
--- a/b2g/app/ua-update.json.in
+++ b/b2g/app/ua-update.json.in
@@ -12,18 +12,16 @@
   // bug 826514, estadao.com.br
   "estadao.com.br": "\\(Mobile#(Android; Mobile",
   // bug 826711, bb.com.br
   "bb.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827622, bing.com
   "bing.com": "\\(Mobile#(Android; Mobile",
   // bug 827626, magazineluiza.com.br
   "magazineluiza.com.br": "\\(Mobile#(Android; Mobile",
-  // bug 827628, groupon.com.br
-  "groupon.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827633, hao123.com
   "hao123.com": "\\(Mobile#(Android; Mobile",
   // bug 827573, webmotors.com.br
   "webmotors.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827670, elpais.com.co
   "elpais.com.co": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 827674, avianca.com
   "avianca.com": "\\(Mobile#(Android; Mobile",
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -191,29 +191,30 @@ SettingsListener.observe('devtools.overl
   } else {
     if (developerHUD) {
       developerHUD.uninit();
     }
   }
 });
 
 #ifdef MOZ_WIDGET_GONK
+
 let LogShake;
-SettingsListener.observe('devtools.logshake', false, (value) => {
+(function() {
+  let scope = {};
+  Cu.import('resource://gre/modules/LogShake.jsm', scope);
+  LogShake = scope.LogShake;
+  LogShake.init();
+})();
+
+SettingsListener.observe('devtools.logshake', false, value => {
   if (value) {
-    if (!LogShake) {
-      let scope = {};
-      Cu.import('resource://gre/modules/LogShake.jsm', scope);
-      LogShake = scope.LogShake;
-    }
-    LogShake.init();
+    LogShake.enableDeviceMotionListener();
   } else {
-    if (LogShake) {
-      LogShake.uninit();
-    }
+    LogShake.disableDeviceMotionListener();
   }
 });
 #endif
 
 // =================== Device Storage ====================
 SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
   if (Services.prefs.getPrefType('device.storage.writable.name') != Ci.nsIPrefBranch.PREF_STRING) {
     // We clear the pref because it used to be erroneously written as a bool
--- a/b2g/components/AlertsHelper.jsm
+++ b/b2g/components/AlertsHelper.jsm
@@ -20,16 +20,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage",
+                                   "@mozilla.org/notificationStorage;1",
+                                   "nsINotificationStorage");
+
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"]
          .getService(Ci.nsIMessageListenerManager);
 });
 
 function debug(str) {
   //dump("=*= AlertsHelper.jsm : " + str + "\n");
 }
@@ -152,20 +156,23 @@ let AlertsHelper = {
               timestamp: listener.timestamp,
               data: listener.dataObj
             },
             Services.io.newURI(listener.target, null, null),
             Services.io.newURI(listener.manifestURL, null, null)
           );
         }
       }
+      if (detail.type === kDesktopNotificationClose && listener.dbId) {
+        notificationStorage.delete(listener.manifestURL, listener.dbId);
+      }
     }
 
     // we"re done with this notification
-    if (topic === kTopicAlertFinished) {
+    if (detail.type === kDesktopNotificationClose) {
       delete this._listeners[uid];
     }
   },
 
   registerListener: function(alertId, cookie, alertListener) {
     this._listeners[alertId] = { observer: alertListener, cookie: cookie };
   },
 
@@ -280,16 +287,17 @@ let AlertsHelper = {
     let listener = {
       mm: aMessage.target,
       title: data.title,
       text: data.text,
       manifestURL: details.manifestURL,
       imageURL: data.imageURL,
       lang: details.lang || undefined,
       id: details.id || undefined,
+      dbId: details.dbId || undefined,
       dir: details.dir || undefined,
       tag: details.tag || undefined,
       timestamp: details.timestamp || undefined,
       dataObj: dataObject || undefined
     };
     this.registerAppListener(data.uid, listener);
     this.showNotification(data.imageURL, data.title, data.text,
                           details.textClickable, null, data.uid, details.dir,
--- a/b2g/components/AlertsService.js
+++ b/b2g/components/AlertsService.js
@@ -149,17 +149,16 @@ AlertsService.prototype = {
               clicked: (topic === kTopicAlertClickCallback),
               title: listener.title,
               body: listener.text,
               imageURL: listener.imageURL,
               lang: listener.lang,
               dir: listener.dir,
               id: listener.id,
               tag: listener.tag,
-              dbId: listener.dbId,
               timestamp: listener.timestamp,
               data: listener.dataObj || undefined,
             },
             Services.io.newURI(data.target, null, null),
             Services.io.newURI(listener.manifestURL, null, null)
           );
         }
       }
--- a/b2g/components/LogShake.jsm
+++ b/b2g/components/LogShake.jsm
@@ -51,30 +51,43 @@ function debug(msg) {
 
 /**
  * An empirically determined amount of acceleration corresponding to a
  * shake
  */
 const EXCITEMENT_THRESHOLD = 500;
 const DEVICE_MOTION_EVENT = "devicemotion";
 const SCREEN_CHANGE_EVENT = "screenchange";
+const CAPTURE_LOGS_CONTENT_EVENT = "requestSystemLogs";
 const CAPTURE_LOGS_START_EVENT = "capture-logs-start";
 const CAPTURE_LOGS_ERROR_EVENT = "capture-logs-error";
 const CAPTURE_LOGS_SUCCESS_EVENT = "capture-logs-success";
 
 let LogShake = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
   /**
    * If LogShake is listening for device motion events. Required due to lag
    * between HAL layer of device motion events and listening for device motion
    * events.
    */
   deviceMotionEnabled: false,
 
   /**
+   * We only listen to motion events when the screen is enabled, keep track
+   * of its state.
+   */
+  screenEnabled: true,
+
+  /**
+   * Flag monitoring if the preference to enable shake to capture is
+   * enabled in gaia.
+   */
+  listenToDeviceMotion: true,
+
+  /**
    * If a capture has been requested and is waiting for reads/parsing. Used for
    * debouncing.
    */
   captureRequested: false,
 
   /**
    * Map of files which have log-type information to their parsers
    */
@@ -104,16 +117,17 @@ let LogShake = {
     // }});
 
     // However, the screen is always on when we are being enabled because it is
     // either due to the phone starting up or a user enabling us directly.
     this.handleScreenChangeEvent({ detail: {
       screenEnabled: true
     }});
 
+    SystemAppProxy.addEventListener(CAPTURE_LOGS_CONTENT_EVENT, this, false);
     SystemAppProxy.addEventListener(SCREEN_CHANGE_EVENT, this, false);
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
   },
 
   /**
    * Handle an arbitrary event, passing it along to the proper function
    */
@@ -124,30 +138,46 @@ let LogShake = {
         return;
       }
       this.handleDeviceMotionEvent(event);
       break;
 
     case SCREEN_CHANGE_EVENT:
       this.handleScreenChangeEvent(event);
       break;
+
+    case CAPTURE_LOGS_CONTENT_EVENT:
+      this.startCapture();
+      break;
     }
   },
 
   /**
    * Handle an observation from Services.obs
    */
   observe: function(subject, topic) {
     if (topic === "xpcom-shutdown") {
       this.uninit();
     }
   },
 
+  enableDeviceMotionListener: function() {
+    this.listenToDeviceMotion = true;
+    this.startDeviceMotionListener();
+  },
+
+  disableDeviceMotionListener: function() {
+    this.listenToDeviceMotion = false;
+    this.stopDeviceMotionListener();
+  },
+
   startDeviceMotionListener: function() {
-    if (!this.deviceMotionEnabled) {
+    if (!this.deviceMotionEnabled &&
+        this.listenToDeviceMotion &&
+        this.screenEnabled) {
       SystemAppProxy.addEventListener(DEVICE_MOTION_EVENT, this, false);
       this.deviceMotionEnabled = true;
     }
   },
 
   stopDeviceMotionListener: function() {
     SystemAppProxy.removeEventListener(DEVICE_MOTION_EVENT, this, false);
     this.deviceMotionEnabled = false;
@@ -164,38 +194,43 @@ let LogShake = {
       return;
     }
 
     var acc = event.accelerationIncludingGravity;
 
     var excitement = acc.x * acc.x + acc.y * acc.y + acc.z * acc.z;
 
     if (excitement > EXCITEMENT_THRESHOLD) {
-      if (!this.captureRequested) {
-        this.captureRequested = true;
-        SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_START_EVENT, {});
-        this.captureLogs().then(logResults => {
-          // On resolution send the success event to the requester
-          SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_SUCCESS_EVENT, {
-            logFilenames: logResults.logFilenames,
-            logPrefix: logResults.logPrefix
-          });
-          this.captureRequested = false;
-        },
-        error => {
-          // On an error send the error event
-          SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_ERROR_EVENT, {error: error});
-          this.captureRequested = false;
-        });
-      }
+      this.startCapture();
     }
   },
 
+  startCapture: function() {
+    if (this.captureRequested) {
+      return;
+    }
+    this.captureRequested = true;
+    SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_START_EVENT, {});
+    this.captureLogs().then(logResults => {
+      // On resolution send the success event to the requester
+      SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_SUCCESS_EVENT, {
+        logFilenames: logResults.logFilenames,
+        logPrefix: logResults.logPrefix
+      });
+      this.captureRequested = false;
+    }, error => {
+      // On an error send the error event
+      SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_ERROR_EVENT, {error: error});
+      this.captureRequested = false;
+    });
+  },
+
   handleScreenChangeEvent: function(event) {
-    if (event.detail.screenEnabled) {
+    this.screenEnabled = event.detail.screenEnabled;
+    if (this.screenEnabled) {
       this.startDeviceMotionListener();
     } else {
       this.stopDeviceMotionListener();
     }
   },
 
   /**
    * Captures and saves the current device logs, returning a promise that will
--- a/b2g/components/PaymentProviderStrategy.js
+++ b/b2g/components/PaymentProviderStrategy.js
@@ -6,19 +6,19 @@
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 const PREF_DEBUG = "dom.payment.debug";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
-                                   "@mozilla.org/ril/content-helper;1",
-                                   "nsIIccProvider");
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gRil",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
@@ -143,17 +143,18 @@ PaymentProviderStrategy.prototype = {
   set paymentServiceId(aServiceId) {
     this._settings.paymentServiceId = aServiceId;
   },
 
   get iccInfo() {
     if (!this._iccInfo) {
       this._iccInfo = [];
       for (let i = 0; i < gRil.numRadioInterfaces; i++) {
-        let info = iccProvider.getIccInfo(i);
+        let icc = gIccService.getIccByServiceId(i);
+        let info = icc && icc.iccInfo;
         if (!info) {
           LOGE("Tried to get the ICC info for an invalid service ID " + i);
           continue;
         }
 
         this._iccInfo.push({
           iccId: info.iccid,
           mcc: info.mcc,
--- a/b2g/components/ProcessGlobal.js
+++ b/b2g/components/ProcessGlobal.js
@@ -161,17 +161,18 @@ ProcessGlobal.prototype = {
     }
     case 'console-api-log-event': {
       // Pipe `console` log messages to the nsIConsoleService which
       // writes them to logcat on Gonk.
       let message = subject.wrappedJSObject;
       let args = message.arguments;
       let stackTrace = '';
 
-      if (message.level == 'assert' || message.level == 'error' || message.level == 'trace') {
+      if (message.stacktrace &&
+          (message.level == 'assert' || message.level == 'error' || message.level == 'trace')) {
         stackTrace = Array.map(message.stacktrace, formatStackFrame).join('\n');
       } else {
         stackTrace = formatStackFrame(message);
       }
 
       if (stackTrace) {
         args.push('\n' + stackTrace);
       }
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
@@ -133,12 +133,12 @@
   <project name="platform/frameworks/av" path="frameworks/av" revision="fd359e3a74a658d9eaab1c683440ba5412535777"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="df636d2c31ad4434a7de2565359ad982b3767118"/>
   <project name="platform/system/core" path="system/core" revision="a626f6c0ef9e88586569331bd7387b569eaa5ed2"/>
   <project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="4c59900937dc2e978b7b14b7f1ea617e3d5d550e"/>
-  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="c5206aa084ad36037b8ee4b405a71ec7bd88b41c"/>
+  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="e503b1d14d7fdee532b8f391407299da193c1b2d"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,21 +14,21 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3b3407510d6e2c60242e8b9b5f2bc1783ca0a0e4"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -130,12 +130,12 @@
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="125ccf9bd5986c7728ea44508b3e1d1185ac028b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="b37c91354272b7413a0dc058b7445e677921d39e"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="a227c92e0170bcf2296a63386956946b0dd78ca7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="884626610186b6dbea52cec5194b1c4bcfe1cb98"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="29f9b82faa1af9730f52e933dca848546cbea84c"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,21 +14,21 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3b3407510d6e2c60242e8b9b5f2bc1783ca0a0e4"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
--- a/b2g/config/flame-kk/config.json
+++ b/b2g/config/flame-kk/config.json
@@ -25,17 +25,17 @@
         "{workdir}/.config",
         "{workdir}/sources.xml",
         "{workdir}/profile.sh",
         ["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
         ["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
         ["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
     ],
     "env": {
-        "VARIANT": "user",
+        "VARIANT": "userdebug",
         "MOZILLA_OFFICIAL": "1",
         "MOZ_TELEMETRY_REPORTING": "1",
         "B2G_UPDATE_CHANNEL": "nightly",
         "GAIA_KEYBOARD_LAYOUTS": "en,pt-BR,es,de,fr,pl,zh-Hans-Pinyin,zh-Hant-Zhuyin,en-Dvorak"
     },
     "b2g_manifest": "flame-kk.xml",
     "b2g_manifest_intree": true,
     "additional_source_tarballs": ["backup-flame.tar.xz"],
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
@@ -123,17 +123,17 @@
   <remote fetch="https://git.mozilla.org/external/t2m-foxfone" name="t2m"/>
   <default remote="caf" revision="LNX.LA.3.5.2.1.1" sync-j="4"/>
   <!-- Flame specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="2501e5940ba69ece7654ff85611c76ae5bda299c"/>
   <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="b83fc73de7b64594cd74b33e498bf08332b5d87b"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="0865bc4134b67220df4058625fba29305d6b10c3"/>
   <project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
-  <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="26e78a979f3090dc196219e268467620b6c40ec5"/>
+  <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="d5e53ed6f22fa06052351dc03510af9473af01ea"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="d61fc97258c8b0c362430dd2eb195dcc4d266f14"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>
   <project name="platform_external_libnfc-nci" path="external/libnfc-nci" remote="t2m" revision="4186bdecb4dae911b39a8202252cc2310d91b0be"/>
   <project name="platform_external_libnfc-pn547" path="external/libnfc-pn547" remote="b2g" revision="5bb999b84b8adc14f6bea004d523ba258dea8188"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="65f5144987afff35a932262c0c5fad6ecce0c04a"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="da8e6bc53c8bc669da0bb627904d08aa293f2497"/>
   <project name="platform/frameworks/native" path="frameworks/native" revision="a46a9f1ac0ed5662d614c277cbb14eb3f332f365"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="7196881a0e9dd7bfbbcf0af64c8064e70f0fa094"/>
@@ -141,13 +141,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="2a1ded216a91bf62a72b1640cf01ab4998f37028"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="a74adcf8d88320d936daa8d20ce88ca0107fb916"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="9883ea57b0668d8f60dba025d4522dfa69a1fbfa"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="a558dc844bf5144fc38603fd8f4df8d9557052a5"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="57ee1320ed7b4a1a1274d8f3f6c177cd6b9becb2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
   <project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/>
 </manifest>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
@@ -140,13 +140,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="cd76b19aafd4229ccf83853d02faef8c51ca8b34"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
   <project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
 </manifest>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "9cc496cecc37d7a29f9279827cdf6e4891211f67", 
+        "git_revision": "84cbd4391fb7175d5380fa72c04d68873ce77e6d", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "9e79307fd6bcade07847b92d42948a6a6a334f79", 
+    "revision": "a7d22a159f8c412c7d8ebb371025b17563265c06", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -125,17 +125,17 @@
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Nexus 4 specific things -->
   <project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="b0a528d839cfd9d170d092fe3743b5252b4243a6"/>
   <project name="platform/hardware/qcom/bt" path="hardware/qcom/bt" revision="380945eaa249a2dbdde0daa4c8adb8ca325edba6"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="6f3b0272cefaffeaed2a7d2bb8f633059f163ddc"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="16da8262c997a5a0d797885788a64a0771b26910"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="689b476ba3eb46c34b81343295fe144a0e81a18e"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="84cbd4391fb7175d5380fa72c04d68873ce77e6d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1b1d86462d3150dceacff927536ded9fcc168419"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="b37c91354272b7413a0dc058b7445e677921d39e"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="a227c92e0170bcf2296a63386956946b0dd78ca7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="884626610186b6dbea52cec5194b1c4bcfe1cb98"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="29f9b82faa1af9730f52e933dca848546cbea84c"/>
@@ -151,10 +151,10 @@
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="c43952000d57f08b93a0e4fb77052871ce587976"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="028649652cd8f8f18cfb47d34bd78c435eb030ca"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="758a80fbb178b5663d4edbb46944b2dc553cb1ca"/>
   <project name="platform/hardware/qcom/msm8x74" path="hardware/qcom/msm8x74" revision="aa0124820e22302149b1f2db603a9a72f1972527"/>
   <project name="platform/hardware/qcom/power" path="hardware/qcom/power" revision="37499eb89f31233135ca73b830b067ab24dc1be2"/>
   <project name="platform/hardware/qcom/sensors" path="hardware/qcom/sensors" revision="3724fd91ef5183684d97e2bf1d7ff948faabe090"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2e54754cc0529d26ccac37ed291600048adbf6c0"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="71dfa8228ad0d6cdf6bac0426ac59404ab74b7f3"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="306b3290ea22211397d3daf49a42258f4638e4b7"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb5edebf1c1c17ce7f325df388fed082ca788def"/>
 </manifest>
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +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/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=39.0a1
+MOZ_APP_VERSION=40.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=3.0.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
--- a/b2g/dev/app/mulet.js
+++ b/b2g/dev/app/mulet.js
@@ -12,8 +12,9 @@ pref("browser.sessionstore.resume_from_c
 // Display the devtools on the right of the phone
 pref("devtools.toolbox.host", "side");
 pref("devtools.toolbox.sidebar.width", 800);
 
 // Disable e10s as we don't want to run shell.html,
 // nor the system app OOP, but only inner apps
 pref("browser.tabs.remote.autostart", false);
 pref("browser.tabs.remote.autostart.1", false);
+pref("browser.tabs.remote.autostart.2", false);
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -140,50 +140,48 @@
 @RESPATH@/components/components.manifest
 @RESPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
 #ifdef XP_WIN32
 @BINPATH@/AccessibleMarshal.dll
 #endif
 @RESPATH@/components/accessibility.xpt
 #endif
-@RESPATH@/components/appshell.xpt
-@RESPATH@/components/appstartup.xpt
-@RESPATH@/components/autocomplete.xpt
-@RESPATH@/components/autoconfig.xpt
-@RESPATH@/components/browsercompsbase.xpt
-@RESPATH@/components/browser-element.xpt
-@RESPATH@/components/browser-feeds.xpt
-@RESPATH@/components/caps.xpt
-@RESPATH@/components/chardet.xpt
-@RESPATH@/components/chrome.xpt
-@RESPATH@/components/commandhandler.xpt
-@RESPATH@/components/commandlines.xpt
-@RESPATH@/components/compartments.xpt
-@RESPATH@/components/composer.xpt
-@RESPATH@/components/content_events.xpt
-@RESPATH@/components/content_html.xpt
-@RESPATH@/components/content_xslt.xpt
-@RESPATH@/components/cookie.xpt
-@RESPATH@/components/devtools_security.xpt
-@RESPATH@/components/directory.xpt
-@RESPATH@/components/diskspacewatcher.xpt
-@RESPATH@/components/docshell.xpt
-@RESPATH@/components/dom.xpt
-@RESPATH@/components/dom_activities.xpt
-@RESPATH@/components/dom_apps.xpt
-@RESPATH@/components/dom_audiochannel.xpt
-@RESPATH@/components/dom_base.xpt
-@RESPATH@/components/dom_system.xpt
+@BINPATH@/components/appshell.xpt
+@BINPATH@/components/appstartup.xpt
+@BINPATH@/components/autocomplete.xpt
+@BINPATH@/components/autoconfig.xpt
+@BINPATH@/components/browsercompsbase.xpt
+@BINPATH@/components/browser-element.xpt
+@BINPATH@/components/browser-feeds.xpt
+@BINPATH@/components/caps.xpt
+@BINPATH@/components/chardet.xpt
+@BINPATH@/components/chrome.xpt
+@BINPATH@/components/commandhandler.xpt
+@BINPATH@/components/commandlines.xpt
+@BINPATH@/components/composer.xpt
+@BINPATH@/components/content_events.xpt
+@BINPATH@/components/content_html.xpt
+@BINPATH@/components/content_xslt.xpt
+@BINPATH@/components/cookie.xpt
+@BINPATH@/components/devtools_security.xpt
+@BINPATH@/components/directory.xpt
+@BINPATH@/components/diskspacewatcher.xpt
+@BINPATH@/components/docshell.xpt
+@BINPATH@/components/dom.xpt
+@BINPATH@/components/dom_activities.xpt
+@BINPATH@/components/dom_apps.xpt
+@BINPATH@/components/dom_audiochannel.xpt
+@BINPATH@/components/dom_base.xpt
+@BINPATH@/components/dom_system.xpt
 #ifdef MOZ_WIDGET_GONK
 @RESPATH@/components/dom_wifi.xpt
 @RESPATH@/components/dom_system_gonk.xpt
 #endif
 #ifdef MOZ_B2G_RIL
-@RESPATH@/components/dom_icc.xpt
 @RESPATH@/components/dom_wappush.xpt
 @RESPATH@/components/dom_mobileconnection.xpt
 #endif
 #ifdef MOZ_B2G_BT
 @RESPATH@/components/dom_bluetooth.xpt
 #endif
 #ifdef MOZ_B2G_CAMERA
 @BINPATH@/components/dom_camera.xpt
@@ -213,16 +211,17 @@
 @RESPATH@/components/dom_power.xpt
 @RESPATH@/components/dom_quota.xpt
 @RESPATH@/components/dom_range.xpt
 @RESPATH@/components/dom_security.xpt
 @RESPATH@/components/dom_settings.xpt
 @RESPATH@/components/dom_permissionsettings.xpt
 @RESPATH@/components/dom_sidebar.xpt
 @RESPATH@/components/dom_cellbroadcast.xpt
+@RESPATH@/components/dom_icc.xpt
 @RESPATH@/components/dom_mobilemessage.xpt
 @RESPATH@/components/dom_storage.xpt
 @RESPATH@/components/dom_stylesheets.xpt
 @RESPATH@/components/dom_telephony.xpt
 @RESPATH@/components/dom_threads.xpt
 @RESPATH@/components/dom_traversal.xpt
 @RESPATH@/components/dom_tv.xpt
 @RESPATH@/components/dom_views.xpt
@@ -321,16 +320,17 @@
 @RESPATH@/components/spellchecker.xpt
 @RESPATH@/components/storage.xpt
 @RESPATH@/components/telemetry.xpt
 @RESPATH@/components/toolkit_asyncshutdown.xpt
 @RESPATH@/components/toolkit_filewatcher.xpt
 @RESPATH@/components/toolkit_finalizationwitness.xpt
 @RESPATH@/components/toolkit_formautofill.xpt
 @RESPATH@/components/toolkit_osfile.xpt
+@RESPATH@/components/toolkit_perfmonitoring.xpt
 @RESPATH@/components/toolkit_xulstore.xpt
 @RESPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @RESPATH@/components/toolkitremote.xpt
 #endif
 @RESPATH@/components/txtsvc.xpt
 @RESPATH@/components/txmgr.xpt
 #ifdef MOZ_USE_NATIVE_UCONV
@@ -425,16 +425,19 @@
 @RESPATH@/components/nsSidebar.manifest
 @RESPATH@/components/nsSidebar.js
 @RESPATH@/components/nsAsyncShutdown.manifest
 @RESPATH@/components/nsAsyncShutdown.js
 @RESPATH@/components/htmlMenuBuilder.js
 @RESPATH@/components/htmlMenuBuilder.manifest
 @RESPATH@/components/PresentationDeviceInfoManager.manifest
 @RESPATH@/components/PresentationDeviceInfoManager.js
+@RESPATH@/components/BuiltinProviders.manifest
+@RESPATH@/components/TCPPresentationServer.js
+
 #ifdef MOZ_SECUREELEMENT
 @RESPATH@/components/SecureElement.js
 @RESPATH@/components/SecureElement.manifest
 @RESPATH@/components/UiccConnector.js
 @RESPATH@/components/UiccConnector.manifest
 #endif
 
 ; WiFi, NetworkManager, NetworkStats
@@ -478,16 +481,18 @@
 @RESPATH@/components/ResourceStatsManager.js
 @RESPATH@/components/ResourceStatsManager.manifest
 #endif // MOZ_WIDGET_GONK
 
 ; RIL
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
 @RESPATH@/components/CellBroadcastService.js
 @RESPATH@/components/CellBroadcastService.manifest
+@BINPATH@/components/IccService.js
+@BINPATH@/components/IccService.manifest
 @RESPATH@/components/MmsService.js
 @RESPATH@/components/MmsService.manifest
 @RESPATH@/components/MobileMessageDatabaseService.js
 @RESPATH@/components/MobileMessageDatabaseService.manifest
 #ifndef DISABLE_MOZ_RIL_GEOLOC
 @RESPATH@/components/MobileConnectionService.js
 @RESPATH@/components/MobileConnectionService.manifest
 @RESPATH@/components/RadioInterfaceLayer.js
@@ -945,8 +950,14 @@ bin/libfreebl_32int64_3.so
 [gaia]
 @RESPATH@/gaia/*
 @BINPATH@/b2g-bin@BIN_SUFFIX@
 #endif
 
 #ifdef PACKAGE_MOZTT
 @RESPATH@/fonts/*
 #endif
+
+; media
+#ifdef MOZ_EME
+@RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
+@RESPATH@/gmp-clearkey/0.1/clearkey.info
+#endif
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -2977,10 +2977,19 @@
                       <device>0x9805</device>
                       <device>0x9806</device>
                       <device>0x9807</device>
                   </devices>
             <feature>DIRECT3D_9_LAYERS</feature>      <featureStatus>BLOCKED_DEVICE</featureStatus>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g511">      <os>WINNT 5.1</os>      <vendor>0x8086</vendor>            <feature>DIRECT3D_9_LAYERS, WEBGL_ANGLE</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>6.14.10.5218</driverVersion>      <driverVersionComparator>LESS_THAN</driverVersionComparator>    </gfxBlacklistEntry>
     </gfxItems>
 
+  <certItems>
+        <certItem issuerName="MIGQMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE2MDQGA1UEAxMtQ09NT0RPIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB">
+      <serialNumber>D9UltDPl4XVfSSqQOvdiwQ==</serialNumber>
+    </certItem>
+        <certItem issuerName="MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==">
+      <serialNumber>STMAjg==</serialNumber>
+    </certItem>
+      </certItems>
+
 
 </blocklist>
\ No newline at end of file
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -236,17 +236,17 @@ pref("extensions.dss.enabled", false);  
 pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
                                                 // restart.
 
 pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://browser/locale/browser.properties");
 pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://browser/locale/browser.properties");
 
 pref("lightweightThemes.update.enabled", true);
 pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
-pref("lightweightThemes.recommendedThemes", "[{\"id\":\"recommended-1\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/a-web-browser-renaissance/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.footer.jpg\",\"textcolor\":\"#000000\",\"accentcolor\":\"#f2d9b1\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.preview.jpg\",\"author\":\"Sean.Martell\",\"version\":\"0\"},{\"id\":\"recommended-2\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/space-fantasy/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.footer.jpg\",\"textcolor\":\"#ffffff\",\"accentcolor\":\"#d9d9d9\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.preview.jpg\",\"author\":\"fx5800p\",\"version\":\"1.0\"},{\"id\":\"recommended-3\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/linen-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.footer.png\",\"textcolor\":\"#None\",\"accentcolor\":\"#ada8a8\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.preview.png\",\"author\":\"DVemer\",\"version\":\"1.0\"},{\"id\":\"recommended-4\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/pastel-gradient/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.footer.png\",\"textcolor\":\"#000000\",\"accentcolor\":\"#000000\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.preview.png\",\"author\":\"darrinhenein\",\"version\":\"1.0\"},{\"id\":\"recommended-5\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/carbon-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.footer.png\",\"textcolor\":\"#3b3b3b\",\"accentcolor\":\"#2e2e2e\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.preview.jpg\",\"author\":\"Jaxivo\",\"version\":\"1.0\"}]");
+pref("lightweightThemes.recommendedThemes", "[{\"id\":\"recommended-1\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/a-web-browser-renaissance/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.footer.jpg\",\"textcolor\":\"#000000\",\"accentcolor\":\"#f2d9b1\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.preview.jpg\",\"author\":\"Sean.Martell\",\"version\":\"0\"},{\"id\":\"recommended-2\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/space-fantasy/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.footer.jpg\",\"textcolor\":\"#ffffff\",\"accentcolor\":\"#d9d9d9\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.preview.jpg\",\"author\":\"fx5800p\",\"version\":\"1.0\"},{\"id\":\"recommended-3\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/linen-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.footer.png\",\"accentcolor\":\"#ada8a8\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.preview.png\",\"author\":\"DVemer\",\"version\":\"1.0\"},{\"id\":\"recommended-4\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/pastel-gradient/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.footer.png\",\"textcolor\":\"#000000\",\"accentcolor\":\"#000000\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.preview.png\",\"author\":\"darrinhenein\",\"version\":\"1.0\"},{\"id\":\"recommended-5\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/carbon-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.footer.png\",\"textcolor\":\"#3b3b3b\",\"accentcolor\":\"#2e2e2e\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.preview.jpg\",\"author\":\"Jaxivo\",\"version\":\"1.0\"}]");
 
 pref("browser.eme.ui.enabled", false);
 
 // UI tour experience.
 pref("browser.uitour.enabled", true);
 pref("browser.uitour.loglevel", "Error");
 pref("browser.uitour.requireSecure", true);
 pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
@@ -1059,16 +1059,18 @@ pref("browser.sessionstore.restore_on_de
 // Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
 pref("browser.sessionstore.restore_hidden_tabs", false);
 // If restore_on_demand is set, pinned tabs are restored on startup by default.
 // When set to true, this pref overrides that behavior, and pinned tabs will only
 // be restored when they are focused.
 pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
 // The version at which we performed the latest upgrade backup
 pref("browser.sessionstore.upgradeBackup.latestBuildID", "");
+// How many upgrade backups should be kept
+pref("browser.sessionstore.upgradeBackup.maxUpgradeBackups", 3);
 // End-users should not run sessionstore in debug mode
 pref("browser.sessionstore.debug", false);
 // Forget closed windows/tabs after two weeks
 pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
 
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
 
@@ -1172,16 +1174,18 @@ pref("browser.flash-protected-mode-flip.
 #ifdef XP_MACOSX
 // On mac, the default pref is per-architecture
 pref("dom.ipc.plugins.enabled.i386", true);
 pref("dom.ipc.plugins.enabled.x86_64", true);
 #else
 pref("dom.ipc.plugins.enabled", true);
 #endif
 
+pref("dom.ipc.shims.enabledWarnings", false);
+
 // Start the browser in e10s mode
 pref("browser.tabs.remote.autostart", false);
 pref("browser.tabs.remote.desktopbehavior", true);
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 // When this pref is true the Windows process sandbox will set up dummy
 // interceptions and log to the browser console when calls fail in the sandboxed
 // process and also if they are subsequently allowed by the broker process.
@@ -1359,32 +1363,33 @@ pref("devtools.appmanager.manifestEditor
 // Enable DevTools WebIDE by default
 pref("devtools.webide.enabled", true);
 
 // Toolbox preferences
 pref("devtools.toolbox.footer.height", 250);
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
 pref("devtools.toolbox.selectedTool", "webconsole");
-pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage"]');
+pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage", "rulers"]');
 pref("devtools.toolbox.sideEnabled", true);
 pref("devtools.toolbox.zoomValue", "1");
 pref("devtools.toolbox.splitconsoleEnabled", false);
 pref("devtools.toolbox.splitconsoleHeight", 100);
 
 // Toolbox Button preferences
 pref("devtools.command-button-pick.enabled", true);
 pref("devtools.command-button-frames.enabled", false);
 pref("devtools.command-button-splitconsole.enabled", true);
 pref("devtools.command-button-paintflashing.enabled", false);
 pref("devtools.command-button-tilt.enabled", false);
 pref("devtools.command-button-scratchpad.enabled", false);
 pref("devtools.command-button-responsive.enabled", true);
 pref("devtools.command-button-eyedropper.enabled", false);
 pref("devtools.command-button-screenshot.enabled", false);
+pref("devtools.command-button-rulers.enabled", false);
 
 // Inspector preferences
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
 // What was the last active sidebar in the inspector
 pref("devtools.inspector.activeSidebar", "ruleview");
 // Enable the markup preview
 pref("devtools.inspector.markupPreview", false);
@@ -1401,17 +1406,16 @@ pref("devtools.inspector.showAllAnonymou
 // DevTools default color unit
 pref("devtools.defaultColorUnit", "hex");
 
 // Enable the Responsive UI tool
 pref("devtools.responsiveUI.no-reload-notification", false);
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", true);
-pref("devtools.debugger.chrome-enabled", true);
 pref("devtools.debugger.chrome-debugging-host", "localhost");
 pref("devtools.debugger.chrome-debugging-port", 6080);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-timeout", 20000);
 pref("devtools.debugger.pause-on-exceptions", false);
 pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
@@ -1436,16 +1440,17 @@ pref("devtools.performance.memory.max-lo
 pref("devtools.performance.timeline.hidden-markers", "[]");
 pref("devtools.performance.ui.invert-call-tree", true);
 pref("devtools.performance.ui.invert-flame-graph", false);
 pref("devtools.performance.ui.flatten-tree-recursion", true);
 pref("devtools.performance.ui.show-platform-data", false);
 pref("devtools.performance.ui.show-idle-blocks", true);
 pref("devtools.performance.ui.enable-memory", false);
 pref("devtools.performance.ui.enable-framerate", true);
+pref("devtools.performance.ui.show-jit-optimizations", false);
 
 // The default cache UI setting
 pref("devtools.cache.disabled", false);
 
 // The default service workers UI setting
 pref("devtools.serviceWorkers.testing.enabled", false);
 
 // Enable the Network Monitor
@@ -1462,21 +1467,26 @@ pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
 // Scratchpad settings
 // - recentFileMax: The maximum number of recently-opened files
 //                  stored. Setting this preference to 0 will not
 //                  clear any recent files, but rather hide the
 //                  'Open Recent'-menu.
+// - lineNumbers: Whether to show line numbers or not.
+// - wrapText: Whether to wrap text or not.
 // - showTrailingSpace: Whether to highlight trailing space or not.
-// - enableCodeFolding: Whether to enable code folding or not.
+// - editorFontSize: Editor font size configuration.
 // - enableAutocompletion: Whether to enable JavaScript autocompletion.
 pref("devtools.scratchpad.recentFilesMax", 10);
+pref("devtools.scratchpad.lineNumbers", true);
+pref("devtools.scratchpad.wrapText", false);
 pref("devtools.scratchpad.showTrailingSpace", false);
+pref("devtools.scratchpad.editorFontSize", 12);
 pref("devtools.scratchpad.enableAutocompletion", true);
 
 // Enable the Storage Inspector
 pref("devtools.storage.enabled", false);
 
 // Enable the Style Editor.
 pref("devtools.styleeditor.enabled", true);
 pref("devtools.styleeditor.source-maps-enabled", true);
@@ -1579,16 +1589,17 @@ pref("devtools.hud.loglimit.console", 20
 pref("devtools.eyedropper.zoom", 6);
 
 // The developer tools editor configuration:
 // - tabsize: how many spaces to use when a Tab character is displayed.
 // - expandtab: expand Tab characters to spaces.
 // - keymap: which keymap to use (can be 'default', 'emacs' or 'vim')
 // - autoclosebrackets: whether to permit automatic bracket/quote closing.
 // - detectindentation: whether to detect the indentation from the file
+// - enableCodeFolding: Whether to enable code folding or not.
 pref("devtools.editor.tabsize", 2);
 pref("devtools.editor.expandtab", true);
 pref("devtools.editor.keymap", "default");
 pref("devtools.editor.autoclosebrackets", true);
 pref("devtools.editor.detectindentation", true);
 pref("devtools.editor.enableCodeFolding", true);
 pref("devtools.editor.autocomplete", true);
 
@@ -1839,17 +1850,21 @@ pref("media.gmp-provider.enabled", true)
 pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
 
 #ifdef NIGHTLY_BUILD
 pref("browser.polaris.enabled", false);
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
 
 #ifdef NIGHTLY_BUILD
-pref("browser.tabs.remote.autostart.1", true);
+// At the moment, autostart.2 is used, while autostart.1 is unused.
+// We leave it here set to false to reset users' defaults and allow
+// us to change everybody to true in the future, when desired.
+pref("browser.tabs.remote.autostart.1", false);
+pref("browser.tabs.remote.autostart.2", true);
 #endif
 
 #ifdef NIGHTLY_BUILD
 // Enable e10s add-on interposition by default.
 pref("extensions.interposition.enabled", true);
 pref("extensions.interposition.prefetching", true);
 #endif
 
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -174,27 +174,20 @@ const gXPInstallObserver = {
             this.acceptInstallation = null;
             break;
           case "shown":
             let addonList = document.getElementById("addon-install-confirmation-content");
             while (addonList.firstChild)
               addonList.firstChild.remove();
 
             for (let install of installInfo.installs) {
-              let container = document.createElement("hbox");
               let name = document.createElement("label");
-              let author = document.createElement("label");
               name.setAttribute("value", install.addon.name);
-              author.setAttribute("value", !install.addon.creator ? "" :
-                gNavigatorBundle.getFormattedString("addonConfirmInstall.author", [install.addon.creator]));
               name.setAttribute("class", "addon-install-confirmation-name");
-              author.setAttribute("class", "addon-install-confirmation-author");
-              container.appendChild(name);
-              container.appendChild(author);
-              addonList.appendChild(container);
+              addonList.appendChild(name);
             }
 
             this.acceptInstallation = () => {
               for (let install of installInfo.installs)
                 install.install();
               installInfo = null;
 
               Services.telemetry
@@ -260,17 +253,17 @@ const gXPInstallObserver = {
       });
 
       if (needsRestart) {
         messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
         action = {
           label: gNavigatorBundle.getString("addonInstallRestartButton"),
           accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
           callback: function() {
-            Application.restart();
+            BrowserUtils.restartApplication();
           }
         };
       }
       else {
         messageString = gNavigatorBundle.getString("addonsInstalled");
         action = null;
       }
 
@@ -376,17 +369,17 @@ var LightWeightThemeWebInstaller = {
 
         let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
           [aAddon.name], 1);
 
         let action = {
           label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
           accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
           callback: function () {
-            Application.restart();
+            BrowserUtils.restartApplication();
           }
         };
 
         let options = {
           timeout: Date.now() + 30000
         };
 
         PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
--- a/browser/base/content/browser-eme.js
+++ b/browser/base/content/browser-eme.js
@@ -150,16 +150,28 @@ let gEMEHandler = {
     while (descriptionContainer.childNodes.length) {
       fragment.appendChild(descriptionContainer.childNodes[0]);
     }
 
     box.appendNotification(fragment, notificationId, iconURL, box.PRIORITY_WARNING_MEDIUM,
                            buttons);
   },
   showPopupNotificationForSuccess: function(browser, keySystem) {
+    // We're playing EME content! Remove any "we can't play because..." messages.
+    var box = gBrowser.getNotificationBox(browser);
+    ["drmContentDisabled",
+     "drmContentCDMNotSupported",
+     "drmContentCDMInsufficientVersion",
+     "drmContentCDMInstalling"
+     ].forEach(function (value) {
+        var notification = box.getNotificationWithValue(value);
+        if (notification)
+          box.removeNotification(notification);
+      });
+
     // Don't bother creating it if it's already there:
     if (PopupNotifications.getNotification("drmContentPlaying", browser)) {
       return;
     }
 
     let msgPrefix = "emeNotifications.drmContentPlaying.";
     let msgId = msgPrefix + "message2";
     let btnLabelId = msgPrefix + "button.label";
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -269,35 +269,28 @@ var PlacesCommandHook = {
    *        aBrowser isn't bookmarked yet, defaults to the unfiled root.
    * @param [optional] aShowEditUI
    *        whether or not to show the edit-bookmark UI for the bookmark item
    */  
   bookmarkPage: function PCH_bookmarkPage(aBrowser, aParent, aShowEditUI) {
     var uri = aBrowser.currentURI;
     var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
     if (itemId == -1) {
-      // Copied over from addBookmarkForBrowser:
-      // Bug 52536: We obtain the URL and title from the nsIWebNavigation
-      // associated with a <browser/> rather than from a DOMWindow.
-      // This is because when a full page plugin is loaded, there is
-      // no DOMWindow (?) but information about the loaded document
-      // may still be obtained from the webNavigation.
-      var webNav = aBrowser.webNavigation;
-      var url = webNav.currentURI;
+      // Bug 1148838 - Make this code work for full page plugins.
       var title;
       var description;
       var charset;
       try {
         let isErrorPage = /^about:(neterror|certerror|blocked)/
-                          .test(webNav.document.documentURI);
-        title = isErrorPage ? PlacesUtils.history.getPageTitle(url)
-                            : webNav.document.title;
-        title = title || url.spec;
-        description = PlacesUIUtils.getDescriptionFromDocument(webNav.document);
-        charset = webNav.document.characterSet;
+                          .test(aBrowser.contentDocumentAsCPOW.documentURI);
+        title = isErrorPage ? PlacesUtils.history.getPageTitle(uri)
+                            : aBrowser.contentTitle;
+        title = title || uri.spec;
+        description = PlacesUIUtils.getDescriptionFromDocument(aBrowser.contentDocumentAsCPOW);
+        charset = aBrowser.characterSet;
       }
       catch (e) { }
 
       if (aShowEditUI) {
         // If we bookmark the page here (i.e. page was not "starred" already)
         // but open right into the "edit" state, start batching here, so
         // "Cancel" in that state removes the bookmark.
         StarUI.beginBatch();
@@ -436,20 +429,20 @@ var PlacesCommandHook = {
    *            The nsIURI of the page the feed was attached to
    * @title     title
    *            The title of the feed. Optional.
    * @subtitle  subtitle
    *            A short description of the feed. Optional.
    */
   addLiveBookmark: function PCH_addLiveBookmark(url, feedTitle, feedSubtitle) {
     var feedURI = makeURI(url);
-    
-    var doc = gBrowser.contentDocument;
+
+    var doc = gBrowser.contentDocumentAsCPOW;
     var title = (arguments.length > 1) ? feedTitle : doc.title;
- 
+
     var description;
     if (arguments.length > 2)
       description = feedSubtitle;
     else
       description = PlacesUIUtils.getDescriptionFromDocument(doc);
 
     var toolbarIP = new InsertionPoint(PlacesUtils.toolbarFolderId, -1);
     PlacesUIUtils.showBookmarkDialog({ action: "add"
--- a/browser/base/content/browser-readinglist.js
+++ b/browser/base/content/browser-readinglist.js
@@ -12,16 +12,17 @@ const READINGLIST_COMMAND_ID = "readingL
 let ReadingListUI = {
   /**
    * Frame-script messages we want to listen to.
    * @type {[string]}
    */
   MESSAGES: [
     "ReadingList:GetVisibility",
     "ReadingList:ToggleVisibility",
+    "ReadingList:ShowIntro",
   ],
 
   /**
    * Add-to-ReadingList toolbar button in the URLbar.
    * @type {Element}
    */
   toolbarButton: null,
 
@@ -98,16 +99,17 @@ let ReadingListUI = {
         ReadingList.removeListener(this);
         this.listenerRegistered = false;
       }
 
       this.hideSidebar();
     }
 
     document.getElementById(READINGLIST_COMMAND_ID).setAttribute("hidden", !enabled);
+    document.getElementById(READINGLIST_COMMAND_ID).setAttribute("disabled", !enabled);
   },
 
   /**
    * Show the ReadingList sidebar.
    * @return {Promise}
    */
   showSidebar() {
     if (this.enabled) {
@@ -217,16 +219,24 @@ let ReadingListUI = {
         }
         break;
       }
 
       case "ReadingList:ToggleVisibility": {
         this.toggleSidebar();
         break;
       }
+
+      case "ReadingList:ShowIntro": {
+        if (this.enabled && !Preferences.get("browser.readinglist.introShown", false)) {
+          Preferences.set("browser.readinglist.introShown", true);
+          this.showSidebar();
+        }
+        break;
+      }
     }
   },
 
   /**
    * Handles toolbar button styling based on page proxy state changes.
    *
    * @see SetPageProxyState()
    *
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -144,17 +144,17 @@
     <!-- for both places and non-places, the sidebar lives at
          chrome://browser/content/history/history-panel.xul so there are no
          problems when switching between versions -->
     <broadcaster id="viewHistorySidebar" autoCheck="false" sidebartitle="&historyButton.label;"
                  type="checkbox" group="sidebar"
                  sidebarurl="chrome://browser/content/history/history-panel.xul"
                  oncommand="SidebarUI.toggle('viewHistorySidebar');"/>
 
-    <broadcaster id="readingListSidebar" hidden="true" autoCheck="false"
+    <broadcaster id="readingListSidebar" hidden="true" autoCheck="false" disabled="true"
                  sidebartitle="&readingList.label;" type="checkbox" group="sidebar"
                  sidebarurl="chrome://browser/content/readinglist/sidebar.xhtml"
                  oncommand="SidebarUI.toggle('readingListSidebar');"/>
 
     <broadcaster id="viewWebPanelsSidebar" autoCheck="false"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
                  oncommand="SidebarUI.toggle('viewWebPanelsSidebar');"/>
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -41,16 +41,22 @@
 #main-window[customize-entered] {
   min-width: -moz-fit-content;
 }
 
 searchbar {
   -moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
 }
 
+/* Prevent shrinking the page content to 0 height and width */
+.browserStack > browser {
+  min-height: 25px;
+  min-width: 25px;
+}
+
 .browserStack > browser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-browser");
 }
 
 .browserStack > browser[remote="true"] {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-remote-browser");
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2553,21 +2553,17 @@ let gMenuButtonUpdateBadge = {
     switch (status) {
       case STATE_APPLIED:
       case STATE_APPLIED_SVC:
       case STATE_PENDING:
       case STATE_PENDING_SVC:
         // If the update is successfully applied, or if the updater has fallen back
         // to non-staged updates, add a badge to the hamburger menu to indicate an
         // update will be applied once the browser restarts.
-        let badge = document.getAnonymousElementByAttribute(PanelUI.menuButton,
-                                                            "class",
-                                                            "toolbarbutton-badge");
-        badge.style.backgroundColor = '#74BF43';
-        PanelUI.menuButton.setAttribute("badge", "\u2B06");
+        PanelUI.menuButton.setAttribute("update-status", "succeeded");
 
         let brandBundle = document.getElementById("bundle_brand");
         let brandShortName = brandBundle.getString("brandShortName");
         stringId = "appmenu.restartNeeded.description";
         updateButtonText = gNavigatorBundle.getFormattedString(stringId,
                                                                [brandShortName]);
 
         updateButton.setAttribute("label", updateButtonText);
@@ -2575,16 +2571,17 @@ let gMenuButtonUpdateBadge = {
         updateButton.hidden = false;
 
         PanelUI.panel.addEventListener("popupshowing", this, true);
 
         break;
       case STATE_FAILED:
         // Background update has failed, let's show the UI responsible for
         // prompting the user to update manually.
+        PanelUI.menuButton.setAttribute("update-status", "failed");
         PanelUI.menuButton.setAttribute("badge", "!");
 
         stringId = "appmenu.updateFailed.description";
         updateButtonText = gNavigatorBundle.getString(stringId);
 
         updateButton.setAttribute("label", updateButtonText);
         updateButton.setAttribute("update-status", "failed");
         updateButton.hidden = false;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -656,17 +656,17 @@
 
       <tabs id="tabbrowser-tabs"
             class="tabbrowser-tabs"
             tabbrowser="content"
             flex="1"
             setfocus="false"
             tooltip="tabbrowser-tab-tooltip"
             stopwatchid="FX_TAB_CLICK_MS">
-        <tab class="tabbrowser-tab" selected="true" fadein="true"/>
+        <tab class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
       </tabs>
 
       <toolbarbutton id="new-tab-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&tabCmd.label;"
                      command="cmd_newNavigatorTab"
                      onclick="checkForMiddleClick(this, event);"
                      tooltip="dynamic-shortcut-tooltip"
@@ -829,17 +829,17 @@
                        onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
                 <image id="readinglist-addremove-button"
                        class="urlbar-icon"
                        hidden="true"
                        onclick="ReadingListUI.togglePageByBrowser(gBrowser.selectedBrowser);"/>
                 <image id="reader-mode-button"
                        class="urlbar-icon"
                        hidden="true"
-                       onclick="ReaderParent.toggleReaderMode(event);"/>
+                       onclick="ReaderParent.buttonClick(event);"/>
               </hbox>
               <toolbarbutton id="urlbar-go-button"
                              class="chromeclass-toolbar-additional"
                              onclick="gURLBar.handleCommand(event);"
                              tooltiptext="&goEndCap.tooltip;"/>
               <toolbarbutton id="urlbar-reload-button"
                              class="chromeclass-toolbar-additional"
                              command="Browser:ReloadOrDuplicate"
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -160,31 +160,51 @@ let handleContentContextMenu = function 
 
   let doc = event.target.ownerDocument;
   let docLocation = doc.location.href;
   let charSet = doc.characterSet;
   let baseURI = doc.baseURI;
   let referrer = doc.referrer;
   let referrerPolicy = doc.referrerPolicy;
 
+  // Media related cache info parent needs for saving
+  let contentType = null;
+  let contentDisposition = null;
+  if (event.target.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
+      event.target instanceof Ci.nsIImageLoadingContent &&
+      event.target.currentURI) {
+    try {
+      let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+                                                       .getImgCacheForDocument(doc);
+      let props =
+        imageCache.findEntryProperties(event.target.currentURI);
+      if (props) {
+        contentType = props.get("type", Ci.nsISupportsCString).data;
+        contentDisposition = props.get("content-disposition", Ci.nsISupportsCString).data;
+      }
+    } catch (e) {
+      Cu.reportError(e);
+    }
+  }
+
   if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
     let editFlags = SpellCheckHelper.isEditable(event.target, content);
     let spellInfo;
     if (editFlags &
         (SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
       spellInfo =
         InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
     }
 
     let customMenuItems = PageMenuChild.build(event.target);
     let principal = doc.nodePrincipal;
     sendSyncMessage("contextmenu",
                     { editFlags, spellInfo, customMenuItems, addonInfo,
                       principal, docLocation, charSet, baseURI, referrer,
-                      referrerPolicy },
+                      referrerPolicy, contentType, contentDisposition },
                     { event, popupNode: event.target });
   }
   else {
     // Break out to the parent window and pass the add-on info along
     let browser = docShell.chromeEventHandler;
     let mainWin = browser.ownerDocument.defaultView;
     mainWin.gContextMenuContentData = {
       isRemote: false,
@@ -192,16 +212,18 @@ let handleContentContextMenu = function 
       popupNode: event.target,
       browser: browser,
       addonInfo: addonInfo,
       documentURIObject: doc.documentURIObject,
       docLocation: docLocation,
       charSet: charSet,
       referrer: referrer,
       referrerPolicy: referrerPolicy,
+      contentType: contentType,
+      contentDisposition: contentDisposition,
     };
   }
 }
 
 Cc["@mozilla.org/eventlistenerservice;1"]
   .getService(Ci.nsIEventListenerService)
   .addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
 
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -154,17 +154,17 @@ input[type=button] {
 #newtab-grid[page-disabled] {
   pointer-events: none;
 }
 
 /* CELLS */
 .newtab-cell {
   display: -moz-box;
   height: 180px;
-  margin: 20px 10px 85px;
+  margin: 20px 10px 68px;
   width: 290px;
 }
 
 /* SITES */
 .newtab-site {
   position: relative;
   -moz-box-flex: 1;
   transition: 100ms ease-out;
@@ -221,17 +221,17 @@ input[type=button] {
   display: none;
   margin-left: auto;
   margin-right: auto;
   left: 0;
   top: 215px;
 }
 
 .newtab-suggested-bounds {
-  max-height: 51px; /* 51 / 17 = 3 lines maximum */
+  max-height: 34px; /* 34 / 17 = 2 lines maximum */
 }
 
 .newtab-title {
   left: 0;
   padding-top: 14px;
 }
 
 .newtab-sponsored {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -547,18 +547,17 @@ nsContextMenu.prototype = {
       aNode = gContextMenuContentData.event.target;
       aRangeParent = gContextMenuContentData.event.rangeParent;
       aRangeOffset = gContextMenuContentData.event.rangeOffset;
       editFlags = gContextMenuContentData.editFlags;
     }
 
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (aNode.namespaceURI == xulNS ||
-        aNode.nodeType == Node.DOCUMENT_NODE ||
-        this.isDisabledForEvents(aNode)) {
+        aNode.nodeType == Node.DOCUMENT_NODE) {
       this.shouldDisplay = false;
       return;
     }
 
     // Initialize contextual info.
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
@@ -1316,22 +1315,25 @@ nsContextMenu.prototype = {
   },
 
   // Save URL of the clicked upon image, video, or audio.
   saveMedia: function() {
     var doc =  this.target.ownerDocument;
     if (this.onCanvas) {
       // Bypass cache, since it's a data: URL.
       saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
-                   true, false, BrowserUtils.makeURIFromCPOW(doc.documentURIObject), doc);
+                   true, false, gContextMenuContentData.documentURIObject,
+                   doc);
     }
     else if (this.onImage) {
       urlSecurityCheck(this.mediaURL, this.principal);
+      let uri = gContextMenuContentData.documentURIObject;
       saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
-                   false, BrowserUtils.makeURIFromCPOW(doc.documentURIObject), doc);
+                   false, uri, doc, gContextMenuContentData.contentType,
+                   gContextMenuContentData.contentDisposition);
     }
     else if (this.onVideo || this.onAudio) {
       urlSecurityCheck(this.mediaURL, this.principal);
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
       this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
     }
   },
 
@@ -1526,26 +1528,16 @@ nsContextMenu.prototype = {
     return "contextMenu.target     = " + this.target + "\n" +
            "contextMenu.onImage    = " + this.onImage + "\n" +
            "contextMenu.onLink     = " + this.onLink + "\n" +
            "contextMenu.link       = " + this.link + "\n" +
            "contextMenu.inFrame    = " + this.inFrame + "\n" +
            "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
   },
 
-  isDisabledForEvents: function(aNode) {
-    let ownerDoc = aNode.ownerDocument;
-    return
-      ownerDoc.defaultView &&
-      ownerDoc.defaultView
-              .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-              .getInterface(Components.interfaces.nsIDOMWindowUtils)
-              .isNodeDisabledForEvents(aNode);
-  },
-
   isTargetATextBox: function(node) {
     if (node instanceof HTMLInputElement)
       return node.mozIsTextField(false);
 
     return (node instanceof HTMLTextAreaElement);
   },
 
   // Determines whether or not the separator with the specified ID should be
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -64,16 +64,20 @@
 
 #ifdef E10S_TESTING_ONLY
     <popupnotification id="enable-e10s-notification" hidden="true">
       <popupnotificationcontent orient="vertical"/>
     </popupnotification>
 #endif
 
     <popupnotification id="addon-progress-notification" hidden="true">
+      <popupnotificationcontent orient="vertical">
+        <progressmeter id="addon-progress-notification-progressmeter"/>
+        <label id="addon-progress-notification-progresstext" crop="end"/>
+      </popupnotificationcontent>
       <button id="addon-progress-cancel"
               oncommand="this.parentNode.cancel();"/>
       <button id="addon-progress-accept" disabled="true"/>
     </popupnotification>
 
     <popupnotification id="addon-install-confirmation-notification" hidden="true">
       <popupnotificationcontent id="addon-install-confirmation-content" orient="vertical"/>
       <button id="addon-install-confirmation-cancel"
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -194,16 +194,21 @@ Sanitizer.prototype = {
               cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
           }
         }
         else {
           // Remove everything
           cookieMgr.removeAll();
         }
 
+        // Clear deviceIds. Done asynchronously (returns before complete).
+        let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"]
+                                 .getService(Ci.nsIMediaManagerService);
+        mediaMgr.sanitizeDeviceIds(this.range && this.range[0]);
+
         // Clear plugin data.
         const phInterface = Ci.nsIPluginHost;
         const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
         let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
 
         // Determine age range in seconds. (-1 means clear all.) We don't know
         // that this.range[1] is actually now, so we compute age range based
         // on the lower bound. If this.range results in a negative age, do
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1198,28 +1198,36 @@
                   findBar._findField.getAttribute("focused") == "true");
               }
 
               // If focus is in the tab bar, retain it there.
               if (document.activeElement == oldTab) {
                 // We need to explicitly focus the new tab, because
                 // tabbox.xml does this only in some cases.
                 this.mCurrentTab.focus();
-              } else if (gMultiProcessBrowser) {
+              } else if (gMultiProcessBrowser && document.activeElement !== newBrowser) {
                 // Clear focus so that _adjustFocusAfterTabSwitch can detect if
                 // some element has been focused and respect that.
                 document.activeElement.blur();
               }
 
               if (!gMultiProcessBrowser)
                 this._adjustFocusAfterTabSwitch(this.mCurrentTab);
             }
 
             this.tabContainer._setPositionalAttributes();
 
+            if (!gMultiProcessBrowser) {
+              let event = new CustomEvent("TabSwitchDone", {
+                bubbles: true,
+                cancelable: true
+              });
+              this.dispatchEvent(event);
+            }
+
             if (!aForceUpdate)
               TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
           ]]>
         </body>
       </method>
 
       <method name="_adjustFocusAfterTabSwitch">
         <parameter name="newTab"/>
@@ -2755,31 +2763,34 @@
           this._lastRelatedTab = null;
 
           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
 
           let wasFocused = (document.activeElement == this.mCurrentTab);
 
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
-          this.mCurrentTab._selected = false;
+          this.mCurrentTab._logicallySelected = false;
+          this.mCurrentTab._visuallySelected = false;
 
           // invalidate caches
           this._browsers = null;
           this._visibleTabs = null;
 
           // use .item() instead of [] because dragging to the end of the strip goes out of
           // bounds: .item() returns null (so it acts like appendChild), but [] throws
           this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
 
           for (let i = 0; i < this.tabs.length; i++) {
             this.tabs[i]._tPos = i;
-            this.tabs[i]._selected = false;
+            this.tabs[i]._logicallySelected = false;
+            this.tabs[i]._visuallySelected = false;
           }
-          this.mCurrentTab._selected = true;
+          this.mCurrentTab._logicallySelected = true;
+          this.mCurrentTab._visuallySelected = true;
 
           if (wasFocused)
             this.mCurrentTab.focus();
 
           this.tabContainer._handleTabSelect(false);
 
           if (aTab.pinned)
             this.tabContainer._positionPinnedTabs();
@@ -2861,16 +2872,528 @@
         <parameter name="aTab"/><!-- can be from a different window as well -->
         <body>
           <![CDATA[
             return SessionStore.duplicateTab(window, aTab);
           ]]>
         </body>
       </method>
 
+      <!--
+        The tab switcher is responsible for asynchronously switching
+        tabs in e10s. It waits until the new tab is ready (i.e., the
+        layer tree is available) before switching to it. Then it
+        unloads the layer tree for the old tab.
+
+        The tab switcher is a state machine. For each tab, it
+        maintains state about whether the layer tree for the tab is
+        available, being loaded, being unloaded, or unavailable. It
+        also keeps track of the tab currently being displayed, the tab
+        it's trying to load, and the tab the user has asked to switch
+        to. The switcher object is created upon tab switch. It is
+        released when there are no pending tabs to load or unload.
+
+        The following general principles have guided the design:
+
+        1. We only request one layer tree at a time. If the user
+        switches to a different tab while waiting, we don't request
+        the new layer tree until the old tab has loaded or timed out.
+
+        2. If loading the layers for a tab times out, we show the
+        spinner and possibly request the layer tree for another tab if
+        the user has requested one.
+
+        3. We discard layer trees on a delay. This way, if the user is
+        switching among the same tabs frequently, we don't continually
+        load the same tabs.
+
+        It's important that we always show either the spinner or a tab
+        whose layers are available. Otherwise the compositor will draw
+        an entirely black frame, which is very jarring. To ensure this
+        never happens, we do the following:
+
+        1. When switching away from a tab, we assume the old tab might
+        still be drawn until a MozAfterPaint event occurs. Because
+        layout and compositing happen asynchronously, we don't have
+        any other way of knowing when the switch actually takes
+        place. Therefore, we don't unload the old tab until the next
+        MozAfterPaint event.
+
+        2. Suppose that the user switches from tab A to B and then
+        back to A. Suppose that we ask for tab A's layers to be
+        unloaded via message M1 after the first switch and then
+        request them again via message M2 once the second switch
+        happens. Both loading and unloading of layers happens
+        asynchronously, and this can cause problems. It's possible
+        that the content process publishes one last layer tree before
+        M1 is received. The parent process doesn't know that this
+        layer tree was published before M1 and not after M2, so it
+        will display the tab. However, once M1 arrives, the content
+        process will destroy the layer tree for A and now we will
+        display black for it.
+
+        To counter this problem, we keep tab A in a separate
+        "unloading" state until the layer tree is actually dropped in
+        the compositor thread. While the tab is in the "unloading"
+        state, we're not allowed to request layers for it. Once the
+        layers are dropped in the compositor, an event will fire and
+        we will transition the tab to the "unloaded" state. Then we
+        are free to request the tab's layers again.
+      -->
+      <field name="_switcher">null</field>
+      <method name="_getSwitcher">
+        <body><![CDATA[
+          if (this._switcher) {
+            return this._switcher;
+          }
+
+          let switcher = {
+            // How long to wait for a tab's layers to load. After this
+            // time elapses, we're free to put up the spinner and start
+            // trying to load a different tab.
+            TAB_SWITCH_TIMEOUT: 300 /* ms */,
+
+            // When the user hasn't switched tabs for this long, we unload
+            // layers for all tabs that aren't in use.
+            UNLOAD_DELAY: 300 /* ms */,
+
+            // The next three tabs form the principal state variables.
+            // See the assertions in postActions for their invariants.
+
+            // Tab the user requested most recently.
+            requestedTab: this.selectedTab,
+
+            // Tab we're currently trying to load.
+            loadingTab: null,
+
+            // We show this tab in case the requestedTab hasn't loaded yet.
+            lastVisibleTab: this.selectedTab,
+
+            // Auxilliary state variables:
+
+            visibleTab: this.selectedTab,   // Tab that's on screen.
+            spinnerTab: null,               // Tab showing a spinner.
+            originalTab: this.selectedTab,  // Tab that we started on.
+
+            tabbrowser: this,  // Reference to gBrowser.
+            loadTimer: null,   // TAB_SWITCH_TIMEOUT timer.
+            unloadTimer: null, // UNLOAD_DELAY timer.
+
+            // Map from tabs to STATE_* (below).
+            tabState: new Map(),
+
+            // Set of tabs that might be visible right now. We maintain
+            // this set because we can't be sure when a tab is actually
+            // drawn. A tab is added to this set when we ask to make it
+            // visible. All tabs but the most recently shown tab are
+            // removed from the set upon MozAfterPaint.
+            maybeVisibleTabs: new Set([this.selectedTab]),
+
+            STATE_UNLOADED: 0,
+            STATE_LOADING: 1,
+            STATE_LOADED: 2,
+            STATE_UNLOADING: 3,
+
+            logging: false,
+
+            getTabState: function(tab) {
+              let state = this.tabState.get(tab);
+              if (state === undefined) {
+                return this.STATE_UNLOADED;
+              }
+              return state;
+            },
+
+            setTabState: function(tab, state) {
+              if (state == this.STATE_UNLOADED) {
+                this.tabState.delete(tab);
+              } else {
+                this.tabState.set(tab, state);
+              }
+
+              let browser = tab.linkedBrowser;
+              let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+              if (state == this.STATE_LOADING) {
+                // Ask for a MozLayerTreeReady event.
+                fl.requestNotifyLayerTreeReady();
+                browser.docShellIsActive = true;
+              } else if (state == this.STATE_UNLOADING) {
+                // Ask for MozLayerTreeCleared event.
+                fl.requestNotifyLayerTreeCleared();
+                browser.docShellIsActive = false;
+              }
+            },
+
+            init: function() {
+              this.log("START");
+
+              window.addEventListener("MozAfterPaint", this);
+              window.addEventListener("MozLayerTreeReady", this);
+              window.addEventListener("MozLayerTreeCleared", this);
+              window.addEventListener("TabRemotenessChange", this);
+              this.setTabState(this.requestedTab, this.STATE_LOADED);
+            },
+
+            destroy: function() {
+              clearTimeout(this.unloadTimer);
+              clearTimeout(this.loadTimer);
+
+              window.removeEventListener("MozAfterPaint", this);
+              window.removeEventListener("MozLayerTreeReady", this);
+              window.removeEventListener("MozLayerTreeCleared", this);
+              window.removeEventListener("TabRemotenessChange", this);
+
+              this.tabbrowser._switcher = null;
+            },
+
+            finish: function() {
+              this.log("FINISH");
+
+              this.assert(this.tabbrowser._switcher);
+              this.assert(this.tabbrowser._switcher === this);
+              this.assert(!this.spinnerTab);
+              this.assert(!this.loadTimer);
+              this.assert(!this.loadingTab);
+              this.assert(this.lastVisibleTab === this.requestedTab);
+              this.assert(this.getTabState(this.requestedTab) == this.STATE_LOADED);
+
+              this.destroy();
+
+              let toBrowser = this.requestedTab.linkedBrowser;
+              toBrowser.setAttribute("type", "content-primary");
+
+              this.tabbrowser._adjustFocusAfterTabSwitch(this.requestedTab);
+
+              let fromBrowser = this.originalTab.linkedBrowser;
+              // It's possible that the tab we're switching from closed
+              // before we were able to finalize, in which case, fromBrowser
+              // doesn't exist.
+              if (fromBrowser) {
+                fromBrowser.setAttribute("type", "content-targetable");
+              }
+
+              let event = new CustomEvent("TabSwitchDone", {
+                bubbles: true,
+                cancelable: true
+              });
+              this.tabbrowser.dispatchEvent(event);
+            },
+
+            // This function is called after all the main state changes to
+            // make sure we display the right tab.
+            updateDisplay: function() {
+              // Figure out which tab we actually want visible right now.
+              let showTab = null;
+              if (this.getTabState(this.requestedTab) != this.STATE_LOADED &&
+                  this.lastVisibleTab && this.loadTimer) {
+                // If we can't show the requestedTab, and lastVisibleTab is
+                // available, show it.
+                showTab = this.lastVisibleTab;
+              } else {
+                // Show the requested tab. If it's not available, we'll show the spinner.
+                showTab = this.requestedTab;
+              }
+
+              // Show or hide the spinner as needed.
+              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED;
+              if (!needSpinner && this.spinnerTab) {
+                this.tabbrowser.removeAttribute("pendingpaint");
+                this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
+                this.spinnerTab = null;
+              } else if (needSpinner && this.spinnerTab !== showTab) {
+                if (this.spinnerTab) {
+                  this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
+                }
+                this.spinnerTab = showTab;
+                this.tabbrowser.setAttribute("pendingpaint", "true");
+                this.spinnerTab.linkedBrowser.setAttribute("pendingpaint", "true");
+              }
+
+              // Switch to the tab we've decided to make visible.
+              if (this.visibleTab !== showTab) {
+                this.visibleTab = showTab;
+
+                this.maybeVisibleTabs.add(showTab);
+
+                let tabs = this.tabbrowser.mTabBox.tabs;
+                let tabPanel = this.tabbrowser.mPanelContainer;
+                let showPanel = tabs.getRelatedElement(showTab);
+                let index = Array.indexOf(tabPanel.childNodes, showPanel);
+                if (index != -1) {
+                  this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
+                  tabPanel.setAttribute("selectedIndex", index);
+                  if (showTab === this.requestedTab) {
+                    this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
+                  }
+                }
+
+                // This doesn't necessarily exist if we're a new window and haven't switched tabs yet
+                if (this.lastVisibleTab)
+                  this.lastVisibleTab._visuallySelected = false;
+
+                this.visibleTab._visuallySelected = true;
+              }
+
+              this.lastVisibleTab = this.visibleTab;
+
+            },
+
+            assert: function(cond) {
+              if (!cond) {
+                dump("Assertion failure\n" + Error().stack);
+                throw new Error("Assertion failure");
+              }
+            },
+
+            // We've decided to try to load requestedTab.
+            loadRequestedTab: function() {
+              this.assert(!this.loadTimer);
+
+              // loadingTab can be non-null here if we timed out loading the current tab.
+              // In that case we just overwrite it with a different tab; it's had its chance.
+              this.loadingTab = this.requestedTab;
+              this.log("Loading tab " + this.tinfo(this.loadingTab));
+
+              this.setTabState(this.requestedTab, this.STATE_LOADING);
+              this.loadTimer = setTimeout(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
+            },
+
+            // This function runs before every event. It fixes up the state
+            // to account for closed tabs.
+            preActions: function() {
+              this.assert(this.tabbrowser._switcher);
+              this.assert(this.tabbrowser._switcher === this);
+
+              for (let [tab, state] of this.tabState) {
+                if (!tab.linkedBrowser) {
+                  this.tabState.delete(tab);
+                }
+              }
+
+              if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
+                this.lastVisibleTab = null;
+              }
+              if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
+                this.spinnerTab = null;
+              }
+              if (this.loadingTab && !this.loadingTab.linkedBrowser) {
+                this.loadingTab = null;
+                clearTimeout(this.loadTimer);
+                this.loadTimer = null;
+              }
+            },
+
+            // This code runs after we've responded to an event or requested a new
+            // tab. It's expected that we've already updated all the principal
+            // state variables. This function takes care of updating any auxilliary
+            // state.
+            postActions: function() {
+              // Once we finish loading loadingTab, we null it out. So the state should
+              // always be LOADING.
+              this.assert(!this.loadingTab ||
+                          this.getTabState(this.loadingTab) == this.STATE_LOADING);
+
+              // We guarantee that loadingTab is non-null iff loadTimer is non-null. So
+              // the timer is set only when we're loading something.
+              this.assert(!this.loadTimer || this.loadingTab);
+              this.assert(!this.loadingTab || this.loadTimer);
+
+              // If we're not loading anything, try loading the requested tab.
+              if (!this.loadTimer && this.getTabState(this.requestedTab) == this.STATE_UNLOADED) {
+                this.loadRequestedTab();
+              }
+
+              // See how many tabs still have work to do.
+              let numPending = 0;
+              for (let [tab, state] of this.tabState) {
+                if (state == this.STATE_LOADED && tab !== this.requestedTab) {
+                  numPending++;
+                }
+                if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
+                  numPending++;
+                }
+              }
+
+              this.updateDisplay();
+
+              // It's possible for updateDisplay to trigger one of our own event
+              // handlers, which might cause finish() to already have been called.
+              // Check for that before calling finish() again.
+              if (!this.tabbrowser._switcher) {
+                return;
+              }
+
+              if (numPending == 0) {
+                this.finish();
+              }
+
+              this.logState("done");
+            },
+
+            // Fires when we're ready to unload unused tabs.
+            onUnloadTimeout: function() {
+              this.logState("onUnloadTimeout");
+              this.preActions();
+
+              let numPending = 0;
+
+              // Unload any tabs that can be unloaded.
+              for (let [tab, state] of this.tabState) {
+                if (state == this.STATE_LOADED &&
+                    !this.maybeVisibleTabs.has(tab) &&
+                    tab !== this.lastVisibleTab &&
+                    tab !== this.loadingTab &&
+                    tab !== this.requestedTab)
+                {
+                  this.setTabState(tab, this.STATE_UNLOADING);
+                }
+
+                if (state != this.STATE_UNLOADED && tab !== this.requestedTab) {
+                  numPending++;
+                }
+              }
+
+              if (numPending) {
+                // Keep the timer going since there may be more tabs to unload.
+                this.unloadTimer = setTimeout(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
+              }
+
+              this.postActions();
+            },
+
+            // Fires when an ongoing load has taken too long.
+            onLoadTimeout: function() {
+              this.logState("onLoadTimeout");
+              this.preActions();
+              this.loadTimer = null;
+              this.loadingTab = null;
+              this.postActions();
+            },
+
+            // Fires when the layers become available for a tab.
+            onLayersReady: function(browser) {
+              this.logState("onLayersReady");
+
+              let tab = this.tabbrowser.getTabForBrowser(browser);
+              this.setTabState(tab, this.STATE_LOADED);
+
+              if (this.loadingTab === tab) {
+                clearTimeout(this.loadTimer);
+                this.loadTimer = null;
+                this.loadingTab = null;
+              }
+            },
+
+            // Fires when we paint the screen. Any tab switches we initiated
+            // previously are done, so there's no need to keep the old layers
+            // around.
+            onPaint: function() {
+              this.maybeVisibleTabs.clear();
+            },
+
+            // Called when we're done clearing the layers for a tab.
+            onLayersCleared: function(browser) {
+              this.logState("onLayersCleared");
+
+              let tab = this.tabbrowser.getTabForBrowser(browser);
+              if (tab) {
+                this.setTabState(tab, this.STATE_UNLOADED);
+              }
+            },
+
+            // Called when a tab switches from remote to non-remote. In this case
+            // a MozLayerTreeReady notification that we requested may never fire,
+            // so we need to simulate it.
+            onRemotenessChange: function(tab) {
+              this.logState("onRemotenessChange");
+              if (!tab.linkedBrowser.isRemoteBrowser) {
+                if (this.getTabState(tab) == this.STATE_LOADING) {
+                  this.onLayersReady(tab.linkedBrowser);
+                } else if (this.getTabState(tab) == this.STATE_UNLOADING) {
+                  this.onLayersCleared(tab.linkedBrowser);
+                }
+              }
+            },
+
+            // Called when the user asks to switch to a given tab.
+            requestTab: function(tab) {
+              if (tab === this.requestedTab) {
+                return;
+              }
+
+              this.logState("requestTab " + this.tinfo(tab));
+
+              this.requestedTab = tab;
+
+              this.preActions();
+
+              clearTimeout(this.unloadTimer);
+              this.unloadTimer = setTimeout(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
+
+              this.postActions();
+            },
+
+            handleEvent: function(event) {
+              this.preActions();
+
+              if (event.type == "MozLayerTreeReady") {
+                this.onLayersReady(event.originalTarget);
+              } if (event.type == "MozAfterPaint") {
+                this.onPaint();
+              } else if (event.type == "MozLayerTreeCleared") {
+                this.onLayersCleared(event.originalTarget);
+              } else if (event.type == "TabRemotenessChange") {
+                this.onRemotenessChange(event.target);
+              }
+
+              this.postActions();
+            },
+
+            tinfo: function(tab) {
+              if (tab) {
+                return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
+              } else {
+                return "null";
+              }
+            },
+
+            log: function(s) {
+              if (!this.logging)
+                return;
+              dump(s + "\n");
+            },
+
+            logState: function(prefix) {
+              if (!this.logging)
+                return;
+
+              dump(prefix + " ");
+              for (let i = 0; i < this.tabbrowser.tabs.length; i++) {
+                let tab = this.tabbrowser.tabs[i];
+                let state = this.getTabState(tab);
+
+                dump(i + ":");
+                if (tab === this.lastVisibleTab) dump("V");
+                if (tab === this.loadingTab) dump("L");
+                if (tab === this.requestedTab) dump("R");
+                if (state == this.STATE_LOADED) dump("(+)");
+                if (state == this.STATE_LOADING) dump("(+?)");
+                if (state == this.STATE_UNLOADED) dump("(-)");
+                if (state == this.STATE_UNLOADING) dump("(-?)");
+                dump(" ");
+              }
+              dump("\n");
+            },
+          };
+          this._switcher = switcher;
+          switcher.init();
+          return switcher;
+        ]]></body>
+      </method>
+
       <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
            MAKE SURE TO ADD IT HERE AS WELL. -->
       <property name="canGoBack"
                 onget="return this.mCurrentBrowser.canGoBack;"
                 readonly="true"/>
 
       <property name="canGoForward"
                 onget="return this.mCurrentBrowser.canGoForward;"
@@ -3018,16 +3541,20 @@
       <property name="webProgress"
                 readonly="true"
                 onget="return this.mCurrentBrowser.webProgress"/>
 
       <property name="contentWindow"
                 readonly="true"
                 onget="return this.mCurrentBrowser.contentWindow"/>
 
+      <property name="contentWindowAsCPOW"
+                readonly="true"
+                onget="return this.mCurrentBrowser.contentWindowAsCPOW"/>
+
       <property name="sessionHistory"
                 onget="return this.mCurrentBrowser.sessionHistory;"
                 readonly="true"/>
 
       <property name="markupDocumentViewer"
                 onget="return this.mCurrentBrowser.markupDocumentViewer;"
                 readonly="true"/>
 
@@ -3038,16 +3565,20 @@
       <property name="contentViewerFile"
                 onget="return this.mCurrentBrowser.contentViewerFile;"
                 readonly="true"/>
 
       <property name="contentDocument"
                 onget="return this.mCurrentBrowser.contentDocument;"
                 readonly="true"/>
 
+      <property name="contentDocumentAsCPOW"
+                onget="return this.mCurrentBrowser.contentDocumentAsCPOW;"
+                readonly="true"/>
+
       <property name="contentTitle"
                 onget="return this.mCurrentBrowser.contentTitle;"
                 readonly="true"/>
 
       <property name="contentPrincipal"
                 onget="return this.mCurrentBrowser.contentPrincipal;"
                 readonly="true"/>
 
@@ -3238,16 +3769,18 @@
                                           principal: aMessage.data.principal,
                                           customMenuItems: aMessage.data.customMenuItems,
                                           addonInfo: aMessage.data.addonInfo,
                                           documentURIObject: documentURIObject,
                                           docLocation: aMessage.data.docLocation,
                                           charSet: aMessage.data.charSet,
                                           referrer: aMessage.data.referrer,
                                           referrerPolicy: aMessage.data.referrerPolicy,
+                                          contentType: aMessage.data.contentType,
+                                          contentDisposition: aMessage.data.contentDisposition,
                                         };
               let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
               let event = gContextMenuContentData.event;
               popup.openPopupAtScreen(event.screenX, event.screenY, true);
               break;
             }
             case "DOMWebNotificationClicked": {
               let tab = this.getTabForBrowser(browser);
@@ -3375,188 +3908,44 @@
                               .getService(nsIEventListenerService);
           els.removeSystemEventListener(document, "keydown", this, false);
           els.removeSystemEventListener(document, "keypress", this, false);
           window.removeEventListener("sizemodechange", this, false);
 
           if (gMultiProcessBrowser) {
             messageManager.removeMessageListener("DOMTitleChanged", this);
             messageManager.removeMessageListener("contextmenu", this);
+
+            if (this._switcher) {
+              this._switcher.destroy();
+            }
           }
         ]]>
       </destructor>
 
       <!-- Deprecated stuff, implemented for backwards compatibility. -->
       <method name="enterTabbedMode">
         <body>
-          Application.console.log("enterTabbedMode is an obsolete method and " +
-                                  "will be removed in a future release.");
+          Services.console.logStringMessage("enterTabbedMode is an obsolete method and " +
+                                            "will be removed in a future release.");
         </body>
       </method>
       <field name="mTabbedMode" readonly="true">true</field>
       <method name="setStripVisibilityTo">
         <parameter name="aShow"/>
         <body>
           this.tabContainer.visible = aShow;
         </body>
       </method>
       <method name="getStripVisibility">
         <body>
           return this.tabContainer.visible;
         </body>
       </method>
 
-      <method name="_showBusySpinnerRemoteBrowser">
-        <parameter name="aBrowser"/>
-        <body><![CDATA[
-          aBrowser.setAttribute("pendingpaint", "true");
-          if (this._contentWaitingCount <= 0) {
-            // We are not currently spinning
-            this.setAttribute("pendingpaint", "true");
-            this._contentWaitingCount = 1;
-          } else {
-            this._contentWaitingCount++;
-          }
-        ]]></body>
-      </method>
-
-      <method name="_hideBusySpinnerRemoteBrowser">
-        <parameter name="aBrowser"/>
-        <body><![CDATA[
-          aBrowser.removeAttribute("pendingpaint");
-          this._contentWaitingCount--;
-          if (this._contentWaitingCount <= 0) {
-            this.removeAttribute("pendingpaint");
-          }
-        ]]></body>
-      </method>
-
-      <method name="_prepareForTabSwitch">
-        <parameter name="toTab"/>
-        <parameter name="fromTab"/>
-        <body><![CDATA[
-          const kTabSwitchTimeout = 300;
-          let toBrowser = this.getBrowserForTab(toTab);
-          let fromBrowser = fromTab ? this.getBrowserForTab(fromTab)
-                                    : null;
-
-          // We only want to wait for the MozAfterRemotePaint event if
-          // the tab we're switching to is a remote tab, and if the tab
-          // we're switching to isn't the one we've already got. The latter
-          // case can occur when closing tabs before the currently selected
-          // one.
-          let shouldWait = toBrowser.getAttribute("remote") == "true" &&
-                           toBrowser != fromBrowser;
-
-          let switchPromise;
-
-          if (shouldWait) {
-            let timeoutId;
-            let panels = this.mPanelContainer;
-
-            // Both the timeout and MozAfterPaint promises use this same
-            // logic to determine whether they should carry out the tab
-            // switch, or reject it outright.
-            let attemptTabSwitch = (aResolve, aReject) => {
-              if (this.selectedBrowser == toBrowser) {
-                aResolve();
-              } else {
-                // We switched away or closed the browser before we timed
-                // out. We reject, which will cancel the tab switch.
-                aReject();
-              }
-            };
-
-            let timeoutPromise = new Promise((aResolve, aReject) => {
-              timeoutId = setTimeout(() => {
-                if (toBrowser.isRemoteBrowser) {
-                  // The browser's remoteness could have changed since we
-                  // setTimeout so only show the spinner if it's still remote.
-                  this._showBusySpinnerRemoteBrowser(toBrowser);
-                }
-                attemptTabSwitch(aResolve, aReject);
-              }, kTabSwitchTimeout);
-            });
-
-            let paintPromise = new Promise((aResolve, aReject) => {
-              let onRemotePaint = () => {
-                toBrowser.removeEventListener("MozAfterRemotePaint", onRemotePaint);
-                this._hideBusySpinnerRemoteBrowser(toBrowser);
-                clearTimeout(timeoutId);
-                attemptTabSwitch(aResolve, aReject);
-              };
-              toBrowser.addEventListener("MozAfterRemotePaint", onRemotePaint);
-              toBrowser.QueryInterface(Ci.nsIFrameLoaderOwner)
-                       .frameLoader
-                       .requestNotifyAfterRemotePaint();
-               // We need to activate the docShell on the tab we're switching
-               // to - otherwise, we won't initiate a remote paint request and
-               // therefore we won't get the MozAfterRemotePaint event that we're
-               // waiting for.
-               // Note that this happens, as we require, even if the timeout in the
-               // timeoutPromise triggers before the paintPromise even runs.
-               toBrowser.docShellIsActive = true;
-            });
-
-            switchPromise = Promise.race([paintPromise, timeoutPromise]);
-          } else {
-            // Activate the docShell on the tab we're switching to.
-            toBrowser.docShellIsActive = true;
-
-            // No need to wait - just resolve immediately to do the switch ASAP.
-            switchPromise = Promise.resolve();
-          }
-
-          return switchPromise;
-        ]]></body>
-      </method>
-
-      <method name="_deactivateContent">
-        <parameter name="tab"/>
-        <body><![CDATA[
-          // It's unlikely, yet possible, that while we were waiting
-          // to deactivate this tab, that something closed it and wiped
-          // out the browser. For example, during a tab switch, while waiting
-          // for the MozAfterRemotePaint event to fire, something closes the
-          // original tab that the user had selected. If that's the case, then
-          // there's nothing to deactivate.
-          let browser = this.getBrowserForTab(tab);
-          if (browser && this.selectedBrowser != browser) {
-            browser.docShellIsActive = false;
-          }
-        ]]></body>
-      </method>
-
-      <method name="_finalizeTabSwitch">
-        <parameter name="toTab"/>
-        <parameter name="fromTab"/>
-        <body><![CDATA[
-          this._adjustFocusAfterTabSwitch(toTab);
-          this._deactivateContent(fromTab);
-
-          let toBrowser = this.getBrowserForTab(toTab);
-          toBrowser.setAttribute("type", "content-primary");
-
-          let fromBrowser = this.getBrowserForTab(fromTab);
-          // It's possible that the tab we're switching from closed
-          // before we were able to finalize, in which case, fromBrowser
-          // doesn't exist.
-          if (fromBrowser) {
-            fromBrowser.setAttribute("type", "content-targetable");
-          }
-        ]]></body>
-      </method>
-
-      <method name="_cancelTabSwitch">
-        <parameter name="toTab"/>
-        <body><![CDATA[
-          this._deactivateContent(toTab);
-        ]]></body>
-      </method>
-
       <property name="mContextTab" readonly="true"
                 onget="return TabContextMenu.contextTab;"/>
       <property name="mPrefs" readonly="true"
                 onget="return Services.prefs;"/>
       <property name="mTabContainer" readonly="true"
                 onget="return this.tabContainer;"/>
       <property name="mTabs" readonly="true"
                 onget="return this.tabs;"/>
@@ -5037,52 +5426,71 @@
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content context="tabContextMenu" closetabtext="&closeTab.label;">
       <xul:stack class="tab-stack" flex="1">
-        <xul:hbox xbl:inherits="pinned,selected,titlechanged,fadein"
+        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,fadein"
                   class="tab-background">
-          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-start"/>
-          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-middle"/>
-          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-end"/>
         </xul:hbox>
-        <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                   class="tab-content" align="center">
-          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected"
+          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected,visuallyselected"
                      class="tab-throbber"
                      role="presentation"
                      layer="true" />
-          <xul:image xbl:inherits="src=image,fadein,pinned,selected,busy,crashed"
+          <xul:image xbl:inherits="src=image,fadein,pinned,selected,visuallyselected,busy,crashed"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="crashed,busy"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      anonid="tab-label"
-                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected"
+                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
-                             xbl:inherits="fadein,pinned,selected"
+                             xbl:inherits="fadein,pinned,selected,visuallyselected"
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
+
+      <property name="_selected">
+        <setter>
+          <![CDATA[
+          // in e10s we want to only pseudo-select a tab before its rendering is done, so that
+          // the rest of the system knows that the tab is selected, but we don't want to update its
+          // visual status to selected until after we receive confirmation that its content has painted.
+          this._logicallySelected = val;
+
+          // If we're non-e10s we should update the visual selection as well at the same time
+          if (!gMultiProcessBrowser) {
+            this._visuallySelected = val;
+          }
+
+          return val;
+        ]]>
+        </setter>
+      </property>
+
       <property name="label">
         <getter>
           return this.getAttribute("label");
         </getter>
         <setter>
           this.setAttribute("label", val);
           let event = new CustomEvent("TabLabelModified", {
             bubbles: true,
@@ -5509,47 +5917,27 @@
         <![CDATA[
           if (val < 0 || val >= this.childNodes.length)
             return val;
 
           let toTab = this.getRelatedElement(this.childNodes[val]);
           let fromTab = this._selectedPanel ? this.getRelatedElement(this._selectedPanel)
                                             : null;
 
-          let switchPromise = gBrowser._prepareForTabSwitch(toTab, fromTab);
+          gBrowser._getSwitcher().requestTab(toTab);
 
           var panel = this._selectedPanel;
           var newPanel = this.childNodes[val];
           this._selectedPanel = newPanel;
           if (this._selectedPanel != panel) {
             var event = document.createEvent("Events");
             event.initEvent("select", true, true);
             this.dispatchEvent(event);
 
             this._selectedIndex = val;
-
-            switchPromise.then(() => {
-              // If we cannot find the tabpanel that we were trying to switch to, then
-              // it must have been removed before our Promise could be resolved. In
-              // that case, we just cancel the tab switch.
-              var updatedTabIndex = Array.indexOf(this.childNodes, newPanel);
-              if (updatedTabIndex == -1) {
-                gBrowser._cancelTabSwitch(toTab);
-              } else {
-                this.setAttribute("selectedIndex", updatedTabIndex);
-                gBrowser._finalizeTabSwitch(toTab, fromTab);
-              }
-            }, () => {
-              // If the promise rejected, that means we don't want to actually
-              // flip the deck, so we cancel the tab switch.
-              // We need to nullcheck the method we're about to call because
-              // the binding might be dead at this point.
-              if (gBrowser._cancelTabSwitch)
-                gBrowser._cancelTabSwitch(toTab);
-            });
           }
 
           return val;
         ]]>
         </setter>
       </property>
     </implementation>
   </binding>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -220,17 +220,16 @@ skip-if = e10s
 [browser_bug561636.js]
 [browser_bug562649.js]
 [browser_bug563588.js]
 [browser_bug565575.js]
 skip-if = e10s
 [browser_bug565667.js]
 skip-if = toolkit != "cocoa"
 [browser_bug567306.js]
-skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
 [browser_bug575561.js]
 [browser_bug575830.js]
 skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug577121.js]
 [browser_bug578534.js]
 [browser_bug579872.js]
 [browser_bug580638.js]
 [browser_bug580956.js]
@@ -327,27 +326,27 @@ skip-if = buildapp == 'mulet' || e10s ||
 [browser_fxa_profile_channel.js]
 [browser_gestureSupport.js]
 skip-if = e10s # Bug 863514 - no gesture support.
 [browser_getshortcutoruri.js]
 [browser_hide_removing.js]
 [browser_homeDrop.js]
 skip-if = buildapp == 'mulet'
 [browser_identity_UI.js]
+skip-if = e10s && debug # Seeing lots of timeouts (bug 1095517)
 [browser_keywordBookmarklets.js]
 skip-if = e10s # Bug 1102025 - different principals for the bookmarklet only in e10s mode (unclear if test or 'real' issue)
 [browser_keywordSearch.js]
 skip-if = e10s # Bug 921957 - remote webprogress doesn't supply cancel method on the request object
 [browser_keywordSearch_postData.js]
 [browser_lastAccessedTab.js]
 skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
 [browser_locationBarCommand.js]
-skip-if = os == "linux" || e10s # Linux: Intermittent failures, bug 917535; e10s: Bug 1094252 - Focus issues (There should be no focused element - Got [object XULElement], expected null)
+skip-if = os == "linux" # Linux: Intermittent failures, bug 917535
 [browser_locationBarExternalLoad.js]
-skip-if = e10s
 [browser_menuButtonFitts.js]
 skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
 [browser_middleMouse_noJSPaste.js]
 skip-if = e10s # Bug 921952 - Content:Click event issues
 [browser_minimize.js]
 skip-if = e10s # Bug 1100664 - test directly access content docShells (TypeError: gBrowser.docShell is null)
 [browser_mixedcontent_securityflags.js]
 [browser_notification_tab_switching.js]
@@ -417,17 +416,17 @@ skip-if = buildapp == 'mulet' || e10s
 skip-if = buildapp == 'mulet'
 [browser_tabMatchesInAwesomebar.js]
 [browser_tabMatchesInAwesomebar_perwindowpb.js]
 skip-if = e10s || os == 'linux' # Bug 1093373, bug 1104755
 [browser_tab_detach_restore.js]
 [browser_tab_drag_drop_perwindow.js]
 skip-if = buildapp == 'mulet'
 [browser_tab_dragdrop.js]
-skip-if = buildapp == 'mulet'
+skip-if = buildapp == 'mulet' || (e10s && debug) # Bug 1150036: In e10s, content process crashes, main process leaks!
 [browser_tab_dragdrop2.js]
 skip-if = buildapp == 'mulet'
 [browser_tabbar_big_widgets.js]
 skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
                                        # Disabled on OS X because of bug 967917
 [browser_tabfocus.js]
 [browser_tabkeynavigation.js]
 skip-if = e10s
@@ -457,17 +456,16 @@ skip-if = os == "linux" || e10s # Bug 10
 [browser_urlbarRevert.js]
 skip-if = e10s # Bug 1093941 - ESC reverted the location bar value - Got foobar, expected example.com
 [browser_urlbarSearchSingleWordNotification.js]
 [browser_urlbarStop.js]
 [browser_urlbarTrimURLs.js]
 [browser_urlbar_search_healthreport.js]
 [browser_utilityOverlay.js]
 [browser_visibleFindSelection.js]
-skip-if = e10s # Bug 921935 - focusmanager issues with e10s (test calls waitForFocus)
 [browser_visibleLabel.js]
 [browser_visibleTabs.js]
 [browser_visibleTabs_bookmarkAllPages.js]
 skip-if = true # Bug 1005420 - fails intermittently. also with e10s enabled: bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined'
 [browser_visibleTabs_bookmarkAllTabs.js]
 [browser_visibleTabs_contextMenu.js]
 [browser_visibleTabs_tabPreview.js]
 skip-if = (os == "win" && !debug) || e10s # Bug 1007418
--- a/browser/base/content/test/general/browser_bug567306.js
+++ b/browser/base/content/test/general/browser_bug567306.js
@@ -2,53 +2,49 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const {Ci: interfaces, Cc: classes} = Components;
 
 let Clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
 let HasFindClipboard = Clipboard.supportsFindClipboard();
 
-function test() {
-  waitForExplicitFinish();
+add_task(function* () {
+  let newwindow = yield BrowserTestUtils.openNewBrowserWindow();
 
-  whenNewWindowLoaded(undefined, function (win) {
-    whenDelayedStartupFinished(win, function () {
-      let selectedBrowser = win.gBrowser.selectedBrowser;
-      selectedBrowser.addEventListener("pageshow", function pageshowListener() {
-        selectedBrowser.removeEventListener("pageshow", pageshowListener, true);
-        ok(true, "pageshow listener called: " + win.content.location);
-        waitForFocus(function () {
-          onFocus(win);
-        }, selectedBrowser.contentWindow);
-      }, true);
-      selectedBrowser.loadURI("data:text/html,<h1 id='h1'>Select Me</h1>");
-    });
+  let selectedBrowser = newwindow.gBrowser.selectedBrowser;
+  yield new Promise((resolve, reject) => {
+    selectedBrowser.addEventListener("pageshow", function pageshowListener() {
+      if (selectedBrowser.currentURI.spec == "about:blank")
+        return;
+
+      selectedBrowser.removeEventListener("pageshow", pageshowListener, true);
+      ok(true, "pageshow listener called: " + newwindow.content.location);
+      resolve();
+    }, true);
+    selectedBrowser.loadURI("data:text/html,<h1 id='h1'>Select Me</h1>");
   });
-}
 
-function selectText(win) {
-  let elt = win.document.getElementById("h1");
-  let selection = win.getSelection();
-  let range = win.document.createRange();
-  range.setStart(elt, 0);
-  range.setEnd(elt, 1);
-  selection.removeAllRanges();
-  selection.addRange(range);
-}
+  yield SimpleTest.promiseFocus(newwindow);
+
+  ok(!newwindow.gFindBarInitialized, "find bar is not yet initialized");
+  let findBar = newwindow.gFindBar;
 
-function onFocus(win) {
-  ok(!win.gFindBarInitialized, "find bar is not yet initialized");
-  let findBar = win.gFindBar;
-  selectText(win.content);
+  yield ContentTask.spawn(selectedBrowser, { }, function* () {
+    let elt = content.document.getElementById("h1");
+    let selection = content.getSelection();
+    let range = content.document.createRange();
+    range.setStart(elt, 0);
+    range.setEnd(elt, 1);
+    selection.removeAllRanges();
+    selection.addRange(range);
+  });
 
-  findBar.onFindCommand().then(onInitialized.bind(null, findBar, win));
-}
+  yield findBar.onFindCommand();
 
-function onInitialized(findBar, win) {
   // When the OS supports the Find Clipboard (OSX), the find field value is
   // persisted across Fx sessions, thus not useful to test.
   if (!HasFindClipboard)
     is(findBar._findField.value, "Select Me", "Findbar is initialized with selection");
   findBar.close();
-  win.close();
-  finish();
-}
+  yield promiseWindowClosed(newwindow);
+});
+
--- a/browser/base/content/test/general/browser_fullscreen-window-open.js
+++ b/browser/base/content/test/general/browser_fullscreen-window-open.js
@@ -176,24 +176,22 @@ function test_open_with_pref_to_disable_
 
 // Test for window.open() called from chrome context.
 function test_open_from_chrome() {
   waitForWindowOpenFromChrome({
     message: {
       title: "test_open_from_chrome",
       param: "",
     },
-    finalizeFn: function () {},
-    timeout: 10000,
+    finalizeFn: function () {}
   });
 }
 
 function waitForTabOpen(aOptions) {
   let start = Date.now();
-  let timeout = aOptions.timeout || 5000;
   let message = aOptions.message;
 
   if (!message.title) {
     ok(false, "Can't get message.title.");
     aOptions.finalizeFn();
     runNextTest();
     return;
   }
@@ -202,17 +200,16 @@ function waitForTabOpen(aOptions) {
 
   let onTabOpen = function onTabOpen(aEvent) {
     gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true);
 
     let tab = aEvent.target;
     tab.linkedBrowser.addEventListener("load", function onLoad(ev){
       let browser = ev.currentTarget;
       browser.removeEventListener("load", onLoad, true, true);
-      clearTimeout(onTimeout);
 
       is(browser.contentWindow.document.title, message.title,
          "Opened Tab is expected: " + message.title);
 
       if (aOptions.successFn) {
         aOptions.successFn();
       }
 
@@ -223,39 +220,30 @@ function waitForTabOpen(aOptions) {
   gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true);
 
   let finalize = function () {
     aOptions.finalizeFn();
     info("Finished: " + message.title);
     runNextTest();
   };
 
-  let onTimeout = setTimeout(function(){
-    gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true);
-
-    ok(false, "Timeout: '"+message.title + "'.");
-    finalize();
-  }, timeout);
-
-
   const URI = "data:text/html;charset=utf-8,<!DOCTYPE html><html><head><title>"+
               message.title +
               "<%2Ftitle><%2Fhead><body><%2Fbody><%2Fhtml>";
 
   executeWindowOpenInContent({
     uri: URI,
     title: message.title,
     option: message.param,
   });
 }
 
 
 function waitForWindowOpen(aOptions) {
   let start = Date.now();
-  let timeout = aOptions.timeout || 10000;
   let message = aOptions.message;
   let url = aOptions.url || getBrowserURL();
 
   if (!message.title) {
     ok(false, "Can't get message.title");
     aOptions.finalizeFn();
     runNextTest();
     return;
@@ -265,26 +253,18 @@ function waitForWindowOpen(aOptions) {
 
   let onFinalize = function () {
     aOptions.finalizeFn();
 
     info("Finished: " + message.title);
     runNextTest();
   };
 
-  let onTimeout = setTimeout(function(){
-    Services.wm.removeListener(listener);
-    ok(false, "Fail: '"+message.title + "'.");
-
-    onFinalize();
-  }, timeout);
-
   let listener = new WindowListener(message.title, url, {
     onSuccess: aOptions.successFn,
-    onTimeout: onTimeout,
     onFinalize: onFinalize,
   });
   Services.wm.addListener(listener);
 
   const URI = aOptions.url || "about:blank";
 
   executeWindowOpenInContent({
     uri: URI,
@@ -298,17 +278,16 @@ function executeWindowOpenInContent(aPar
   var testElm = testWindow.document.getElementById("test");
 
   testElm.setAttribute("data-test-param", JSON.stringify(aParam));
   EventUtils.synthesizeMouseAtCenter(testElm, {}, testWindow);
 }
 
 function waitForWindowOpenFromChrome(aOptions) {
   let start = Date.now();
-  let timeout = aOptions.timeout || 10000;
   let message = aOptions.message;
   let url = aOptions.url || getBrowserURL();
 
   if (!message.title) {
     ok(false, "Can't get message.title");
     aOptions.finalizeFn();
     runNextTest();
     return;
@@ -318,66 +297,54 @@ function waitForWindowOpenFromChrome(aOp
 
   let onFinalize = function () {
     aOptions.finalizeFn();
 
     info("Finished: " + message.title);
     runNextTest();
   };
 
-  let onTimeout = setTimeout(function(){
-    Services.wm.removeListener(listener);
-    ok(false, "Fail: '"+message.title + "'.");
-
-    testWindow.close();
-    onFinalize();
-  }, timeout);
-
   let listener = new WindowListener(message.title, url, {
     onSuccess: aOptions.successFn,
-    onTimeout: onTimeout,
     onFinalize: onFinalize,
   });
   Services.wm.addListener(listener);
 
 
   const URI = aOptions.url || "about:blank";
 
   let testWindow = window.open(URI, message.title, message.option);
 }
 
 function WindowListener(aTitle, aUrl, aCallBackObj) {
   this.test_title = aTitle;
   this.test_url = aUrl;
   this.callback_onSuccess = aCallBackObj.onSuccess;
-  this.callBack_onTimeout = aCallBackObj.onTimeout;
   this.callBack_onFinalize = aCallBackObj.onFinalize;
 }
 WindowListener.prototype = {
 
   test_title: null,
   test_url: null,
   callback_onSuccess: null,
-  callBack_onTimeout: null,
   callBack_onFinalize: null,
 
   onOpenWindow: function(aXULWindow) {
     Services.wm.removeListener(this);
 
     let domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindow);
     domwindow.addEventListener("load", function onLoad(aEvent) {
       is(domwindow.document.location.href, this.test_url,
         "Opened Window is expected: "+ this.test_title);
       if (this.callback_onSuccess) {
         this.callback_onSuccess();
       }
 
       domwindow.removeEventListener("load", onLoad, true);
-      clearTimeout(this.callBack_onTimeout);
 
       // wait for trasition to fullscreen on OSX Lion later
       if (isOSX) {
         setTimeout(function(){
           domwindow.close();
           executeSoon(this.callBack_onFinalize);
         }.bind(this), 3000);
       }
--- a/browser/base/content/test/general/browser_locationBarCommand.js
+++ b/browser/base/content/test/general/browser_locationBarCommand.js
@@ -40,46 +40,45 @@ add_task(function* shift_left_click_test
   let win = yield newWindowPromise;
 
   // Wait for the initial browser to load.
   let browser = win.gBrowser.selectedBrowser;
   yield BrowserTestUtils.browserLoaded(browser);
 
   info("URL should be loaded in a new window");
   is(gURLBar.value, "", "Urlbar reverted to original value");
-  is(Services.focus.focusedElement, null, "There should be no focused element");
-  is(Services.focus.focusedWindow, win.gBrowser.contentWindow, "Content window should be focused");
+  let childFocus = yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
+  ok(childFocus, "There should be no focused element");
+  is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
   is(win.gURLBar.textValue, TEST_VALUE, "New URL is loaded in new window");
 
   // Cleanup.
   yield promiseWindowClosed(win);
 });
 
 add_task(function* right_click_test() {
   info("Running test: Right click on go button");
 
   // Add a new tab.
-  let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
-  yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  yield* promiseOpenNewTab();
 
   triggerCommand(true, {button: 2});
 
   // Right click should do nothing (context menu will be shown).
   is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
 
   // Cleanup.
   gBrowser.removeCurrentTab();
 });
 
 add_task(function* shift_accel_left_click_test() {
   info("Running test: Shift+Ctrl/Cmd left click on go button");
 
   // Add a new tab.
-  let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
-  yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  let tab = yield* promiseOpenNewTab();
 
   let loadStartedPromise = promiseLoadStarted();
   triggerCommand(true, {accelKey: true, shiftKey: true});
   yield loadStartedPromise;
 
   // Check the load occurred in a new background tab.
   info("URL should be loaded in a new background tab");
   is(gURLBar.value, "", "Urlbar reverted to original value");
@@ -102,28 +101,28 @@ add_task(function* load_in_current_tab_t
     {desc: "Ctrl/Cmd+Return keypress", event: {accelKey: true}},
     {desc: "Alt+Return keypress in a blank tab", event: {altKey: true}}
   ];
 
   for (let test of tests) {
     info(`Running test: ${test.desc}`);
 
     // Add a new tab.
-    let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
-    yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+    let tab = yield* promiseOpenNewTab();
 
     // Trigger a load and check it occurs in the current tab.
     let loadStartedPromise = promiseLoadStarted();
     triggerCommand(test.click || false, test.event || {});
     yield loadStartedPromise;
 
     info("URL should be loaded in the current tab");
     is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
-    is(Services.focus.focusedElement, null, "There should be no focused element");
-    is(Services.focus.focusedWindow, gBrowser.contentWindow, "Content window should be focused");
+    let childFocus = yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
+    ok(childFocus, "There should be no focused element");
+    is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
     is(gBrowser.selectedTab, tab, "New URL was loaded in the current tab");
 
     // Cleanup.
     gBrowser.removeCurrentTab();
   }
 });
 
 add_task(function* load_in_new_tab_test() {
@@ -131,29 +130,29 @@ add_task(function* load_in_new_tab_test(
     {desc: "Ctrl/Cmd left click on go button", click: true, event: {accelKey: true}},
     {desc: "Alt+Return keypress in a dirty tab", event: {altKey: true}, url: START_VALUE}
   ];
 
   for (let test of tests) {
     info(`Running test: ${test.desc}`);
 
     // Add a new tab.
-    let tab = gBrowser.selectedTab = gBrowser.addTab(test.url || "about:blank");
-    yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+    let tab = yield* promiseOpenNewTab(test.url || "about:blank");
 
     // Trigger a load and check it occurs in the current tab.
-    let tabSelectedPromise = promiseNewTabSelected();
+    let tabSwitchedPromise = promiseNewTabSwitched();
     triggerCommand(test.click || false, test.event || {});
-    yield tabSelectedPromise;
+    yield tabSwitchedPromise;
 
     // Check the load occurred in a new tab.
     info("URL should be loaded in a new focused tab");
-    is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
-    is(Services.focus.focusedElement, null, "There should be no focused element");
-    is(Services.focus.focusedWindow, gBrowser.contentWindow, "Content window should be focused");
+    is(gURLBar.inputField.value, TEST_VALUE, "Urlbar still has the value we entered");
+    let childFocus = yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
+    ok(childFocus, "There should be no focused element");
+    is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
     isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab");
 
     // Cleanup.
     gBrowser.removeCurrentTab();
     gBrowser.removeCurrentTab();
   }
 });
 
@@ -180,21 +179,30 @@ function promiseLoadStarted() {
           gBrowser.removeTabsProgressListener(this);
           resolve();
         }
       }
     });
   });
 }
 
-function promiseNewTabSelected() {
+function* promiseOpenNewTab(url = "about:blank") {
+  let tab = gBrowser.addTab(url);
+  let tabSwitchPromise = promiseNewTabSwitched(tab);
+  gBrowser.selectedTab = tab;
+  yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  yield tabSwitchPromise;
+  return tab;
+}
+
+function promiseNewTabSwitched() {
   return new Promise(resolve => {
-    gBrowser.tabContainer.addEventListener("TabSelect", function onSelect() {
-      gBrowser.tabContainer.removeEventListener("TabSelect", onSelect);
-      resolve();
+    gBrowser.addEventListener("TabSwitchDone", function onSwitch() {
+      gBrowser.removeEventListener("TabSwitchDone", onSwitch);
+      executeSoon(resolve);
     });
   });
 }
 
 function promiseWaitForNewWindow() {
   return new Promise(resolve => {
     let listener = {
       onOpenWindow(xulWindow) {
@@ -207,8 +215,21 @@ function promiseWaitForNewWindow() {
 
       onCloseWindow() {},
       onWindowTitleChange() {}
     };
 
     Services.wm.addListener(listener);
   });
 }
+
+function promiseCheckChildNoFocusedElement(browser)
+{
+  if (!gMultiProcessBrowser) {
+    return Services.focus.focusedElement == null;
+  }
+
+  return ContentTask.spawn(browser, { }, function* () {
+    const fm = Components.classes["@mozilla.org/focus-manager;1"].
+                          getService(Components.interfaces.nsIFocusManager);
+    return fm.focusedElement == null;
+  });
+}
--- a/browser/base/content/test/general/browser_locationBarExternalLoad.js
+++ b/browser/base/content/test/general/browser_locationBarExternalLoad.js
@@ -1,64 +1,71 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function test() {
-  waitForExplicitFinish();
+const url = "data:text/html,<body>hi";
 
-  nextTest();
-}
-
-let urls = [
-  "data:text/html,<body>hi"
-];
+add_task(function*() {
+  yield* testURL(url, urlEnter);
+  yield* testURL(url, urlClick);
+});
 
 function urlEnter(url) {
   gURLBar.value = url;
   gURLBar.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
 }
 
 function urlClick(url) {
   gURLBar.value = url;
   gURLBar.focus();
   let goButton = document.getElementById("urlbar-go-button");
   EventUtils.synthesizeMouseAtCenter(goButton, {});
 }
 
-function nextTest() {
-  let url = urls.shift();
-  if (url) {
-    testURL(url, urlEnter, function () {
-      testURL(url, urlClick, nextTest);
-    });
-  }
-  else
-    finish();
-}
-
-function testURL(url, loadFunc, endFunc) {
-  let tab = gBrowser.selectedTab = gBrowser.addTab();
-  registerCleanupFunction(function () {
-    gBrowser.removeTab(tab);
-  });
-  addPageShowListener(function () {
-    let pagePrincipal = gBrowser.contentPrincipal;
-    loadFunc(url);
-
-    addPageShowListener(function () {
-      let fm = Services.focus;
-      is(fm.focusedElement, null, "should be no focused element");
-      is(fm.focusedWindow, gBrowser.contentWindow, "content window should be focused");
-
-      ok(!gBrowser.contentPrincipal.equals(pagePrincipal),
-         "load of " + url + " by " + loadFunc.name + " should produce a page with a different principal");
-      endFunc();
+function promiseNewTabSwitched() {
+  return new Promise(resolve => {
+    gBrowser.addEventListener("TabSwitchDone", function onSwitch() {
+      gBrowser.removeEventListener("TabSwitchDone", onSwitch);
+      executeSoon(resolve);
     });
   });
 }
 
-function addPageShowListener(func) {
-  gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
-    gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
-    func();
+function testURL(url, loadFunc, endFunc) {
+  let tabSwitchedPromise = promiseNewTabSwitched();
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+  let browser = gBrowser.selectedBrowser;
+
+  let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
+
+  yield tabSwitchedPromise;
+  yield pageshowPromise;
+
+  let pagePrincipal = gBrowser.contentPrincipal;
+  loadFunc(url);
+
+  yield promiseWaitForEvent(browser, "pageshow");
+
+  let focused = yield ContentTask.spawn(browser, { isRemote: gMultiProcessBrowser },
+    function* (arg) {
+      const fm = Components.classes["@mozilla.org/focus-manager;1"].
+                            getService(Components.interfaces.nsIFocusManager);
+      if (fm.focusedElement != null) {
+        return "FAIL - focusedElement not null";
+      }
+
+      if (arg.isRemote) {
+        return fm.activeWindow == content ? "PASS" :
+                                            "FAIL - activeWindow not correct";
+      }
+
+      return "PASS";
   });
+
+  is(focused, "PASS", "should be no focused element");
+  is(document.activeElement, browser, "content window should be focused");
+
+  ok(!gBrowser.contentPrincipal.equals(pagePrincipal),
+     "load of " + url + " by " + loadFunc.name + " should produce a page with a different principal");
+
+  gBrowser.removeTab(tab);
 }
--- a/browser/base/content/test/general/browser_readerMode.js
+++ b/browser/base/content/test/general/browser_readerMode.js
@@ -2,82 +2,87 @@
  * 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/. */
 
 /**
  * Test that the reader mode button appears and works properly on
  * reader-able content, and that ReadingList button can open and close
  * its Sidebar UI.
  */
-const READER_PREF = "reader.parse-on-load.enabled";
-const READING_LIST_PREF = "browser.readinglist.enabled";
+const TEST_PREFS = [
+  ["reader.parse-on-load.enabled", true],
+  ["browser.readinglist.enabled", true],
+  ["browser.readinglist.introShown", false],
+];
 
 const TEST_PATH = "http://example.com/browser/browser/base/content/test/general/";
 
 let readerButton = document.getElementById("reader-mode-button");
 
 add_task(function* () {
   registerCleanupFunction(function() {
-    Services.prefs.clearUserPref(READER_PREF);
-    Services.prefs.clearUserPref(READING_LIST_PREF);
+    // Reset test prefs.
+    TEST_PREFS.forEach(([name, value]) => {
+      Services.prefs.clearUserPref(name);
+    });
     while (gBrowser.tabs.length > 1) {
       gBrowser.removeCurrentTab();
     }
   });
 
-  // Enable the reader mode and ReadingList buttons.
-  Services.prefs.setBoolPref(READER_PREF, true);
-  Services.prefs.setBoolPref(READING_LIST_PREF, true);
+  // Set required test prefs.
+  TEST_PREFS.forEach(([name, value]) => {
+    Services.prefs.setBoolPref(name, value);
+  });
 
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   is_element_hidden(readerButton, "Reader mode button is not present on a new tab");
 
   // Point tab to a test page that is reader-able.
   let url = TEST_PATH + "readerModeArticle.html";
   yield promiseTabLoadEvent(tab, url);
   yield promiseWaitForCondition(() => !readerButton.hidden);
   is_element_visible(readerButton, "Reader mode button is present on a reader-able page");
 
+  // Switch page into reader mode.
   readerButton.click();
   yield promiseTabLoadEvent(tab);
 
   let readerUrl = gBrowser.selectedBrowser.currentURI.spec;
   ok(readerUrl.startsWith("about:reader"), "about:reader loaded after clicking reader mode button");
   is_element_visible(readerButton, "Reader mode button is present on about:reader");
 
   is(gURLBar.value, readerUrl, "gURLBar value is about:reader URL");
   is(gURLBar.textValue, url.substring("http://".length), "gURLBar is displaying original article URL");
 
-  // Readinglist button should be present, and status should be "closed".
+  // Readinglist button should be present, and status should be "openned", as the
+  // first time in readerMode opens the Sidebar ReadingList as a feature introduction.
   let listButton;
   yield promiseWaitForCondition(() =>
     listButton = gBrowser.contentDocument.getElementById("list-button"));
   is_element_visible(listButton, "List button is present on a reader-able page");
-  yield promiseWaitForCondition(() => !listButton.classList.contains("on"));
-  ok(!listButton.classList.contains("on"),
-    "List button should not indicate SideBar-ReadingList open.");
-  ok(!ReadingListUI.isSidebarOpen,
-    "The ReadingListUI should not indicate SideBar-ReadingList open.");
-
-  // After we click ReadingList button, status should be "open".
-  listButton.click();
   yield promiseWaitForCondition(() => listButton.classList.contains("on"));
   ok(listButton.classList.contains("on"),
-    "List button should now indicate SideBar-ReadingList open.");
+    "List button should indicate SideBar-ReadingList open.");
   ok(ReadingListUI.isSidebarOpen,
-    "The ReadingListUI should now indicate SideBar-ReadingList open.");
+    "The ReadingListUI should indicate SideBar-ReadingList open.");
 
-  // Now close the sidebar.
+  // Now close the Sidebar ReadingList.
   listButton.click();
   yield promiseWaitForCondition(() => !listButton.classList.contains("on"));
-  ok(!ReadingListUI.isSidebarOpen, "The sidebar should be closed.");
+  ok(!listButton.classList.contains("on"),
+    "List button should now indicate SideBar-ReadingList closed.");
+  ok(!ReadingListUI.isSidebarOpen,
+    "The ReadingListUI should now indicate SideBar-ReadingList closed.");
 
+  // Switch page back out of reader mode.
   readerButton.click();
   yield promiseTabLoadEvent(tab);
-  is(gBrowser.selectedBrowser.currentURI.spec, url, "Original page loaded after clicking active reader mode button");
+  is(gBrowser.selectedBrowser.currentURI.spec, url,
+    "Original page loaded after clicking active reader mode button");
 
   // Load a new tab that is NOT reader-able.
   let newTab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(newTab, "about:robots");
   yield promiseWaitForCondition(() => readerButton.hidden);
   is_element_hidden(readerButton, "Reader mode button is not present on a non-reader-able page");
 
   // Switch back to the original tab to make sure reader mode button is still visible.
--- a/browser/base/content/test/general/browser_selectTabAtIndex.js
+++ b/browser/base/content/test/general/browser_selectTabAtIndex.js
@@ -1,14 +1,17 @@
 function test() {
   for (let i = 0; i < 9; i++)
     gBrowser.addTab();
 
   var isLinux = navigator.platform.indexOf("Linux") == 0;
   for (let i = 9; i >= 1; i--) {
+    // Make sure the keystroke goes to chrome.
+    document.activeElement.blur();
+
     EventUtils.synthesizeKey(i.toString(), { altKey: isLinux, accelKey: !isLinux });
 
     is(gBrowser.tabContainer.selectedIndex, (i == 9 ? gBrowser.tabs.length : i) - 1,
        (isLinux ? "Alt" : "Accel") + "+" + i + " selects expected tab");
   }
 
   gBrowser.selectTabAtIndex(-3);
   is(gBrowser.tabContainer.selectedIndex, gBrowser.tabs.length - 3,
--- a/browser/base/content/test/general/browser_ssl_error_reports.js
+++ b/browser/base/content/test/general/browser_ssl_error_reports.js
@@ -1,34 +1,36 @@
+"use strict";
+
 let badChainURL = "https://badchain.include-subdomains.pinning.example.com";
 let noCertURL = "https://fail-handshake.example.com";
 let enabledPref = false;
 let automaticPref = false;
 let urlPref = "security.ssl.errorReporting.url";
 let enforcement_level = 1;
 let ROOT = getRootDirectory(gTestPath);
 
-add_task(function*(){
-  waitForExplicitFinish();
-  SimpleTest.requestCompleteLog();
-  yield testSendReportDisabled();
+SimpleTest.requestCompleteLog();
+
+add_task(function* test_send_report_manual_badchain() {
   yield testSendReportManual(badChainURL, "succeed");
+});
+
+add_task(function* test_send_report_manual_nocert() {
   yield testSendReportManual(noCertURL, "nocert");
-  yield testSendReportAuto();
-  yield testSendReportError();
-  yield testSetAutomatic();
 });
 
 // creates a promise of the message in an error page
 function createNetworkErrorMessagePromise(aBrowser) {
-  return new Promise(function(resolve, reject) {
+  let progressListener;
+  let promise = new Promise(function(resolve, reject) {
     // Error pages do not fire "load" events, so use a progressListener.
     let originalDocumentURI = aBrowser.contentDocument.documentURI;
 
-    let progressListener = {
+    progressListener = {
       onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
         // Make sure nothing other than an error page is loaded.
         if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
           reject("location change was not to an error page");
         }
       },
 
       onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
@@ -59,20 +61,29 @@ function createNetworkErrorMessagePromis
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                           Ci.nsISupportsWeakReference])
     };
 
     aBrowser.addProgressListener(progressListener,
             Ci.nsIWebProgress.NOTIFY_LOCATION |
             Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
   });
+
+  // Ensure the weak progress listener is kept alive as long as the promise.
+  createNetworkErrorMessagePromise.listeners.set(promise, progressListener);
+
+  return promise;
 }
 
+// Keep a map of promises to their progress listeners so
+// the weak progress listeners aren't GCed too early.
+createNetworkErrorMessagePromise.listeners = new WeakMap();
+
 // check we can set the 'automatically send' pref
-let testSetAutomatic = function*() {
+add_task(function* test_set_automatic() {
   setup();
   let tab = gBrowser.addTab(badChainURL, {skipAnimation: true});
   let browser = tab.linkedBrowser;
   let mm = browser.messageManager;
   mm.loadFrameScript(ROOT + "browser_ssl_error_reports_content.js", true);
 
   gBrowser.selectedTab = tab;
 
@@ -111,17 +122,17 @@ let testSetAutomatic = function*() {
   });
 
   mm.sendAsyncMessage("ssler-test:SetAutoPref",{value:false});
 
   yield prefDisabled;
 
   gBrowser.removeTab(tab);
   cleanup();
-};
+});
 
 // test that manual report sending (with button clicks) works
 let testSendReportManual = function*(testURL, suffix) {
   setup();
   Services.prefs.setBoolPref("security.ssl.errorReporting.enabled", true);
   Services.prefs.setCharPref("security.ssl.errorReporting.url",
     "https://example.com/browser/browser/base/content/test/general/pinning_reports.sjs?" + suffix);
 
@@ -175,17 +186,17 @@ let testSendReportManual = function*(tes
 
   yield deferredReportSucceeds.promise;
 
   gBrowser.removeTab(tab);
   cleanup();
 };
 
 // test that automatic sending works
-let testSendReportAuto = function*() {
+add_task(function* test_send_report_auto() {
   setup();
   Services.prefs.setBoolPref("security.ssl.errorReporting.enabled", true);
   Services.prefs.setBoolPref("security.ssl.errorReporting.automatic", true);
   Services.prefs.setCharPref("security.ssl.errorReporting.url", "https://example.com/browser/browser/base/content/test/general/pinning_reports.sjs?succeed");
 
   let tab = gBrowser.addTab(badChainURL, {skipAnimation: true});
   let browser = tab.linkedBrowser;
   let mm = browser.messageManager;
@@ -222,20 +233,20 @@ let testSendReportAuto = function*() {
 
   mm.addMessageListener("ssler-test:SSLErrorReportStatus", statusListener);
 
   // Ensure the report is sent with no interaction
   yield deferredReportSucceeds.promise;
 
   gBrowser.removeTab(tab);
   cleanup();
-};
+});
 
 // test that an error is shown if there's a problem with the report server
-let testSendReportError = function*() {
+add_task(function* test_send_report_error() {
   setup();
   // set up prefs so error send is automatic and an error will occur
   Services.prefs.setBoolPref("security.ssl.errorReporting.enabled", true);
   Services.prefs.setBoolPref("security.ssl.errorReporting.automatic", true);
   Services.prefs.setCharPref("security.ssl.errorReporting.url", "https://example.com/browser/browser/base/content/test/general/pinning_reports.sjs?error");
 
   // load the test URL so error page is seen
   let tab = gBrowser.addTab(badChainURL, {skipAnimation: true});
@@ -260,19 +271,19 @@ let testSendReportError = function*() {
     mm.addMessageListener("ssler-test:SSLErrorReportStatus", statusListener);
   });
 
   // check that errors are sent
   yield reportErrors;
 
   gBrowser.removeTab(tab);
   cleanup();
-};
+});
 
-let testSendReportDisabled = function*() {
+add_task(function* test_send_report_disabled() {
   setup();
   Services.prefs.setBoolPref("security.ssl.errorReporting.enabled", false);
   Services.prefs.setCharPref("security.ssl.errorReporting.url", "https://offdomain.com");
 
   let tab = gBrowser.addTab(badChainURL, {skipAnimation: true});
   let browser = tab.linkedBrowser;
   let mm = browser.messageManager;
   mm.loadFrameScript(ROOT + "browser_ssl_error_reports_content.js", true);
@@ -302,17 +313,17 @@ let testSendReportDisabled = function*()
   // click the button
   mm.sendAsyncMessage("ssler-test:SendBtnClick",{forceUI:true});
 
   // check we get an error
   yield reportErrors;
 
   gBrowser.removeTab(tab);
   cleanup();
-};
+});
 
 function setup() {
   // ensure the relevant prefs are set
   enabledPref = Services.prefs.getBoolPref("security.ssl.errorReporting.enabled");
   automaticPref = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
   urlPref = Services.prefs.getCharPref("security.ssl.errorReporting.url");
 
   enforcement_level = Services.prefs.getIntPref("security.cert_pinning.enforcement_level");
--- a/browser/base/content/test/general/browser_tab_dragdrop.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop.js
@@ -27,16 +27,65 @@ let clickTest = Task.async(function*(tab
   is(newClicks, clicks + 1, "adding 1 more click on BODY");
 });
 
 function loadURI(tab, url) {
   tab.linkedBrowser.loadURI(url);
   return BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 }
 
+// Creates a framescript which caches the current object value from the plugin
+// in the page. checkObjectValue below verifies that the framescript is still
+// active for the browser and that the cached value matches that from the plugin
+// in the page which tells us the plugin hasn't been reinitialized.
+function cacheObjectValue(browser) {
+  let frame_script = function() {
+    let plugin = content.document.wrappedJSObject.body.firstChild;
+    let objectValue = plugin.getObjectValue();
+
+    addMessageListener("Test:CheckObjectValue", () => {
+      try {
+        let plugin = content.document.wrappedJSObject.body.firstChild;
+        sendAsyncMessage("Test:CheckObjectValue", {
+          result: plugin.checkObjectValue(objectValue)
+        });
+      }
+      catch (e) {
+        sendAsyncMessage("Test:CheckObjectValue", {
+          result: null,
+          exception: e.toString()
+        });
+      }
+    });
+  };
+
+  browser.messageManager.loadFrameScript("data:,(" + frame_script.toString() + ")();", false)
+}
+
+// See the notes for cacheObjectValue above.
+function checkObjectValue(browser) {
+  let mm = browser.messageManager;
+
+  return new Promise((resolve, reject) => {
+    let listener  = ({ data }) => {
+      mm.removeMessageListener("Test:CheckObjectValue", listener);
+      if (data.result === null) {
+        ok(false, "checkObjectValue threw an exception: " + data.exception);
+        reject(data.exception);
+      }
+      else {
+        resolve(data.result);
+      }
+    };
+
+    mm.addMessageListener("Test:CheckObjectValue", listener);
+    mm.sendAsyncMessage("Test:CheckObjectValue");
+  });
+}
+
 add_task(function*() {
   let embed = '<embed type="application/x-test" allowscriptaccess="always" allowfullscreen="true" wmode="window" width="640" height="480"></embed>'
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
 
   // create a few tabs
   let tabs = [
     gBrowser.tabs[0],
     gBrowser.addTab("about:blank", {skipAnimation: true}),
@@ -52,39 +101,34 @@ add_task(function*() {
   yield loadURI(tabs[4], "data:text/html;charset=utf-8,<body onload='clicks=0' onclick='++clicks'>"+embed);
   gBrowser.selectedTab = tabs[3];
 
   swapTabsAndCloseOther(2, 3); // now: 0 1 2 4
   is(gBrowser.tabs[1], tabs[1], "tab1");
   is(gBrowser.tabs[2], tabs[3], "tab3");
   is(gBrowser.tabs[3], tabs[4], "tab4");
 
-  let plugin = tabs[4].linkedBrowser.contentDocument.wrappedJSObject.body.firstChild;
-  let tab4_plugin_object = plugin.getObjectValue();
+  cacheObjectValue(tabs[4].linkedBrowser);
 
   swapTabsAndCloseOther(3, 2); // now: 0 1 4
   gBrowser.selectedTab = gBrowser.tabs[2];
 
-  let doc = gBrowser.tabs[2].linkedBrowser.contentDocument.wrappedJSObject;
-  plugin = doc.body.firstChild;
-  ok(plugin && plugin.checkObjectValue(tab4_plugin_object), "same plugin instance");
+  ok((yield checkObjectValue(gBrowser.tabs[2].linkedBrowser)), "same plugin instance");
 
   is(gBrowser.tabs[1], tabs[1], "tab1");
   is(gBrowser.tabs[2], tabs[3], "tab4");
 
   let clicks = yield getClicks(gBrowser.tabs[2]);
   is(clicks, 0, "no click on BODY so far");
   yield clickTest(gBrowser.tabs[2]);
 
   swapTabsAndCloseOther(2, 1); // now: 0 4
   is(gBrowser.tabs[1], tabs[1], "tab1");
 
-  doc = gBrowser.tabs[1].linkedBrowser.contentDocument.wrappedJSObject;
-  plugin = doc.body.firstChild;
-  ok(plugin && plugin.checkObjectValue(tab4_plugin_object), "same plugin instance");
+  ok((yield checkObjectValue(gBrowser.tabs[1].linkedBrowser)), "same plugin instance");
 
   yield clickTest(gBrowser.tabs[1]);
 
   // Load a new document (about:blank) in tab4, then detach that tab into a new window.
   // In the new window, navigate back to the original document and click on its <body>,
   // verify that its onclick was called.
   gBrowser.selectedTab = tabs[1];
   yield loadURI(tabs[1], "about:blank");
--- a/browser/base/content/test/general/browser_tabfocus.js
+++ b/browser/base/content/test/general/browser_tabfocus.js
@@ -211,37 +211,36 @@ add_task(function*() {
   // When focus is in the tab bar, it should be retained there
   yield expectFocusShift(function () gBrowser.selectedTab.focus(),
                          "main-window", "tab2", true,
                          "focusing tab element");
   yield expectFocusShift(function () gBrowser.selectedTab = tab1,
                          "main-window", "tab1", true,
                          "tab change when selected tab element was focused");
 
-  let paintWaiter;
+  let switchWaiter;
   if (gMultiProcessBrowser) {
-    paintWaiter = new Promise((resolve, reject) => {
-      browser2.addEventListener("MozAfterRemotePaint", function paintListener() {
-        browser2.removeEventListener("MozAfterRemotePaint", paintListener, false);
+    switchWaiter = new Promise((resolve, reject) => {
+      gBrowser.addEventListener("TabSwitchDone", function listener() {
+        gBrowser.removeEventListener("TabSwitchDone", listener);
         executeSoon(resolve);
-      }, false);
-      browser2.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.requestNotifyAfterRemotePaint();
+      });
     });
   }
 
   yield expectFocusShift(function () gBrowser.selectedTab = tab2,
                          "main-window", "tab2", true,
                          "another tab change when selected tab element was focused");
 
   // When this a remote browser, wait for the paint on the second browser so that
   // any post tab-switching stuff has time to complete before blurring the tab.
   // Otherwise, the _adjustFocusAfterTabSwitch in tabbrowser gets confused and
   // isn't sure what tab is really focused.
   if (gMultiProcessBrowser) {
-    yield paintWaiter;
+    yield switchWaiter;
   }
 
   yield expectFocusShift(function () gBrowser.selectedTab.blur(),
                          "main-window", null, true,
                          "blurring tab element");
 
   // focusing the url field should switch active focus away from the browser but
   // not clear what would be the focus in the browser
--- a/browser/base/content/test/general/browser_urlbarRevert.js
+++ b/browser/base/content/test/general/browser_urlbarRevert.js
@@ -1,29 +1,37 @@
+let tab = null;
+
 function test() {
   waitForExplicitFinish();
 
-  let tab = gBrowser.addTab("http://example.com");
-  gBrowser.selectedTab = tab;
-
-  onLoad(function () {
-    let originalValue = gURLBar.value;
+  let pageLoaded = {
+    onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
+        gBrowser.removeProgressListener(this);
+        executeSoon(checkURLBarRevert);
+      }
+    }
+  }
 
-    gBrowser.userTypedValue = "foobar";
-    gBrowser.selectedTab = gBrowser.tabs[0];
-    gBrowser.selectedTab = tab;
-    is(gURLBar.value, "foobar", "location bar displays typed value");
-
-    gURLBar.focus();
-    EventUtils.synthesizeKey("VK_ESCAPE", {});
-    is(gURLBar.value, originalValue, "ESC reverted the location bar value");
-
-    gBrowser.removeTab(tab);
-    finish();
-  });
+  gBrowser.addProgressListener(pageLoaded);
+  tab = gBrowser.addTab("http://example.com");
+  gBrowser.selectedTab = tab;
 }
 
-function onLoad(callback) {
-  gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
-    gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
-    executeSoon(callback);
-  });
+function checkURLBarRevert() {
+  let originalValue = gURLBar.value;
+
+  gBrowser.userTypedValue = "foobar";
+  gBrowser.selectedTab = gBrowser.tabs[0];
+  gBrowser.selectedTab = tab;
+  is(gURLBar.value, "foobar", "location bar displays typed value");
+
+  gURLBar.focus();
+
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+
+  is(gURLBar.value, originalValue, "ESC reverted the location bar value");
+
+  gBrowser.removeTab(tab);
+  finish();
 }
--- a/browser/base/content/test/general/browser_visibleFindSelection.js
+++ b/browser/base/content/test/general/browser_visibleFindSelection.js
@@ -1,39 +1,42 @@
-
-function test() {
-  waitForExplicitFinish();
+add_task(function*() {
+  const childContent = "<div style='position: absolute; left: 2200px; background: green; width: 200px; height: 200px;'>" +
+                       "div</div><div  style='position: absolute; left: 0px; background: red; width: 200px; height: 200px;'>" +
+                       "<span id='s'>div</span></div>";
 
-  let tab = gBrowser.addTab();
-  gBrowser.selectedTab = tab;
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
 
-    ok(true, "Load listener called");
-    waitForFocus(onFocus, content);
-  }, true);
+  yield promiseTabLoadEvent(tab, "data:text/html," + escape(childContent));
+  yield SimpleTest.promiseFocus(gBrowser.selectedBrowser.contentWindowAsCPOW);
 
-  content.location = "data:text/html,<div style='position: absolute; left: 2200px; background: green; width: 200px; height: 200px;'>div</div><div style='position: absolute; left: 0px; background: red; width: 200px; height: 200px;'><span id='s'>div</span></div>";
-}
+  let findBarOpenPromise = promiseWaitForEvent(gBrowser, "findbaropen");
+  EventUtils.synthesizeKey("f", { accelKey: true });
+  yield findBarOpenPromise;
 
-function onFocus() {
-  EventUtils.synthesizeKey("f", { accelKey: true });
   ok(gFindBarInitialized, "find bar is now initialized");
 
+  // Finds the div in the green box.
+  let scrollPromise = promiseWaitForEvent(gBrowser, "scroll");
   EventUtils.synthesizeKey("d", {});
   EventUtils.synthesizeKey("i", {});
   EventUtils.synthesizeKey("v", {});
-  // finds the div in the green box
+  yield scrollPromise;
 
+  // Finds the div in the red box.
+  scrollPromise = promiseWaitForEvent(gBrowser, "scroll");
   EventUtils.synthesizeKey("g", { accelKey: true });
-  // finds the div in the red box
+  yield scrollPromise;
 
-  var rect = content.document.getElementById("s").getBoundingClientRect();
-  ok(rect.left >= 0, "scroll should include find result");
+  let scrollLeftPos = yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
+    return content.document.getElementById("s").getBoundingClientRect().left;
+  });
+
+  ok(scrollLeftPos >= 0, "scroll should include find result");
 
   // clear the find bar
   EventUtils.synthesizeKey("a", { accelKey: true });
   EventUtils.synthesizeKey("VK_DELETE", { });
 
   gFindBar.close();
   gBrowser.removeCurrentTab();
-  finish();
-}
+});
+
--- a/browser/base/content/test/general/test_contextmenu.html
+++ b/browser/base/content/test/general/test_contextmenu.html
@@ -843,17 +843,17 @@ function waitForEvents(event)
 {
   if (event.type == "MozAfterPaint")
     painted = true;
   else if (event.type == "load")
     loaded = true;
   if (painted && loaded) {
     subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
     subwindow.onload = null;
-    startTest();
+    SimpleTest.waitForFocus(startTest, subwindow);
   }
 }
 
 SpecialPowers.setBoolPref("plugins.click_to_play", true);
 setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
 var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
 subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
--- a/browser/base/content/test/general/test_contextmenu_input.html
+++ b/browser/base/content/test/general/test_contextmenu_input.html
@@ -322,17 +322,17 @@ function waitForEvents(event)
 {
   if (event.type == "MozAfterPaint")
     painted = true;
   else if (event.type == "load")
     loaded = true;
   if (painted && loaded) {
     subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
     subwindow.onload = null;
-    startTest();
+    SimpleTest.waitForFocus(startTest, subwindow);
   }
 }
 
 var subwindow = window.open("data:text/html,<!DOCTYPE html><input><input spellcheck='true' value='prodkjfgigrty'><input spellcheck='true' value='foo'><input readonly spellcheck='false'>", "contextmenu-subtext", "width=600,height=700");
 subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
 subwindow.onload = waitForEvents;
 
 SimpleTest.waitForExplicitFinish();
--- a/browser/base/content/test/newtab/browser_newtab_enhanced.js
+++ b/browser/base/content/test/newtab/browser_newtab_enhanced.js
@@ -1,19 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const PRELOAD_PREF = "browser.newtab.preload";
 
 gDirectorySource = "data:application/json," + JSON.stringify({
-  "directory": [{
+  "enhanced": [{
     url: "http://example.com/",
     enhancedImageURI: "data:image/png;base64,helloWORLD",
     title: "title",
+    type: "organic",
+  }],
+  "directory": [{
+    url: "http://example1.com/",
+    enhancedImageURI: "data:image/png;base64,helloWORLD2",
+    title: "title1",
     type: "organic"
+  }],
+  "suggested": [{
+    url: "http://example1.com/2",
+    imageURI: "data:image/png;base64,helloWORLD3",
+    title: "title2",
+    type: "affiliate",
+    frecent_sites: ["test.com"]
   }]
 });
 
 function runTests() {
   let origEnhanced = NewTabUtils.allPages.enhanced;
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref(PRELOAD_PREF);
     NewTabUtils.allPages.enhanced = origEnhanced;
@@ -28,17 +41,17 @@ function runTests() {
     let siteNode = cell.site.node;
     return {
       type: siteNode.getAttribute("type"),
       enhanced: siteNode.querySelector(".enhanced-content").style.backgroundImage,
       title: siteNode.querySelector(".newtab-title").textContent,
     };
   }
 
-  // Make the page have a directory link followed by a history link
+  // Make the page have a directory link, enhanced link, and history link
   yield setLinks("-1");
 
   // Test with enhanced = false
   yield addNewTabPageTab();
   yield customizeNewTabPage("classic");
   let {type, enhanced, title} = getData(0);
   isnot(type, "enhanced", "history link is not enhanced");
   is(enhanced, "", "history link has no enhanced image");
@@ -47,35 +60,83 @@ function runTests() {
   is(getData(1), null, "there is only one link and it's a history link");
 
   // Test with enhanced = true
   yield addNewTabPageTab();
   yield customizeNewTabPage("enhanced");
   ({type, enhanced, title} = getData(0));
   is(type, "organic", "directory link is organic");
   isnot(enhanced, "", "directory link has enhanced image");
+  is(title, "title1");
+
+  ({type, enhanced, title} = getData(1));
+  is(type, "enhanced", "history link is enhanced");
+  isnot(enhanced, "", "history link has enhanced image");
   is(title, "title");
 
-  is(getData(1), null, "history link pushed out by directory link");
+  is(getData(2), null, "there are only 2 links, directory and enhanced history");
 
   // Test with a pinned link
   setPinnedLinks("-1");
   yield addNewTabPageTab();
   ({type, enhanced, title} = getData(0));
   is(type, "enhanced", "pinned history link is enhanced");
   isnot(enhanced, "", "pinned history link has enhanced image");
   is(title, "title");
 
-  is(getData(1), null, "directory link pushed out by pinned history link");
+  ({type, enhanced, title} = getData(1));
+  is(type, "organic", "directory link is organic");
+  isnot(enhanced, "", "directory link has enhanced image");
+  is(title, "title1");
+
+  is(getData(2), null, "directory link pushed out by pinned history link");
 
   // Test pinned link with enhanced = false
   yield addNewTabPageTab();
   yield customizeNewTabPage("classic");
   ({type, enhanced, title} = getData(0));
   isnot(type, "enhanced", "history link is not enhanced");
   is(enhanced, "", "history link has no enhanced image");
   is(title, "site#-1");
 
   is(getData(1), null, "directory link still pushed out by pinned history link");
 
   ok(getContentDocument().getElementById("newtab-intro-what"),
      "'What is this page?' link exists");
+
+  yield unpinCell(0);
+
+
+
+  // Test that a suggested tile is not enhanced by a directory tile
+  let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+  NewTabUtils.isTopPlacesSite = () => true;
+  yield setLinks("-1");
+
+  // Test with enhanced = false
+  yield addNewTabPageTab();
+  yield customizeNewTabPage("classic");
+  ({type, enhanced, title} = getData(0));
+  isnot(type, "enhanced", "history link is not enhanced");
+  is(enhanced, "", "history link has no enhanced image");
+  is(title, "site#-1");
+
+  is(getData(1), null, "there is only one link and it's a history link");
+
+
+  // Test with enhanced = true
+  yield addNewTabPageTab();
+  yield customizeNewTabPage("enhanced");
+
+  // Suggested link was not enhanced by directory link with same domain
+  ({type, enhanced, title} = getData(0));
+  is(type, "affiliate", "suggested link is affiliate");
+  is(enhanced, "", "suggested link has no enhanced image");
+  is(title, "title2");
+
+  // Enhanced history link shows up second
+  ({type, enhanced, title} = getData(1));
+  is(type, "enhanced", "pinned history link is enhanced");
+  isnot(enhanced, "", "pinned history link has enhanced image");
+  is(title, "title");
+
+  is(getData(2), null, "there is only a suggested link followed by an enhanced history link");
 }
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -291,17 +291,18 @@ function fillHistory(aLinks, aCallback =
  */
 function setPinnedLinks(aLinks) {
   let links = aLinks;
 
   if (typeof links == "string") {
     links = aLinks.split(/\s*,\s*/).map(function (id) {
       if (id)
         return {url: "http://example" + (id != "-1" ? id : "") + ".com/",
-                title: "site#" + id};
+                title: "site#" + id,
+                type: "history"};
     });
   }
 
   let string = Cc["@mozilla.org/supports-string;1"]
                  .createInstance(Ci.nsISupportsString);
   string.data = JSON.stringify(links);
   Services.prefs.setComplexValue("browser.newtabpage.pinned",
                                  Ci.nsISupportsString, string);
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -24,32 +24,35 @@ support-files =
   social_sidebar_empty.html
   social_window.html
   social_worker.js
   unchecked.jpg
 
 [browser_aboutHome_activation.js]
 skip-if = e10s # Bug 1053965 "cw.ensureSnippetsMapThen is not a function", also see general/browser.ini about:home comments
 [browser_addons.js]
+skip-if = e10s && debug # Leaking docshells (bug 1150147)
 [browser_blocklist.js]
+skip-if = e10s && debug # Leaking docshells (bug 1150147)
 [browser_share.js]
 skip-if = true # bug 1115131
 [browser_social_activation.js]
 skip-if = e10s # Bug 933103 synthesizeMouseAtCenter not e10s friendly
 [browser_social_chatwindow.js]
 [browser_social_chatwindow_resize.js]
 [browser_social_chatwindowfocus.js]
 skip-if = e10s # tab crash on data url used in this test
 [browser_social_contextmenu.js]
 skip-if = e10s # Bug 1072669 context menu relies on target element
 [browser_social_errorPage.js]
 [browser_social_flyout.js]
 skip-if = e10s # when we backed out bug 1047603, this test broke.
 [browser_social_isVisible.js]
 [browser_social_marks.js]
+skip-if = e10s && debug # Leaking docshells (bug 1150147)
 [browser_social_multiprovider.js]
 skip-if = e10s # Bug 1069162 - lots of orange
 [browser_social_multiworker.js]
 [browser_social_perwindowPB.js]
 [browser_social_sidebar.js]
 [browser_social_status.js]
 [browser_social_window.js]
 [browser_social_workercrash.js]
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -1,12 +1,13 @@
 
 
 let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
+let AddonWatcher = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}).AddonWatcher;
 
 const ADDON_TYPE_SERVICE     = "service";
 const ID_SUFFIX              = "@services.mozilla.org";
 const STRING_TYPE_NAME       = "type.%ID%.name";
 const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 
 let manifest = {
   name: "provider 1",
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1,14 +1,16 @@
 <?xml version="1.0"?>
 
-# -*- Mode: HTML -*-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+<!--
+-*- Mode: HTML -*-
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
 
 <!DOCTYPE bindings [
 <!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
 %notificationDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 %brandDTD;
@@ -44,16 +46,19 @@
                       allowevents="true"
                       xbl:inherits="open,enablehistory,parentfocused=focused"/>
       <xul:popupset anonid="popupset"
                     class="autocomplete-result-popupset"/>
       <children includes="toolbarbutton"/>
     </content>
 
     <implementation implements="nsIObserver, nsIDOMEventListener">
+      <field name="AppConstants" readonly="true">
+        (Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
+      </field>
       <constructor><![CDATA[
         this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
                                 .getService(Components.interfaces.nsIPrefService)
                                 .getBranch("browser.urlbar.");
 
         this._prefs.addObserver("", this, false);
         this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
         this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
@@ -398,21 +403,19 @@
             aCallback(["", null, false]);
             return;
           }
 
           // Only add the suffix when the URL bar value isn't already "URL-like",
           // and only if we get a keyboard event, to match user expectations.
           if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(url) &&
               (aTriggeringEvent instanceof KeyEvent)) {
-#ifdef XP_MACOSX
-            let accel = aTriggeringEvent.metaKey;
-#else
-            let accel = aTriggeringEvent.ctrlKey;
-#endif
+            let accel = this.AppConstants.platform == "macosx" ?
+                        aTriggeringEvent.metaKey :
+                        aTriggeringEvent.ctrlKey;
             let shift = aTriggeringEvent.shiftKey;
 
             let suffix = "";
 
             switch (true) {
               case (accel && shift):
                 suffix = ".org/";
                 break;
@@ -932,16 +935,20 @@
       ]]></handler>
     </handlers>
 
   </binding>
 
   <!-- Note: this binding is applied to the autocomplete popup used in the Search bar and in web page content -->
   <binding id="browser-autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup">
     <implementation>
+      <field name="AppConstants" readonly="true">
+        (Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
+      </field>
+
       <method name="openAutocompletePopup">
         <parameter name="aInput"/>
         <parameter name="aElement"/>
         <body>
           <![CDATA[
           // initially the panel is hidden
           // to avoid impacting startup / new window performance
           aInput.popup.hidden = false;
@@ -984,23 +991,21 @@
             // close the autocomplete popup and revert the entered search term
             this.closePopup();
             controller.handleEscape();
 
             // open the search results according to the clicking subtlety
             var where = whereToOpenLink(aEvent, false, true);
 
             // But open ctrl/cmd clicks on autocomplete items in a new background tab.
+            let modifier = this.AppConstants.platform == "macosx" ?
+                           aEvent.metaKey :
+                           aEvent.ctrlKey;
             if (where == "tab" && (aEvent instanceof MouseEvent) &&
-                (aEvent.button == 1 ||
-#ifdef XP_MACOSX
-                 aEvent.metaKey))
-#else
-                 aEvent.ctrlKey))
-#endif
+                (aEvent.button == 1 || modifier))
               where = "tab-background";
 
             searchBar.doSearch(search, where);
             if (where == "tab-background")
               searchBar.focus();
             else
               searchBar.value = search;
           }
@@ -1556,52 +1561,16 @@
           ]]>
         </body>
       </method>
 
     </implementation>
   </binding>
 
   <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
-    <content align="start">
-      <xul:image class="popup-notification-icon"
-                 xbl:inherits="popupid,src=icon"/>
-      <xul:vbox flex="1">
-        <xul:label class="popup-notification-originHost header"
-                   xbl:inherits="value=originhost"
-                   crop="end"/>
-        <xul:description class="popup-notification-description"
-                         xbl:inherits="xbl:text=label,popupid"/>
-        <xul:progressmeter anonid="progressmeter" flex="1" mode="undetermined" class="popup-progress-meter"/>
-        <xul:label anonid="progresstext" class="popup-progress-label" flex="1" crop="end"/>
-        <xul:spacer flex="1"/>
-        <xul:hbox class="popup-notification-button-container"
-                  pack="end" align="center">
-          <children includes="button"/>
-          <xul:button anonid="button"
-                      class="popup-notification-menubutton"
-                      type="menu-button"
-                      xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
-            <xul:menupopup anonid="menupopup"
-                           xbl:inherits="oncommand=menucommand">
-              <children/>
-              <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
-                            label="&closeNotificationItem.label;"
-                            xbl:inherits="oncommand=closeitemcommand"/>
-            </xul:menupopup>
-          </xul:button>
-        </xul:hbox>
-      </xul:vbox>
-      <xul:vbox pack="start">
-        <xul:toolbarbutton anonid="closebutton"
-                           class="messageCloseButton close-icon popup-notification-closebutton tabbable"
-                           xbl:inherits="oncommand=closebuttoncommand"
-                           tooltiptext="&closeNotification.tooltip;"/>
-      </xul:vbox>
-    </content>
     <implementation>
       <constructor><![CDATA[
         if (!this.notification)
           return;
 
         this.notification.options.installs.forEach(function(aInstall) {
           aInstall.addListener(this);
         }, this);
@@ -1615,20 +1584,20 @@
         this._updateProgressTimeout = setTimeout(this.updateProgress.bind(this), 0);
       ]]></constructor>
 
       <destructor><![CDATA[
         this.destroy();
       ]]></destructor>
 
       <field name="progressmeter" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "progressmeter");
+        document.getElementById("addon-progress-notification-progressmeter"); 
       </field>
       <field name="progresstext" readonly="true">
-        document.getAnonymousElementByAttribute(this, "anonid", "progresstext");
+        document.getElementById("addon-progress-notification-progresstext");
       </field>
       <field name="DownloadUtils" readonly="true">
         let utils = {};
         Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils);
         utils.DownloadUtils;
       </field>
 
       <method name="destroy">
@@ -1643,21 +1612,21 @@
         ]]></body>
       </method>
 
       <method name="setProgress">
         <parameter name="aProgress"/>
         <parameter name="aMaxProgress"/>
         <body><![CDATA[
           if (aMaxProgress == -1) {
-            this.progressmeter.mode = "undetermined";
+            this.progressmeter.setAttribute("mode", "undetermined");
           }
           else {
-            this.progressmeter.mode = "determined";
-            this.progressmeter.value = (aProgress * 100) / aMaxProgress;
+            this.progressmeter.setAttribute("mode", "determined");
+            this.progressmeter.setAttribute("value", (aProgress * 100) / aMaxProgress);
           }
 
           let now = Date.now();
 
           if (!this.notification.lastUpdate) {
             this.notification.lastUpdate = now;
             this.notification.lastProgress = aProgress;
             return;
@@ -1675,17 +1644,18 @@
             speed = speed * 0.9 + this.notification.speed * 0.1;
 
           this.notification.lastUpdate = now;
           this.notification.lastProgress = aProgress;
           this.notification.speed = speed;
 
           let status = null;
           [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last);
-          this.progresstext.value = this.progresstext.tooltipText = status;
+          this.progresstext.setAttribute("value", status);
+          this.progresstext.setAttribute("tooltiptext", status);
         ]]></body>
       </method>
 
       <method name="cancel">
         <body><![CDATA[
           let installs = this.notification.options.installs;
           installs.forEach(function(aInstall) {
             try {
@@ -1717,19 +1687,20 @@
               maxProgress += aInstall.maxProgress;
             if (aInstall.state < AddonManager.STATE_DOWNLOADED)
               downloadingCount++;
           });
 
           if (downloadingCount == 0) {
             this.destroy();
             if (Preferences.get("xpinstall.customConfirmationUI", false)) {
-              this.progressmeter.mode = "undetermined";
-              this.progresstext.value = this.progresstext.tooltipText =
-                gNavigatorBundle.getString("addonDownloadVerifying");
+              this.progressmeter.setAttribute("mode", "undetermined");
+              let status = gNavigatorBundle.getString("addonDownloadVerifying");
+              this.progresstext.setAttribute("value", status);
+              this.progresstext.setAttribute("tooltiptext", status);
             } else {
               PopupNotifications.remove(this.notification);
             }
           }
           else {
             this.setProgress(progress, maxProgress);
           }
         ]]></body>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -139,17 +139,17 @@ browser.jar:
 *       content/browser/sanitize.js                   (content/sanitize.js)
 *       content/browser/sanitize.xul                  (content/sanitize.xul)
 *       content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)
         content/browser/searchSuggestionUI.js         (content/searchSuggestionUI.js)
         content/browser/searchSuggestionUI.css        (content/searchSuggestionUI.css)
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.xml                (content/tabbrowser.xml)
-*       content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
+        content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
 *       content/browser/utilityOverlay.js             (content/utilityOverlay.js)
         content/browser/web-panels.js                 (content/web-panels.js)
 *       content/browser/web-panels.xul                (content/web-panels.xul)
 *       content/browser/baseMenuOverlay.xul           (content/baseMenuOverlay.xul)
 *       content/browser/nsContextMenu.js              (content/nsContextMenu.js)
 # XXX: We should exclude this one as well (bug 71895)
 *       content/browser/hiddenWindow.xul              (content/hiddenWindow.xul)
 #ifdef XP_MACOSX
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -116,20 +116,22 @@ CustomizeMode.prototype = {
       this.exit();
     } else {
       this.enter();
     }
   },
 
   swatchForTheme: function(aDocument) {
    let lwthemeButton = aDocument.getElementById("customization-lwtheme-button");
+   let lwthemeIcon = aDocument.getAnonymousElementByAttribute(lwthemeButton,
+          "class", "button-icon");
    let imageURL = LightweightThemeManager.currentTheme === null ?
           "chrome://browser/skin/theme-switcher-icon.png" :
           LightweightThemeManager.currentTheme.iconURL;
-    lwthemeButton.setAttribute("image", imageURL);
+    lwthemeIcon.style.backgroundImage = "url(" + imageURL + ")";
   },
 
   enter: function() {
     this._wantToBeInCustomizeMode = true;
 
     if (this._customizing || this._handler.isEnteringCustomizeMode) {
       return;
     }
--- a/browser/components/distribution.js
+++ b/browser/components/distribution.js
@@ -9,16 +9,17 @@ const Cc = Components.classes;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC =
   "distribution-customization-complete";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 
 this.DistributionCustomizer = function DistributionCustomizer() {
   // For parallel xpcshell testing purposes allow loading the distribution.ini
   // file from the profile folder through an hidden pref.
   let loadFromProfile = false;
   try {
@@ -80,153 +81,163 @@ DistributionCustomizer.prototype = {
     this.__defineGetter__("_ioSvc", function() svc);
     return this._ioSvc;
   },
 
   _makeURI: function DIST__makeURI(spec) {
     return this._ioSvc.newURI(spec, null, null);
   },
 
-  _parseBookmarksSection:
-  function DIST_parseBookmarksSection(parentId, section) {
-    let keys = [];
-    for (let i in enumerate(this._ini.getKeys(section)))
-      keys.push(i);
-    keys.sort();
-
+  _parseBookmarksSection: Task.async(function* (parentGuid, section) {
+    let keys = Array.from(enumerate(this._ini.getKeys(section))).sort();
+    let re = /^item\.(\d+)\.(\w+)\.?(\w*)/;
     let items = {};
-    let defaultItemId = -1;
-    let maxItemId = -1;
+    let defaultIndex = -1;
+    let maxIndex = -1;
 
-    for (let i = 0; i < keys.length; i++) {
-      let m = /^item\.(\d+)\.(\w+)\.?(\w*)/.exec(keys[i]);
+    for (let key of keys) {
+      let m = re.exec(key);
       if (m) {
-        let [foo, iid, iprop, ilocale] = m;
-        iid = parseInt(iid);
+        let [foo, itemIndex, iprop, ilocale] = m;
+        itemIndex = parseInt(itemIndex);
 
         if (ilocale)
           continue;
 
-        if (!items[iid])
-          items[iid] = {};
-        if (keys.indexOf(keys[i] + "." + this._locale) >= 0) {
-          items[iid][iprop] = this._ini.getString(section, keys[i] + "." +
-                                                  this._locale);
-        } else {
-          items[iid][iprop] = this._ini.getString(section, keys[i]);
+        if (keys.indexOf(key + "." + this._locale) >= 0) {
+          key += "." + this._locale;
         }
 
-        if (iprop == "type" && items[iid]["type"] == "default")
-          defaultItemId = iid;
+        if (!items[itemIndex])
+          items[itemIndex] = {};
+        items[itemIndex][iprop] = this._ini.getString(section, key);
 
-        if (maxItemId < iid)
-          maxItemId = iid;
+        if (iprop == "type" && items[itemIndex]["type"] == "default")
+          defaultIndex = itemIndex;
+
+        if (maxIndex < itemIndex)
+          maxIndex = itemIndex;
       } else {
-        dump("Key did not match: " + keys[i] + "\n");
+        dump(`Key did not match: ${key}\n`);
       }
     }
 
     let prependIndex = 0;
-    for (let iid = 0; iid <= maxItemId; iid++) {
-      if (!items[iid])
+    for (let itemIndex = 0; itemIndex <= maxIndex; itemIndex++) {
+      if (!items[itemIndex])
         continue;
 
       let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
-      let newId;
+      let item = items[itemIndex];
 
-      switch (items[iid]["type"]) {
+      switch (item.type) {
       case "default":
         break;
 
       case "folder":
-        if (iid < defaultItemId)
+        if (itemIndex < defaultIndex)
           index = prependIndex++;
 
-        newId = PlacesUtils.bookmarks.createFolder(parentId,
-                                                   items[iid]["title"],
-                                                   index);
+        let folder = yield PlacesUtils.bookmarks.insert({
+          type: PlacesUtils.bookmarks.TYPE_FOLDER,
+          parentGuid, index, title: item.title
+        });
+
+        yield this._parseBookmarksSection(folder.guid,
+                                          "BookmarksFolder-" + item.folderId);
 
-        this._parseBookmarksSection(newId, "BookmarksFolder-" +
-                                    items[iid]["folderId"]);
-
-        if (items[iid]["description"])
-          PlacesUtils.annotations.setItemAnnotation(newId,
+        if (item.description) {
+          let folderId = yield PlacesUtils.promiseItemId(folder.guid);
+          PlacesUtils.annotations.setItemAnnotation(folderId,
                                                     "bookmarkProperties/description",
-                                                    items[iid]["description"], 0,
+                                                    item.description, 0,
                                                     PlacesUtils.annotations.EXPIRE_NEVER);
+        }
 
         break;
 
       case "separator":
-        if (iid < defaultItemId)
+        if (itemIndex < defaultIndex)
           index = prependIndex++;
-        PlacesUtils.bookmarks.insertSeparator(parentId, index);
+
+        yield PlacesUtils.bookmarks.insert({
+          type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
+          parentGuid, index
+        });
         break;
 
       case "livemark":
-        if (iid < defaultItemId)
+        if (itemIndex < defaultIndex)
           index = prependIndex++;
 
         // Don't bother updating the livemark contents on creation.
-        PlacesUtils.livemarks.addLivemark({ title: items[iid]["title"]
-                                          , parentId: parentId
-                                          , index: index
-                                          , feedURI: this._makeURI(items[iid]["feedLink"])
-                                          , siteURI: this._makeURI(items[iid]["siteLink"])
-                                          }).then(null, Cu.reportError);
+        let parentId = yield PlacesUtils.promiseItemId(parentGuid);
+        yield PlacesUtils.livemarks.addLivemark({
+          feedURI: this._makeURI(item.feedLink),
+          siteURI: this._makeURI(item.siteLink),
+          parentId, index, title: item.title
+        });
         break;
 
       case "bookmark":
       default:
-        if (iid < defaultItemId)
+        if (itemIndex < defaultIndex)
           index = prependIndex++;
 
-        newId = PlacesUtils.bookmarks.insertBookmark(parentId,
-                                                     this._makeURI(items[iid]["link"]),
-                                                     index, items[iid]["title"]);
+        let bm = yield PlacesUtils.bookmarks.insert({
+          parentGuid, index, title: item.title, url: item.link
+        });
 
-        if (items[iid]["description"])
-          PlacesUtils.annotations.setItemAnnotation(newId,
+        if (item.description) {
+          let bmId = yield PlacesUtils.promiseItemId(bm.guid);
+          PlacesUtils.annotations.setItemAnnotation(bmId,
                                                     "bookmarkProperties/description",
-                                                    items[iid]["description"], 0,
+                                                    item.description, 0,
                                                     PlacesUtils.annotations.EXPIRE_NEVER);
+        }
 
         break;
       }
     }
-  },
+  }),
 
   _customizationsApplied: false,
   applyCustomizations: function DIST_applyCustomizations() {
     this._customizationsApplied = true;
     if (!this._iniFile)
       return this._checkCustomizationComplete();
 
     // nsPrefService loads very early.  Reload prefs so we can set
     // distribution defaults during the prefservice:after-app-defaults
     // notification (see applyPrefDefaults below)
     this._prefSvc.QueryInterface(Ci.nsIObserver);
     this._prefSvc.observe(null, "reload-default-prefs", null);
   },
 
   _bookmarksApplied: false,
-  applyBookmarks: function DIST_applyBookmarks() {
+  applyBookmarks: Task.async(function* () {
+    yield this._doApplyBookmarks();
     this._bookmarksApplied = true;
+    this._checkCustomizationComplete();
+  }),
+
+  _doApplyBookmarks: Task.async(function* () {
     if (!this._iniFile)
-      return this._checkCustomizationComplete();
+      return;
 
     let sections = enumToObject(this._ini.getSections());
 
     // The global section, and several of its fields, is required
     // (we also check here to be consistent with applyPrefDefaults below)
     if (!sections["Global"])
-      return this._checkCustomizationComplete();
+      return;
+
     let globalPrefs = enumToObject(this._ini.getKeys("Global"));
     if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"]))
-      return this._checkCustomizationComplete();
+      return;
 
     let bmProcessedPref;
     try {
       bmProcessedPref = this._ini.getString("Global",
                                             "bookmarks.initialized.pref");
     }
     catch (e) {
       bmProcessedPref = "distribution." +
@@ -236,25 +247,24 @@ DistributionCustomizer.prototype = {
     let bmProcessed = false;
     try {
       bmProcessed = this._prefs.getBoolPref(bmProcessedPref);
     }
     catch (e) {}
 
     if (!bmProcessed) {
       if (sections["BookmarksMenu"])
-        this._parseBookmarksSection(PlacesUtils.bookmarksMenuFolderId,
-                                    "BookmarksMenu");
+        yield this._parseBookmarksSection(PlacesUtils.bookmarks.menuGuid,
+                                          "BookmarksMenu");
       if (sections["BookmarksToolbar"])
-        this._parseBookmarksSection(PlacesUtils.toolbarFolderId,
-                                    "BookmarksToolbar");
+        yield this._parseBookmarksSection(PlacesUtils.bookmarks.toolbarGuid,
+                                          "BookmarksToolbar");
       this._prefs.setBoolPref(bmProcessedPref, true);
     }
-    return this._checkCustomizationComplete();
-  },
+  }),
 
   _prefDefaultsApplied: false,
   applyPrefDefaults: function DIST_applyPrefDefaults() {
     this._prefDefaultsApplied = true;
     if (!this._iniFile)
       return this._checkCustomizationComplete();
 
     let sections = enumToObject(this._ini.getSections());
@@ -286,17 +296,17 @@ DistributionCustomizer.prototype = {
       defaults.setComplexValue("distribution.about",
                                Ci.nsISupportsString, partnerAbout);
     } catch (e) {
       /* ignore bad prefs due to bug 895473 and move on */
       Cu.reportError(e);
     }
 
     if (sections["Preferences"]) {
-      for (let key in enumerate(this._ini.getKeys("Preferences"))) {
+      for (let key of enumerate(this._ini.getKeys("Preferences"))) {
         try {
           let value = eval(this._ini.getString("Preferences", key));
           switch (typeof value) {
           case "boolean":
             defaults.setBoolPref(key, value);
             break;
           case "number":
             defaults.setIntPref(key, value);
@@ -315,28 +325,28 @@ DistributionCustomizer.prototype = {
     // We eval() the localizable prefs as well (even though they'll
     // always get set as a string) to keep the INI format consistent:
     // string prefs always need to be in quotes
 
     let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"].
       createInstance(Ci.nsIPrefLocalizedString);
 
     if (sections["LocalizablePreferences"]) {
-      for (let key in enumerate(this._ini.getKeys("LocalizablePreferences"))) {
+      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences"))) {
         try {
           let value = eval(this._ini.getString("LocalizablePreferences", key));
           value = value.replace(/%LOCALE%/g, this._locale);
           localizedStr.data = "data:text/plain," + key + "=" + value;
           defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
         } catch (e) { /* ignore bad prefs and move on */ }
       }
     }
 
     if (sections["LocalizablePreferences-" + this._locale]) {
-      for (let key in enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
+      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
         try {
           let value = eval(this._ini.getString("LocalizablePreferences-" + this._locale, key));
           localizedStr.data = "data:text/plain," + key + "=" + value;
           defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
         } catch (e) { /* ignore bad prefs and move on */ }
       }
     }
 
@@ -349,19 +359,19 @@ DistributionCustomizer.prototype = {
         prefDefaultsApplied) {
       let os = Cc["@mozilla.org/observer-service;1"].
                getService(Ci.nsIObserverService);
       os.notifyObservers(null, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC, null);
     }
   }
 };
 
-function enumerate(UTF8Enumerator) {
+function* enumerate(UTF8Enumerator) {
   while (UTF8Enumerator.hasMore())
     yield UTF8Enumerator.getNext();
 }
 
 function enumToObject(UTF8Enumerator) {
   let ret = {};
-  for (let i in enumerate(UTF8Enumerator))
+  for (let i of enumerate(UTF8Enumerator))
     ret[i] = 1;
   return ret;
 }
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -682,16 +682,19 @@ DownloadsDataCtor.prototype = {
   /**
    * Asks the back-end to remove finished downloads from the list.
    */
   removeFinished() {
     let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE
                                                         : Downloads.PUBLIC);
     promiseList.then(list => list.removeFinished())
                .then(null, Cu.reportError);
+    let indicatorData = this._isPrivate ? PrivateDownloadsIndicatorData
+                                        : DownloadsIndicatorData;
+    indicatorData.attention = false;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Integration with the asynchronous Downloads back-end
 
   onDownloadAdded(download) {
     // Download objects do not store the end time of downloads, as the Downloads
     // API does not need to persist this information for all platforms. Once a
--- a/browser/components/feeds/nsFeedSniffer.h
+++ b/browser/components/feeds/nsFeedSniffer.h
@@ -5,17 +5,17 @@
 
 
 #include "nsIContentSniffer.h"
 #include "nsIStreamListener.h"
 #include "nsStringAPI.h"
 #include "mozilla/Attributes.h"
 
 class nsFeedSniffer final : public nsIContentSniffer,
-                                       nsIStreamListener
+                                   nsIStreamListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTSNIFFER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
 
   static NS_METHOD AppendSegmentToString(nsIInputStream* inputStream,
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/.eslintignore
@@ -0,0 +1,15 @@
+# We still need to fix warnings in this file.
+MozLoopWorker.js
+# This file currently uses es7 features
+MozLoopAPI.jsm
+# Libs we don't need to check
+content/libs
+content/shared/libs
+standalone/content/libs
+standalone/node_modules
+# We should look at turning these on when we fix the warnings
+test/xpcshell
+test/mochitest
+# Libs we don't need to check
+test/shared/vendor
+
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/.eslintrc
@@ -0,0 +1,106 @@
+{
+  "plugins": [
+    "react"
+  ],
+  "ecmaFeatures": {
+    "forOf": true,
+    "jsx": true,
+    // These are on for this directory for .jsm and content/js files.
+    // If adding more items here, consider turning them off for the following
+    // files if they aren't supported by all browsers.
+    // content/shared/.eslintrc
+    // content/standalone/.eslintrc
+    "blockBindings": true,
+    "arrowFunctions": true,
+    "destructuring": true,
+    "generators": true,
+    "spread": true,
+    "restParams": true,
+    "objectLiteralShorthandMethods": true
+  },
+  "env": {
+    "browser": true,
+    "mocha": true
+  },
+  "globals": {
+    "_": false,
+    "$": false,
+    "Backbone": false,
+    "chai": false,
+    "console": false,
+    "jQuery": false,
+    "loop": false,
+    "MozActivity": false,
+    "OT": false,
+    "Promise": false,
+    "React": false,
+    "sinon": false
+  },
+  "rules": {
+    // turn off all kinds of stuff that we actually do want, because
+    // right now, we're bootstrapping the linting infrastructure.  We'll
+    // want to audit these rules, and start turning them on and fixing the
+    // problems they find, one at a time.
+
+    // Eslint built-in rules are documented at <http://eslint.org/docs/rules/>
+    "camelcase": 0,
+    "comma-dangle": 0,
+    "comma-spacing": 0,
+    "consistent-return": 0,
+    "curly": 0,
+    "dot-notation": 0,
+    "eol-last": 0,
+    "eqeqeq": 0,
+    "global-strict": 0,
+    "key-spacing": 0,
+    "new-cap": 0,
+    "no-catch-shadow": 0,
+    "no-console": 0,
+    "no-empty": 0,
+    "no-extra-bind": 0,
+    "no-extra-boolean-cast": 0,
+    "no-extra-semi": 0,
+    "no-multi-spaces": 0,
+    "no-new": 0,
+    "no-redeclare": 0,
+    "no-return-assign": 0,
+    "no-shadow": 0,
+    "no-spaced-func": 0,
+    "no-trailing-spaces": 0,
+    "no-undef": 0,
+    "no-underscore-dangle": 0,
+    "no-unused-expressions": 0,
+    "no-unused-vars": 0,
+    "no-use-before-define": 0,
+    "no-wrap-func": 0,
+    "quotes": 0,
+    "semi": 0,
+    "semi-spacing": 0,
+    "space-infix-ops": 0,
+    "space-return-throw-case": 0,
+    "strict": 0,
+    "yoda": 0,
+    // eslint-plugin-react rules. These are documented at
+    // <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
+    "react/jsx-quotes": [2, "double", "avoid-escape"],
+    "react/jsx-no-undef": 2,
+    // Need to fix instances where this is failing.
+    "react/jsx-sort-props": 0,
+    "react/jsx-uses-vars": 2,
+    // Need to fix the couple of instances which don't
+    // currently pass this rule.
+    "react/no-did-mount-set-state": 0,
+    "react/no-did-update-set-state": 2,
+    "react/no-unknown-property": 2,
+    // Need to fix instances where this is currently failing
+    "react/prop-types": 0,
+    "react/self-closing-comp": 2,
+    "react/wrap-multilines": 2,
+    // Not worth it: React is defined globally
+    "react/jsx-uses-react": 0,
+    "react/react-in-jsx-scope": 0,
+    // These ones we don't want to ever enable
+    "react/display-name": 0,
+    "react/no-multi-comp": 0
+  }
+}
--- a/browser/components/loop/GoogleImporter.jsm
+++ b/browser/components/loop/GoogleImporter.jsm
@@ -209,16 +209,23 @@ this.GoogleImporter.prototype = {
              Services.prefs.getCharPref("loop.oauth.google." + param));
     }
     const features = "centerscreen,resizable=yes,toolbar=no,menubar=no,status=no,directories=no," +
                      "width=" + kAuthWindowSize.width + ",height=" + kAuthWindowSize.height;
     gAuthWindow = windowRef.openDialog(windowRef.getBrowserURL(), "_blank", features, url);
     gAuthWindow.focus();
 
     let code;
+
+    function promiseTimeOut() {
+      return new Promise(resolve => {
+        setTimeout(resolve, kTitlebarPollTimeout);
+      });
+    }
+
     // The following loops runs as long as the OAuth windows' titlebar doesn't
     // yield a response from the Google service. If an error occurs, the loop
     // will terminate early.
     while (!code) {
       if (!gAuthWindow || gAuthWindow.closed) {
         throw new Error("Popup window was closed before authentication succeeded");
       }
 
@@ -230,17 +237,17 @@ this.GoogleImporter.prototype = {
         if (type == "error") {
           throw new Error("Google authentication failed with error: " + message.trim());
         } else if (type == "code") {
           code = message.trim();
         } else {
           throw new Error("Unknown response from Google");
         }
       } else {
-        yield new Promise(resolve => setTimeout(resolve, kTitlebarPollTimeout));
+        yield promiseTimeOut();
       }
     }
 
     return code;
   }),
 
   /**
    * Fetch an OAuth tokenset, that will be used to authenticate Google API calls,
@@ -567,21 +574,25 @@ this.GoogleImporter.prototype = {
    * @param {LoopContacts} db  Instance of the LoopContacts database object, which
    *                           will store the newly found contacts
    */
   _purgeContacts: Task.async(function* (ids, db) {
     let contacts = yield db.promise("getAll");
     let profileId = "https://www.google.com/m8/feeds/contacts/" + encodeURIComponent(gProfileId);
     let processed = 0;
 
+    function promiseSkipABeat() {
+      return new Promise(resolve => Services.tm.currentThread.dispatch(resolve,
+                                      Ci.nsIThread.DISPATCH_NORMAL));
+    }
+
     for (let [guid, contact] of Iterator(contacts)) {
       if (++processed % kContactsChunkSize === 0) {
         // Skip a beat every time we processed a chunk.
-        yield new Promise(resolve => Services.tm.currentThread.dispatch(resolve,
-                                       Ci.nsIThread.DISPATCH_NORMAL));
+        yield promiseSkipABeat;
       }
 
       if (contact.id.indexOf(profileId) >= 0 && !ids[contact.id]) {
         yield db.promise("remove", guid);
       }
     }
   })
 };
--- a/browser/components/loop/LoopStorage.jsm
+++ b/browser/components/loop/LoopStorage.jsm
@@ -344,32 +344,34 @@ this.LoopStorage = Object.freeze({
     let callbackCalled = false;
     let len = list.length;
 
     if (!len) {
       onDone(new Error("Argument error: empty list"));
       return;
     }
 
-    for (; i < len; ++i) {
-      onItem(list[i], function handler(err) {
-        if (callbackCalled) {
-          return;
-        }
+    function handleCallback(err) {
+      if (callbackCalled) {
+        return;
+      }
 
-        if (err) {
-          onDone(err);
-          callbackCalled = true;
-          return;
-        }
+      if (err) {
+        onDone(err);
+        callbackCalled = true;
+        return;
+      }
 
-        if (++done === len) {
-          onDone();
-          callbackCalled = true;
-        }
-      }, i);
+      if (++done === len) {
+        onDone();
+        callbackCalled = true;
+      }
+    }
+
+    for (; i < len; ++i) {
+      onItem(list[i], handleCallback, i);
     }
   },
 
   on: (...params) => eventEmitter.on(...params),
 
   off: (...params) => eventEmitter.off(...params)
 });
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -144,34 +144,40 @@ const injectObjectAPI = function(api, ta
       let lastParam = params.pop();
       let callbackIsFunction = (typeof lastParam == "function");
       // Clone params coming from content to the current scope.
       params = [cloneValueInto(p, api) for (p of params)];
 
       // If the last parameter is a function, assume its a callback
       // and wrap it differently.
       if (callbackIsFunction) {
-        api[func](...params, function(...results) {
+        api[func](...params, function callback(...results) {
           // When the function was garbage collected due to async events, like
           // closing a window, we want to circumvent a JS error.
           if (callbackIsFunction && typeof lastParam != "function") {
             MozLoopService.log.debug(func + ": callback function was lost.");
+            // Clean up event listeners.
+            if (func == "on" && api.off) {
+              api.off(results[0], callback);
+              return;
+            }
             // Assume the presence of a first result argument to be an error.
             if (results[0]) {
               MozLoopService.log.error(func + " error:", results[0]);
             }
             return;
           }
           lastParam(...[cloneValueInto(r, targetWindow) for (r of results)]);
         });
       } else {
         try {
           lastParam = cloneValueInto(lastParam, api);
           return cloneValueInto(api[func](...params, lastParam), targetWindow);
         } catch (ex) {
+          MozLoopService.log.error(func + " error: ", ex, params, lastParam);
           return cloneValueInto(ex, targetWindow);
         }
       }
     };
   });
 
   let contentObj = Cu.cloneInto(injectedAPI, targetWindow, {cloneFunctions: true});
   // Since we deny preventExtensions on XrayWrappers, because Xray semantics make
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -160,20 +160,24 @@ let MozLoopServiceInternal = {
 
   /**
    * The current deferreds for the registration processes. This is set if in progress
    * or the registration was successful. This is null if a registration attempt was
    * unsuccessful.
    */
   deferredRegistrations: new Map(),
 
-  get pushHandler() this.mocks.pushHandler || MozLoopPushHandler,
+  get pushHandler() {
+    return this.mocks.pushHandler || MozLoopPushHandler
+  },
 
   // The uri of the Loop server.
-  get loopServerUri() Services.prefs.getCharPref("loop.server"),
+  get loopServerUri() {
+    return Services.prefs.getCharPref("loop.server")
+  },
 
   /**
    * The initial delay for push registration. This ensures we don't start
    * kicking off straight after browser startup, just a few seconds later.
    */
   get initialRegistrationDelayMilliseconds() {
     try {
       // Let a pref override this for developer & testing use.
@@ -1195,17 +1199,17 @@ this.MozLoopService = {
     // Set or clear an error depending on how deferredInitialization gets resolved.
     // We do this first so that it can handle the early returns below.
     let completedPromise = deferredInitialization.promise.then(result => {
       MozLoopServiceInternal.clearError("initialization");
       return result;
     },
     error => {
       // If we get a non-object then setError was already called for a different error type.
-      if (typeof(error) == "object") {
+      if (typeof error == "object") {
         MozLoopServiceInternal.setError("initialization", error, () => MozLoopService.delayedInitialize(Promise.defer()));
       }
     });
 
     try {
       if (LoopRooms.getGuestCreatedRoom()) {
         yield this.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
       } else {
@@ -1506,17 +1510,17 @@ this.MozLoopService = {
     }, error => {
       log.error("Failed to retrieve profile", error, this.fetchFxAProfile.bind(this));
       MozLoopServiceInternal.setError("profile", error);
       MozLoopServiceInternal.fxAOAuthProfile = null;
       MozLoopServiceInternal.notifyStatusChanged();
     });
   },
 
-  openFxASettings: Task.async(function() {
+  openFxASettings: Task.async(function* () {
     try {
       let fxAOAuthClient = yield MozLoopServiceInternal.promiseFxAOAuthClient();
       if (!fxAOAuthClient) {
         log.error("Could not get the OAuth client");
         return;
       }
       let url = new URL("/settings", fxAOAuthClient.parameters.content_uri);
       let win = Services.wm.getMostRecentWindow("navigator:browser");
--- a/browser/components/loop/README.txt
+++ b/browser/components/loop/README.txt
@@ -35,16 +35,31 @@ at the same time as changes to their sou
 Hacking
 =======
 Please be sure to execute
 
   browser/components/loop/run-all-loop-tests.sh
 
 from the top level before requesting review on a patch.
 
+Linting
+=======
+run-all-loop-tests.sh will take care of this for you automatically, after
+you've installed the dependencies by typing:
+
+  ( cd standalone ; make install )
+
+If you install eslint and the react plugin globally:
+
+  npm install -g eslint
+  npm install -g eslint-plugin-react
+
+You can also run it by hand in the browser/components/loop directory:
+
+  eslint -ext .js -ext .jsx .
 
 Front-End Unit Tests
 ====================
 The unit tests for Loop reside in three directories:
 
 - test/desktop-local
 - test/shared
 - test/standalone
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -533,18 +533,20 @@ loop.contacts = (function(_, mozL10n) {
       // consistent ordering.
       return contact1._guid - contact2._guid;
     },
 
     render: function() {
       let cx = React.addons.classSet;
 
       let viewForItem = item => {
-        return React.createElement(ContactDetail, {key: item._guid, contact: item, 
-                              handleContactAction: this.handleContactAction})
+        return (
+          React.createElement(ContactDetail, {key: item._guid, contact: item, 
+                         handleContactAction: this.handleContactAction})
+        );
       };
 
       let shownContacts = _.groupBy(this.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
       let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
                        MIN_CONTACTS_FOR_FILTERING;
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -533,18 +533,20 @@ loop.contacts = (function(_, mozL10n) {
       // consistent ordering.
       return contact1._guid - contact2._guid;
     },
 
     render: function() {
       let cx = React.addons.classSet;
 
       let viewForItem = item => {
-        return <ContactDetail key={item._guid} contact={item}
-                              handleContactAction={this.handleContactAction} />
+        return (
+          <ContactDetail key={item._guid} contact={item}
+                         handleContactAction={this.handleContactAction} />
+        );
       };
 
       let shownContacts = _.groupBy(this.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
       let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
                        MIN_CONTACTS_FOR_FILTERING;
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -254,21 +254,23 @@ loop.panel = (function(_, mozL10n) {
             )
           ),
           "privacy_notice": React.renderToStaticMarkup(
             React.createElement("a", {href: privacy_notice_url, target: "_blank"}, 
               mozL10n.get("legal_text_privacy")
             )
           ),
         });
-        return React.createElement("div", {id: "powered-by-wrapper"}, 
-          this.renderPartnerLogo(), 
-          React.createElement("p", {className: "terms-service", 
-             dangerouslySetInnerHTML: {__html: tosHTML}})
-         );
+        return (
+          React.createElement("div", {id: "powered-by-wrapper"}, 
+            this.renderPartnerLogo(), 
+            React.createElement("p", {className: "terms-service", 
+               dangerouslySetInnerHTML: {__html: tosHTML}})
+           )
+        );
       } else {
         return React.createElement("div", null);
       }
     }
   });
 
   /**
    * Panel settings (gear) menu entry.
@@ -655,20 +657,22 @@ loop.panel = (function(_, mozL10n) {
         console.error("RoomList error", this.state.error);
       }
 
       return (
         React.createElement("div", {className: "rooms"}, 
           React.createElement("h1", null, this._getListHeading()), 
           React.createElement("div", {className: "room-list"}, 
             this.state.rooms.map(function(room, i) {
-              return React.createElement(RoomEntry, {
-                key: room.roomToken, 
-                dispatcher: this.props.dispatcher, 
-                room: room}
+              return (
+                React.createElement(RoomEntry, {
+                  key: room.roomToken, 
+                  dispatcher: this.props.dispatcher, 
+                  room: room}
+                )
               );
             }, this)
           ), 
           React.createElement("div", null, 
             React.createElement(ContextInfo, {mozLoop: this.props.mozLoop}), 
             React.createElement("button", {className: "btn btn-info new-room-button", 
                     onClick: this.handleCreateButtonClick, 
                     disabled: this._hasPendingOperation()}, 
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -254,21 +254,23 @@ loop.panel = (function(_, mozL10n) {
             </a>
           ),
           "privacy_notice": React.renderToStaticMarkup(
             <a href={privacy_notice_url} target="_blank">
               {mozL10n.get("legal_text_privacy")}
             </a>
           ),
         });
-        return <div id="powered-by-wrapper">
-          {this.renderPartnerLogo()}
-          <p className="terms-service"
-             dangerouslySetInnerHTML={{__html: tosHTML}}></p>
-         </div>;
+        return (
+          <div id="powered-by-wrapper">
+            {this.renderPartnerLogo()}
+            <p className="terms-service"
+               dangerouslySetInnerHTML={{__html: tosHTML}}></p>
+           </div>
+        );
       } else {
         return <div />;
       }
     }
   });
 
   /**
    * Panel settings (gear) menu entry.
@@ -655,21 +657,23 @@ loop.panel = (function(_, mozL10n) {
         console.error("RoomList error", this.state.error);
       }
 
       return (
         <div className="rooms">
           <h1>{this._getListHeading()}</h1>
           <div className="room-list">{
             this.state.rooms.map(function(room, i) {
-              return <RoomEntry
-                key={room.roomToken}
-                dispatcher={this.props.dispatcher}
-                room={room}
-              />;
+              return (
+                <RoomEntry
+                  key={room.roomToken}
+                  dispatcher={this.props.dispatcher}
+                  room={room}
+                />
+              );
             }, this)
           }</div>
           <div>
             <ContextInfo mozLoop={this.props.mozLoop} />
             <button className="btn btn-info new-room-button"
                     onClick={this.handleCreateButtonClick}
                     disabled={this._hasPendingOperation()}>
               {mozL10n.get("rooms_new_room_button_label")}
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -140,17 +140,17 @@ loop.roomViews = (function(mozL10n) {
               onBlur: this.handleFormSubmit, 
               onKeyDown: this.handleTextareaKeyDown, 
               placeholder: mozL10n.get("rooms_name_this_room_label")})
           ), 
           React.createElement("p", null, mozL10n.get("invite_header_text")), 
           React.createElement("div", {className: "btn-group call-action-group"}, 
             React.createElement("button", {className: "btn btn-info btn-email", 
                     onClick: this.handleEmailButtonClick}, 
-              mozL10n.get("share_button2")
+              mozL10n.get("email_link_button")
             ), 
             React.createElement("button", {className: "btn btn-info btn-copy", 
                     onClick: this.handleCopyButtonClick}, 
               this.state.copiedUrl ? mozL10n.get("copied_url_button") :
                                       mozL10n.get("copy_url_button2")
             )
           )
         )
@@ -171,19 +171,20 @@ loop.roomViews = (function(mozL10n) {
     ],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
     },
 
     _renderInvitationOverlay: function() {
       if (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS) {
-        return React.createElement(DesktopRoomInvitationView, {
-          roomStore: this.props.roomStore, 
-          dispatcher: this.props.dispatcher}
+        return (
+          React.createElement(DesktopRoomInvitationView, {
+            roomStore: this.props.roomStore, 
+            dispatcher: this.props.dispatcher})
         );
       }
       return null;
     },
 
     componentWillUpdate: function(nextProps, nextState) {
       // The SDK needs to know about the configuration and the elements to use
       // for display. So the best way seems to pass the information here - ideally
@@ -243,23 +244,25 @@ loop.roomViews = (function(mozL10n) {
         visible: true
       };
 
       switch(this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
-          return React.createElement(loop.conversationViews.GenericFailureView, {
-            cancelCall: this.closeWindow}
+          return (
+            React.createElement(loop.conversationViews.GenericFailureView, {
+              cancelCall: this.closeWindow})
           );
         }
         case ROOM_STATES.ENDED: {
-          return React.createElement(sharedViews.FeedbackView, {
-            onAfterFeedbackReceived: this.closeWindow}
+          return (
+            React.createElement(sharedViews.FeedbackView, {
+              onAfterFeedbackReceived: this.closeWindow})
           );
         }
         default: {
           return (
             React.createElement("div", {className: "room-conversation-wrapper"}, 
               this._renderInvitationOverlay(), 
               React.createElement("div", {className: "video-layout-wrapper"}, 
                 React.createElement("div", {className: "conversation room-conversation"}, 
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -140,17 +140,17 @@ loop.roomViews = (function(mozL10n) {
               onBlur={this.handleFormSubmit}
               onKeyDown={this.handleTextareaKeyDown}
               placeholder={mozL10n.get("rooms_name_this_room_label")} />
           </form>
           <p>{mozL10n.get("invite_header_text")}</p>
           <div className="btn-group call-action-group">
             <button className="btn btn-info btn-email"
                     onClick={this.handleEmailButtonClick}>
-              {mozL10n.get("share_button2")}
+              {mozL10n.get("email_link_button")}
             </button>
             <button className="btn btn-info btn-copy"
                     onClick={this.handleCopyButtonClick}>
               {this.state.copiedUrl ? mozL10n.get("copied_url_button") :
                                       mozL10n.get("copy_url_button2")}
             </button>
           </div>
         </div>
@@ -171,20 +171,21 @@ loop.roomViews = (function(mozL10n) {
     ],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
     },
 
     _renderInvitationOverlay: function() {
       if (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS) {
-        return <DesktopRoomInvitationView
-          roomStore={this.props.roomStore}
-          dispatcher={this.props.dispatcher}
-        />;
+        return (
+          <DesktopRoomInvitationView
+            roomStore={this.props.roomStore}
+            dispatcher={this.props.dispatcher} />
+        );
       }
       return null;
     },
 
     componentWillUpdate: function(nextProps, nextState) {
       // The SDK needs to know about the configuration and the elements to use
       // for display. So the best way seems to pass the information here - ideally
       // the sdk wouldn't need to know this, but we can't change that.
@@ -243,24 +244,26 @@ loop.roomViews = (function(mozL10n) {
         visible: true
       };
 
       switch(this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
-          return <loop.conversationViews.GenericFailureView
-            cancelCall={this.closeWindow}
-          />;
+          return (
+            <loop.conversationViews.GenericFailureView
+              cancelCall={this.closeWindow} />
+          );
         }
         case ROOM_STATES.ENDED: {
-          return <sharedViews.FeedbackView
-            onAfterFeedbackReceived={this.closeWindow}
-          />;
+          return (
+            <sharedViews.FeedbackView
+              onAfterFeedbackReceived={this.closeWindow} />
+          );
         }
         default: {
           return (
             <div className="room-conversation-wrapper">
               {this._renderInvitationOverlay()}
               <div className="video-layout-wrapper">
                 <div className="conversation room-conversation">
                   <div className="media nested">
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/.eslintrc
@@ -0,0 +1,14 @@
+{
+  "ecmaFeatures": {
+    // Turn off top-level items needed for the jsm files, but not wanted
+    // for shared code as we can't support them.
+    "blockBindings": false,
+    "arrowFunctions": false,
+    "destructuring": false,
+    "forOf": true,
+    "generators": false,
+    "spread": false,
+    "restParams": false,
+    "objectLiteralShorthandMethods": false
+  }
+}
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -967,21 +967,24 @@ html, .fx-embedded, #main,
 
 .standalone .room-conversation-wrapper .room-inner-info-area a.btn {
   padding: .5em 3em .3em 3em;
   border-radius: 3px;
   font-weight: normal;
   max-width: 400px;
 }
 
-.standalone .room-conversation h2.room-name {
+.standalone .room-conversation h2.room-name,
+.standalone .room-conversation h2.room-info-failure {
   position: absolute;
   display: inline-block;
   top: 0;
-  right: 0;
+  right: 10px;
+  /* 20px is 10px for left and right margins. */
+  width: calc(25% - 20px);
   color: #fff;
   z-index: 2000000;
   font-size: 1.2em;
   padding: .4em;
 }
 
 .standalone .room-conversation .media {
   background: #000;
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -38,17 +38,18 @@ loop.shared.actions = (function() {
     GetWindowData: Action.define("getWindowData", {
       windowId: String
     }),
 
     /**
      * Extract the token information and type for the standalone window
      */
     ExtractTokenInfo: Action.define("extractTokenInfo", {
-      windowPath: String
+      windowPath: String,
+      windowHash: String
     }),
 
     /**
      * Used to pass round the window data so that stores can
      * record the appropriate data.
      */
     SetupWindowData: Action.define("setupWindowData", {
       windowId: String,
@@ -60,16 +61,17 @@ loop.shared.actions = (function() {
       // data.
     }),
 
     /**
      * Used to fetch the data from the server for a room or call for the
      * token.
      */
     FetchServerData: Action.define("fetchServerData", {
+      // cryptoKey: String - Optional.
       token: String,
       windowType: String
     }),
 
     /**
      * Used to signal when the window is being unloaded.
      */
     WindowUnload: Action.define("windowUnload", {
@@ -381,16 +383,17 @@ loop.shared.actions = (function() {
 
     /**
      * Updates the room information when it is received.
      * XXX: should move to some roomActions module - refs bug 1079284
      *
      * @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
      */
     UpdateRoomInfo: Action.define("updateRoomInfo", {
+      // context: Object - Optional.
       // roomName: String - Optional.
       roomOwner: String,
       roomUrl: String
     }),
 
     /**
      * Starts the process for the user to join the room.
      * XXX: should move to some roomActions module - refs bug 1079284
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -6,25 +6,28 @@
 
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.ActiveRoomStore = (function() {
   "use strict";
 
   var sharedActions = loop.shared.actions;
+  var crypto = loop.crypto;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   // Error numbers taken from
   // https://github.com/mozilla-services/loop-server/blob/master/loop/errno.json
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
 
   var ROOM_STATES = loop.store.ROOM_STATES;
 
+  var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
+
   /**
    * Active room store.
    *
    * @param {loop.Dispatcher} dispatcher  The dispatcher for dispatching actions
    *                                      and registering to consume actions.
    * @param {Object} options Options object:
    * - {mozLoop}     mozLoop    The MozLoop API object.
    * - {OTSdkDriver} sdkDriver  The SDK driver instance.
@@ -71,17 +74,21 @@ loop.store.ActiveRoomStore = (function()
         // Tracks if the room has been used during this
         // session. 'Used' means at least one call has been placed
         // with it. Entering and leaving the room without seeing
         // anyone is not considered as 'used'
         used: false,
         localVideoDimensions: {},
         remoteVideoDimensions: {},
         screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
-        receivingScreenShare: false
+        receivingScreenShare: false,
+        // The roomCryptoKey to decode the context data if necessary.
+        roomCryptoKey: null,
+        // Room information failed to be obtained for a reason. See ROOM_INFO_FAILURES.
+        roomInfoFailure: null
       };
     },
 
     /**
      * Handles a room failure.
      *
      * @param {sharedActions.RoomFailure} actionData
      */
@@ -194,35 +201,83 @@ loop.store.ActiveRoomStore = (function()
         // Nothing for us to do here, leave it to other stores.
         return;
       }
 
       this._registerPostSetupActions();
 
       this.setStoreState({
         roomToken: actionData.token,
+        roomCryptoKey: actionData.cryptoKey,
         roomState: ROOM_STATES.READY
       });
 
       this._mozLoop.rooms.on("update:" + actionData.roomToken,
         this._handleRoomUpdate.bind(this));
       this._mozLoop.rooms.on("delete:" + actionData.roomToken,
         this._handleRoomDelete.bind(this));
 
-      this._mozLoop.rooms.get(this._storeState.roomToken,
-        function(err, result) {
-          if (err) {
-            // XXX Bug 1110937 will want to handle the error results here
-            // e.g. room expired/invalid.
-            console.error("Failed to get room data:", err);
-            return;
-          }
+      this._getRoomDataForStandalone();
+    },
+
+    _getRoomDataForStandalone: function() {
+      this._mozLoop.rooms.get(this._storeState.roomToken, function(err, result) {
+        if (err) {
+          // XXX Bug 1110937 will want to handle the error results here
+          // e.g. room expired/invalid.
+          console.error("Failed to get room data:", err);
+          return;
+        }
+
+        var roomInfoData = new sharedActions.UpdateRoomInfo({
+          roomOwner: result.roomOwner,
+          roomUrl: result.roomUrl
+        });
+
+        if (!result.context && !result.roomName) {
+          roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.NO_DATA;
+          this.dispatcher.dispatch(roomInfoData);
+          return;
+        }
 
-          this.dispatcher.dispatch(new sharedActions.UpdateRoomInfo(result));
-        }.bind(this));
+        // This handles 'legacy', non-encrypted room names.
+        if (result.roomName && !result.context) {
+          roomInfoData.roomName = result.roomName;
+          this.dispatcher.dispatch(roomInfoData);
+          return;
+        }
+
+        if (!crypto.isSupported()) {
+          roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED;
+          this.dispatcher.dispatch(roomInfoData);
+          return;
+        }
+
+        var roomCryptoKey = this.getStoreState("roomCryptoKey");
+
+        if (!roomCryptoKey) {
+          roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.NO_CRYPTO_KEY;
+          this.dispatcher.dispatch(roomInfoData);
+          return;
+        }
+
+        var dispatcher = this.dispatcher;
+
+        crypto.decryptBytes(roomCryptoKey, result.context.value)
+              .then(function(decryptedResult) {
+          var realResult = JSON.parse(decryptedResult);
+
+          roomInfoData.roomName = realResult.roomName;
+
+          dispatcher.dispatch(roomInfoData);
+        }, function(err) {
+          roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.DECRYPT_FAILED;
+          dispatcher.dispatch(roomInfoData);
+        });
+      }.bind(this));
     },
 
     /**
      * Handles the setupRoomInfo action. Sets up the initial room data and
      * sets the state to `READY`.
      *
      * @param {sharedActions.SetupRoomInfo} actionData
      */
@@ -249,16 +304,17 @@ loop.store.ActiveRoomStore = (function()
 
     /**
      * Handles the updateRoomInfo action. Updates the room data.
      *
      * @param {sharedActions.UpdateRoomInfo} actionData
      */
     updateRoomInfo: function(actionData) {
       this.setStoreState({
+        roomInfoFailure: actionData.roomInfoFailure,
         roomName: actionData.roomName,
         roomOwner: actionData.roomOwner,
         roomUrl: actionData.roomUrl
       });
     },
 
     /**
      * Handles room updates notified by the mozLoop rooms API.
--- a/browser/components/loop/content/shared/js/mixins.js
+++ b/browser/components/loop/content/shared/js/mixins.js
@@ -436,17 +436,17 @@ loop.shared.mixins = (function() {
     /**
      * Returns the default configuration for publishing media on the sdk.
      *
      * @param {Object} options An options object containing:
      * - publishVideo A boolean set to true to publish video when the stream is initiated.
      */
     getDefaultPublisherConfig: function(options) {
       options = options || {};
-      if (!"publishVideo" in options) {
+      if (!("publishVideo" in options)) {
         throw new Error("missing option publishVideo");
       }
 
       // height set to 100%" to fix video layout on Google Chrome
       // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
       return {
         insertMode: "append",
         fitMode: "contain",
--- a/browser/components/loop/content/shared/js/utils.js
+++ b/browser/components/loop/content/shared/js/utils.js
@@ -38,16 +38,27 @@ loop.shared.utils = (function(mozL10n) {
     MEDIA_DENIED: "reason-media-denied",
     UNABLE_TO_PUBLISH_MEDIA: "unable-to-publish-media",
     COULD_NOT_CONNECT: "reason-could-not-connect",
     NETWORK_DISCONNECTED: "reason-network-disconnected",
     EXPIRED_OR_INVALID: "reason-expired-or-invalid",
     UNKNOWN: "reason-unknown"
   };
 
+  var ROOM_INFO_FAILURES = {
+    // There's no data available from the server.
+    NO_DATA: "no_data",
+    // WebCrypto is unsupported in this browser.
+    WEB_CRYPTO_UNSUPPORTED: "web_crypto_unsupported",
+    // The room is missing the crypto key information.
+    NO_CRYPTO_KEY: "no_crypto_key",
+    // Decryption failed.
+    DECRYPT_FAILED: "decrypt_failed"
+  };
+
   var STREAM_PROPERTIES = {
     VIDEO_DIMENSIONS: "videoDimensions",
     HAS_AUDIO: "hasAudio",
     HAS_VIDEO: "hasVideo"
   };
 
   var SCREEN_SHARE_STATES = {
     INACTIVE: "ss-inactive",
@@ -126,16 +137,126 @@ loop.shared.utils = (function(mozL10n) {
     if (/BlackBerry/i.test(platform)) {
       return "blackberry";
     }
 
     return null;
   }
 
   /**
+   * Helper to get the Operating System name.
+   *
+   * @param {String}  [platform]    The platform this is running on, will fall
+   *                                back to navigator.oscpu and navigator.userAgent
+   *                                respectively if not supplied.
+   * @param {Boolean} [withVersion] Optional flag to keep the version number
+   *                                included in the resulting string. Defaults to
+   *                                `false`.
+   * @return {String} The platform we're currently running on, in lower-case.
+   */
+  var getOS = _.memoize(function(platform, withVersion) {
+    if (!platform) {
+      if ("oscpu" in window.navigator) {
+        // See https://developer.mozilla.org/en-US/docs/Web/API/Navigator/oscpu
+        platform = window.navigator.oscpu.split(";")[0].trim();
+      } else {
+        // Fall back to navigator.userAgent as a last resort.
+        platform = window.navigator.userAgent;
+      }
+    }
+
+    if (!platform) {
+      return "unknown";
+    }
+
+    // Support passing in navigator.userAgent.
+    var platformPart = platform.match(/\((.*)\)/);
+    if (platformPart) {
+      platform = platformPart[1];
+    }
+    platform = platform.toLowerCase().split(";");
+    if (/macintosh/.test(platform[0]) || /x11/.test(platform[0])) {
+      platform = platform[1];
+    } else {
+      if (platform[0].indexOf("win") > -1 && platform.length > 4) {
+        // Skip the security notation.
+        platform = platform[2];
+      } else {
+        platform = platform[0];
+      }
+    }
+
+    if (!withVersion) {
+      platform = platform.replace(/\s[0-9.]+/g, "");
+    }
+
+    return platform.trim();
+  }, function(platform, withVersion) {
+    // Cache the return values with the following key.
+    return (platform + "") + (withVersion + "");
+  });
+
+  /**
+   * Helper to get the Operating System version.
+   * See http://en.wikipedia.org/wiki/Windows_NT for a table of Windows NT
+   * versions.
+   *
+   * @param {String} [platform] The platform this is running on, will fall back
+   *                            to navigator.oscpu and navigator.userAgent
+   *                            respectively if not supplied.
+   * @return {String} The current version of the platform we're currently running
+   *                  on.
+   */
+  var getOSVersion = _.memoize(function(platform) {
+    var os = getOS(platform, true);
+    var digitsRE = /\s([0-9.]+)/;
+
+    var version = os.match(digitsRE);
+    if (!version) {
+      if (os.indexOf("win") > -1) {
+        if (os.indexOf("xp")) {
+          return { major: 5, minor: 2 };
+        } else if (os.indexOf("vista") > -1) {
+          return { major: 6, minor: 0 };
+        }
+      }
+    } else {
+      version = version[1];
+      // Windows versions have an interesting scheme.
+      if (os.indexOf("win") > -1) {
+        switch (parseFloat(version)) {
+          case 98:
+            return { major: 4, minor: 1 };
+          case 2000:
+            return { major: 5, minor: 0 };
+          case 2003:
+            return { major: 5, minor: 2 };
+          case 7:
+          case 2008:
+          case 2011:
+            return { major: 6, minor: 1 };
+          case 8:
+            return { major: 6, minor: 2 };
+          case 8.1:
+          case 2012:
+            return { major: 6, minor: 3 };
+        }
+      }
+
+      version = version.split(".");
+      return {
+        major: parseInt(version[0].trim(), 10),
+        minor: parseInt(version[1] ? version[1].trim() : 0, 10)
+      };
+    }
+
+    return { major: Infinity, minor: 0 };
+  });
+
+  /**
    * Helper to allow getting some of the location data in a way that's compatible
    * with stubbing for unit tests.
    */
   function locationData() {
     return {
       hash: window.location.hash,
       pathname: window.location.pathname
     };
@@ -163,16 +284,22 @@ loop.shared.utils = (function(mozL10n) {
         clientShortname2: mozL10n.get("clientShortname2"),
         clientSuperShortname: mozL10n.get("clientSuperShortname"),
         learnMoreUrl: navigator.mozLoop.getLoopPref("learnMoreUrl")
       }).replace(/\r\n/g, "\n").replace(/\n/g, "\r\n"),
       recipient
     );
   }
 
+  // We can alias `subarray` to `slice` when the latter is not available, because
+  // they're semantically identical.
+  if (!Uint8Array.prototype.slice) {
+    Uint8Array.prototype.slice = Uint8Array.prototype.subarray;
+  }
+
   /**
    * Binary-compatible Base64 decoding.
    *
    * Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
    *
    * @param {String} base64str The string to decode.
    * @return {Uint8Array} The decoded result in array format.
    */
@@ -392,19 +519,22 @@ loop.shared.utils = (function(mozL10n) {
 
   return {
     CALL_TYPES: CALL_TYPES,
     FAILURE_DETAILS: FAILURE_DETAILS,
     REST_ERRNOS: REST_ERRNOS,
     WEBSOCKET_REASONS: WEBSOCKET_REASONS,
     STREAM_PROPERTIES: STREAM_PROPERTIES,
     SCREEN_SHARE_STATES: SCREEN_SHARE_STATES,
+    ROOM_INFO_FAILURES: ROOM_INFO_FAILURES,
     composeCallUrlEmail: composeCallUrlEmail,
     formatDate: formatDate,
     getBoolPreference: getBoolPreference,
+    getOS: getOS,
+    getOSVersion: getOSVersion,
     isChrome: isChrome,
     isFirefox: isFirefox,
     isFirefoxOS: isFirefoxOS,
     isOpera: isOpera,
     getUnsupportedPlatform: getUnsupportedPlatform,
     locationData: locationData,
     atob: atob,
     btoa: btoa,
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -88,16 +88,27 @@ loop.shared.views = (function(_, l10n) {
     mixins: [sharedMixins.DropdownMenuMixin],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       visible: React.PropTypes.bool.isRequired,
       state: React.PropTypes.string.isRequired,
     },
 
+    getInitialState: function() {
+      var os = loop.shared.utils.getOS();
+      var osVersion = loop.shared.utils.getOSVersion();
+      // Disable screensharing on older OSX and Windows versions.
+      if ((os.indexOf("mac") > -1 && osVersion.major <= 10 && osVersion.minor <= 6) ||
+          (os.indexOf("win") > -1 && osVersion.major <= 5 && osVersion.minor <= 2)) {
+        return { windowSharingDisabled: true };
+      }
+      return { windowSharingDisabled: false };
+    },
+
     handleClick: function() {
       if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) {
         this.props.dispatcher.dispatch(
           new sharedActions.EndScreenShare({}));
       } else {
         this.toggleDropdownMenu();
       }
     },
@@ -139,29 +150,32 @@ loop.shared.views = (function(_, l10n) {
         "active": isActive,
         "disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
       });
       var dropdownMenuClasses = cx({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showMenu
       });
+      var windowSharingClasses = cx({
+        "disabled": this.state.windowSharingDisabled
+      });
 
       return (
         React.createElement("div", null, 
           React.createElement("button", {className: screenShareClasses, 
                   onClick: this.handleClick, 
                   title: this._getTitle()}, 
             isActive ? null : React.createElement("span", {className: "chevron"})
           ), 
           React.createElement("ul", {ref: "menu", className: dropdownMenuClasses}, 
             React.createElement("li", {onClick: this._handleShareTabs}, 
-              l10n.get("share_tabs_button_title")
+              l10n.get("share_tabs_button_title2")
             ), 
-            React.createElement("li", {onClick: this._handleShareWindows}, 
+            React.createElement("li", {onClick: this._handleShareWindows, className: windowSharingClasses}, 
               l10n.get("share_windows_button_title")
             )
           )
         )
       );
     }
   });
 
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -88,16 +88,27 @@ loop.shared.views = (function(_, l10n) {
     mixins: [sharedMixins.DropdownMenuMixin],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       visible: React.PropTypes.bool.isRequired,
       state: React.PropTypes.string.isRequired,
     },
 
+    getInitialState: function() {
+      var os = loop.shared.utils.getOS();
+      var osVersion = loop.shared.utils.getOSVersion();
+      // Disable screensharing on older OSX and Windows versions.
+      if ((os.indexOf("mac") > -1 && osVersion.major <= 10 && osVersion.minor <= 6) ||
+          (os.indexOf("win") > -1 && osVersion.major <= 5 && osVersion.minor <= 2)) {
+        return { windowSharingDisabled: true };
+      }
+      return { windowSharingDisabled: false };
+    },
+
     handleClick: function() {
       if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) {
         this.props.dispatcher.dispatch(
           new sharedActions.EndScreenShare({}));
       } else {
         this.toggleDropdownMenu();
       }
     },
@@ -139,29 +150,32 @@ loop.shared.views = (function(_, l10n) {
         "active": isActive,
         "disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
       });
       var dropdownMenuClasses = cx({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showMenu
       });
+      var windowSharingClasses = cx({
+        "disabled": this.state.windowSharingDisabled
+      });
 
       return (
         <div>
           <button className={screenShareClasses}
                   onClick={this.handleClick}
                   title={this._getTitle()}>
             {isActive ? null : <span className="chevron"/>}
           </button>
           <ul ref="menu" className={dropdownMenuClasses}>
             <li onClick={this._handleShareTabs}>
-              {l10n.get("share_tabs_button_title")}
+              {l10n.get("share_tabs_button_title2")}
             </li>
-            <li onClick={this._handleShareWindows}>
+            <li onClick={this._handleShareWindows} className={windowSharingClasses}>
               {l10n.get("share_windows_button_title")}
             </li>
           </ul>
         </div>
       );
     }
   });
 
--- a/browser/components/loop/run-all-loop-tests.sh
+++ b/browser/components/loop/run-all-loop-tests.sh
@@ -5,27 +5,39 @@ if [ "$1" == "--help" ]; then
   echo "Usage: ./run-all-loop-tests.sh [options]"
   echo "    --skip-e10s  Skips the e10s tests"
   exit 0;
 fi
 
 set -e
 
 # Main tests
-./mach xpcshell-test browser/components/loop/
-./mach marionette-test browser/components/loop/manifest.ini
+
+LOOPDIR=browser/components/loop
+ESLINT=standalone/node_modules/.bin/eslint
+if [ -x "${LOOPDIR}/${ESLINT}" ]; then
+  echo 'running eslint; see http://eslint.org/docs/rules/ for error info'
+  (cd ${LOOPDIR} && ./${ESLINT} --ext .js --ext .jsm --ext .jsx .)
+  if [ $? != 0 ]; then
+    exit 1;
+  fi
+  echo 'eslint run finished.'
+fi
+
+./mach xpcshell-test ${LOOPDIR}/
+./mach marionette-test ${LOOPDIR}/manifest.ini
 
 # The browser_parsable_css.js can fail if we add some css that isn't parsable.
 #
 # The check to make sure that the media devices can be used in Loop without
 # prompting is in browser_devices_get_user_media_about_urls.js. It's possible
 # to mess this up with CSP handling, and probably other changes, too.
 
 TESTS="
-  browser/components/loop/test/mochitest
+  ${LOOPDIR}/test/mochitest
   browser/modules/test/browser_UITour_loop.js
   browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
 "
 
 ./mach mochitest $TESTS
 
 if [ "$1" != "--skip-e10s" ]; then
   ./mach mochitest --e10s $TESTS
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/standalone/.eslintrc
@@ -0,0 +1,14 @@
+{
+  "ecmaFeatures": {
+    // Turn off top-level items needed for the jsm files, but not wanted
+    // for shared code as we can't support them.
+    "blockBindings": false,
+    "arrowFunctions": false,
+    "destructuring": false,
+    "forOf": true,
+    "generators": false,
+    "spread": false,
+    "restParams": false,
+    "objectLiteralShorthandMethods": false
+  }
+}
--- a/browser/components/loop/standalone/.gitignore
+++ b/browser/components/loop/standalone/.gitignore
@@ -1,14 +1,5 @@
 .module-cache
 node_modules
-bower_components
 *.pyc
 content/config.js
 content/VERSION.txt
-
-# XXX Once a grunt contrib-clean command has been added (bug 1066491), or
-# once legal has centralized their ToS and PP hosting infrastructure,
-# (expected Q4 2014) the legal doc build stuff for Loop can be removed,
-# including the following three lines:
-content/legal/styles/*.css
-content/legal/terms/*.html
-!content/legal/terms/index.html
deleted file mode 100644
--- a/browser/components/loop/standalone/Gruntfile.js
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env node
-
-/* 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/. */
-
-module.exports = function (grunt) {
-  'use strict';
-
-  // show elapsed time at the end
-  require('time-grunt')(grunt);
-
-  // load all grunt tasks
-  require('load-grunt-tasks')(grunt, {scope: 'devDependencies'});
-
-  grunt.initConfig({
-  });
-
-  grunt.loadTasks('grunttasks');
-}
-;
--- a/browser/components/loop/standalone/Makefile
+++ b/browser/components/loop/standalone/Makefile
@@ -1,47 +1,34 @@
 # 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/.
 
 # Note that this Makefile is not invoked by the Mozilla build
 # system, which is why it can have dependencies on things the
 # build system at-large doesn't yet support.
 
-# XXX In the interest of making the build logic simpler and
-# more maintainable, we should be trying to implement new
-# functionality in Gruntfile.js rather than here.
-# Bug 1066176 tracks moving all functionality currently here
-# to the Gruntfile and getting rid of this Makefile entirely.
-
 LOOP_SERVER_URL := $(shell echo $${LOOP_SERVER_URL-http://localhost:5000/v0})
 LOOP_FEEDBACK_API_URL := $(shell echo $${LOOP_FEEDBACK_API_URL-"https://input.allizom.org/api/v1/feedback"})
 LOOP_FEEDBACK_PRODUCT_NAME := $(shell echo $${LOOP_FEEDBACK_PRODUCT_NAME-Loop})
 LOOP_BRAND_WEBSITE_URL := $(shell echo $${LOOP_BRAND_WEBSITE_URL-"https://www.mozilla.org/firefox/"})
 LOOP_PRIVACY_WEBSITE_URL := $(shell echo $${LOOP_PRIVACY_WEBSITE_URL-"https://www.mozilla.org/privacy/firefox-hello/"})
 LOOP_LEGAL_WEBSITE_URL := $(shell echo $${LOOP_LEGAL_WEBSITE_URL-"https://www.mozilla.org/about/legal/terms/firefox-hello/"})
 LOOP_PRODUCT_HOMEPAGE_URL := $(shell echo $${LOOP_PRODUCT_HOMEPAGE_URL-"https://www.firefox.com/hello/"})
 
 NODE_LOCAL_BIN=./node_modules/.bin
 
-install: npm_install tos
+install: npm_install
 
 npm_install:
 	@npm install
 
 test:
 	@echo "Not implemented yet."
 
-tos:
-	@$(NODE_LOCAL_BIN)/grunt replace marked
-	@$(NODE_LOCAL_BIN)/grunt sass
-
-lint:
-	@$(NODE_LOCAL_BIN)/jshint *.js content test
-
 runserver: remove_old_config
 	node server.js
 
 frontend:
 	@echo "Not implemented yet."
 
 # Try hg first, if not fall back to git.
 SOURCE_STAMP := $(shell hg parent --template '{node|short}\n' 2> /dev/null)
--- a/browser/components/loop/standalone/README.md
+++ b/browser/components/loop/standalone/README.md
@@ -4,23 +4,20 @@ Loop Client
 Prerequisites
 -------------
 
 NodeJS and npm installed.
 
 Installation
 ------------
 
-Fetch and install/build any NPM and bower dependencies, as well as the
-localized Terms-of-Service content:
+Fetch and install/build any NPM dependencies:
 
     $ make install
 
-Some of the above is driven by Gruntfile.js.
-
 Configuration
 -------------
 
 If you need a static config.js file for deployment (most people wont; only
 folks deploying the development server will!), you can generate one like this:
 
     $ make config
 
@@ -46,19 +43,14 @@ For development, run a local static file
 Then point your browser at:
 
 - `http://localhost:3000/content/` for all public webapp contents,
 - `http://localhost:3000/test/` for tests.
 
 **Note:** the provided static file server for web contents is **not** intended
 for production use.
 
-Code linting
-------------
-
-    $ make lint
-
 License
 -------
 
 The Loop server code is released under the terms of the
 [Mozilla Public License v2.0](http://www.mozilla.org/MPL/2.0/). See the
 `LICENSE` file at the root of the repository.
deleted file mode 100644
--- a/browser/components/loop/standalone/bower.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "name": "loop-client",
-  "dependencies": {
-    "tos-pp": "https://github.com/mozilla/legal-docs.git"
-  },
-  "devDependencies": {}
-}
--- a/browser/components/loop/standalone/content/index.html
+++ b/browser/components/loop/standalone/content/index.html
@@ -103,16 +103,17 @@
     <script type="text/javascript" src="shared/libs/react-0.12.2.js"></script>
     <script type="text/javascript" src="shared/libs/jquery-2.1.0.js"></script>
     <script type="text/javascript" src="shared/libs/lodash-2.4.1.js"></script>
     <script type="text/javascript" src="shared/libs/backbone-1.1.2.js"></script>
 
     <!-- app scripts -->
     <script type="text/javascript" src="config.js"></script>
     <script type="text/javascript" src="shared/js/utils.js"></script>
+    <script type="text/javascript" src="shared/js/crypto.js"></script>
     <script type="text/javascript" src="shared/js/models.js"></script>
     <script type="text/javascript" src="shared/js/mixins.js"></script>
     <script type="text/javascript" src="shared/js/feedbackApiClient.js"></script>
     <script type="text/javascript" src="shared/js/actions.js"></script>
     <script type="text/javascript" src="shared/js/validate.js"></script>
     <script type="text/javascript" src="shared/js/dispatcher.js"></script>
     <script type="text/javascript" src="shared/js/websocket.js"></script>
     <script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
--- a/browser/components/loop/standalone/content/js/standaloneAppStore.js
+++ b/browser/components/loop/standalone/content/js/standaloneAppStore.js
@@ -91,18 +91,30 @@ loop.store.StandaloneAppStore = (functio
             windowType = "room";
           }
         }
       }
       return [windowType, match && match[1] ? match[1] : null];
     },
 
     /**
+     * Extracts the crypto key from the hash for the page.
+     */
+    _extractCryptoKey: function(windowHash) {
+      if (windowHash && windowHash[0] === "#") {
+        return windowHash.substring(1, windowHash.length);
+      }
+
+      return null;
+    },
+
+    /**
      * Handles the extract token info action - obtains the token information
-     * and its type; updates the store and notifies interested components.
+     * and its type; extracts any crypto information; updates the store and
+     * notifies interested components.
      *
      * @param {sharedActions.GetWindowData} actionData The action data
      */
     extractTokenInfo: function(actionData) {
       var windowType = "home";
       var token;
 
       // Check if we're on a supported device/platform.
@@ -130,16 +142,17 @@ loop.store.StandaloneAppStore = (functio
         isFirefox: sharedUtils.isFirefox(navigator.userAgent),
         unsupportedPlatform: unsupportedPlatform
       });
 
       // If we've not got a window ID, don't dispatch the action, as we don't need
       // it.
       if (token) {
         this._dispatcher.dispatch(new loop.shared.actions.FetchServerData({
+          cryptoKey: this._extractCryptoKey(actionData.windowHash),
           token: token,
           windowType: windowType
         }));
       }
     }
   }, Backbone.Events);
 
   return StandaloneAppStore;
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -7,16 +7,17 @@
 /* global loop:true, React */