Merge mozilla-central to inbound. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Fri, 20 Apr 2018 12:15:48 +0300
changeset 468283 d642b603b348dd8a8d69bf983b1b66ecfc9b7cc1
parent 468282 06de003da627c1657b54c5b56b11a8dc2987860d (current diff)
parent 468170 cc0d7de218cb0c260c8ba0cf6637845ad2222f49 (diff)
child 468284 89504aa6f1b32e195ce4e12584fb5594fc328ea2
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
dom/html/test/test_bug1166138.html
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4520,20 +4520,16 @@ var XULBrowserWindow = {
     delete this.canViewSource;
     return this.canViewSource = document.getElementById("canViewSource");
   },
 
   setJSStatus() {
     // unsupported
   },
 
-  forceInitialBrowserRemote(aRemoteType) {
-    gBrowser.updateBrowserRemoteness(gBrowser.initialBrowser, true, { remoteType: aRemoteType });
-  },
-
   forceInitialBrowserNonRemote(aOpener) {
     gBrowser.updateBrowserRemoteness(gBrowser.initialBrowser, false, { opener: aOpener });
   },
 
   setDefaultStatus(status) {
     this.defaultStatus = status;
     StatusPanel.update();
   },
--- a/browser/extensions/formautofill/content/manageDialog.js
+++ b/browser/extensions/formautofill/content/manageDialog.js
@@ -232,16 +232,19 @@ class ManageRecords {
    * Handle key press events
    *
    * @param  {DOMEvent} event
    */
   handleKeyPress(event) {
     if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
       window.close();
     }
+    if (event.keyCode == KeyEvent.DOM_VK_DELETE) {
+      this.removeRecords(this._selectedOptions);
+    }
   }
 
   observe(subject, topic, data) {
     switch (topic) {
       case "formautofill-storage-changed": {
         this.loadRecords();
       }
     }
--- a/browser/extensions/formautofill/test/browser/browser_manageAddressesDialog.js
+++ b/browser/extensions/formautofill/test/browser/browser_manageAddressesDialog.js
@@ -62,16 +62,33 @@ add_task(async function test_removingSin
 
   EventUtils.synthesizeMouseAtCenter(btnRemove, {}, win);
   await BrowserTestUtils.waitForEvent(selRecords, "RecordsRemoved");
   is(selRecords.length, 0, "All addresses are removed");
 
   win.close();
 });
 
+add_task(async function test_removingAdressViaKeyboardDelete() {
+  await saveAddress(TEST_ADDRESS_1);
+  let win = window.openDialog(MANAGE_ADDRESSES_DIALOG_URL, null, DIALOG_SIZE);
+  await waitForFocusAndFormReady(win);
+
+  let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
+
+  is(selRecords.length, 1, "One address");
+
+  EventUtils.synthesizeMouseAtCenter(selRecords.children[0], {}, win);
+  EventUtils.synthesizeKey("VK_DELETE", {}, win);
+  await BrowserTestUtils.waitForEvent(selRecords, "RecordsRemoved");
+  is(selRecords.length, 0, "No addresses left");
+
+  win.close();
+});
+
 add_task(async function test_addressesDialogWatchesStorageChanges() {
   let win = window.openDialog(MANAGE_ADDRESSES_DIALOG_URL, null, DIALOG_SIZE);
   await waitForFocusAndFormReady(win);
 
   let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
 
   await saveAddress(TEST_ADDRESS_1);
   await BrowserTestUtils.waitForEvent(selRecords, "RecordsLoaded");
--- a/browser/extensions/formautofill/test/browser/browser_manageCreditCardsDialog.js
+++ b/browser/extensions/formautofill/test/browser/browser_manageCreditCardsDialog.js
@@ -69,16 +69,33 @@ add_task(async function test_removingSin
 
   EventUtils.synthesizeMouseAtCenter(btnRemove, {}, win);
   await BrowserTestUtils.waitForEvent(selRecords, "RecordsRemoved");
   is(selRecords.length, 0, "All credit cards are removed");
 
   win.close();
 });
 
+add_task(async function test_removingCreditCardsViaKeyboardDelete() {
+  await saveCreditCard(TEST_CREDIT_CARD_1);
+  let win = window.openDialog(MANAGE_CREDIT_CARDS_DIALOG_URL, null, DIALOG_SIZE);
+  await waitForFocusAndFormReady(win);
+
+  let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
+
+  is(selRecords.length, 1, "One credit card");
+
+  EventUtils.synthesizeMouseAtCenter(selRecords.children[0], {}, win);
+  EventUtils.synthesizeKey("VK_DELETE", {}, win);
+  await BrowserTestUtils.waitForEvent(selRecords, "RecordsRemoved");
+  is(selRecords.length, 0, "No credit cards left");
+
+  win.close();
+});
+
 add_task(async function test_creditCardsDialogWatchesStorageChanges() {
   let win = window.openDialog(MANAGE_CREDIT_CARDS_DIALOG_URL, null, DIALOG_SIZE);
   await waitForFocusAndFormReady(win);
 
   let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
 
   await saveCreditCard(TEST_CREDIT_CARD_1);
   await BrowserTestUtils.waitForEvent(selRecords, "RecordsLoaded");
--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -24,28 +24,26 @@ browser/chrome/browser/skin/classic/brow
 browser/chrome/browser/skin/classic/browser/controlcenter/warning-gray.svg
 # devtools reduction is bug 1311178
 browser/chrome/devtools/content/dom/content/dom-view.css
 browser/chrome/devtools/content/dom/dom.html
 browser/chrome/devtools/content/dom/main.js
 browser/chrome/devtools/content/framework/toolbox-options.js
 browser/chrome/devtools/content/inspector/fonts/fonts.js
 browser/chrome/devtools/content/inspector/inspector.xhtml
-browser/chrome/devtools/content/memory/initializer.js
 browser/chrome/devtools/content/projecteditor/lib/helpers/readdir.js
 browser/chrome/devtools/content/shared/theme-switching.js
 browser/chrome/devtools/modules/devtools/client/dom/content/dom-view.css
 browser/chrome/devtools/modules/devtools/client/dom/dom.html
 browser/chrome/devtools/modules/devtools/client/dom/main.js
 browser/chrome/devtools/modules/devtools/client/framework/toolbox-options.js
 browser/chrome/devtools/modules/devtools/client/inspector/fonts/fonts.js
 browser/chrome/devtools/modules/devtools/client/inspector/inspector.xhtml
 browser/chrome/devtools/modules/devtools/client/jsonview/css/controls.png
 browser/chrome/devtools/modules/devtools/client/jsonview/css/controls@2x.png
-browser/chrome/devtools/modules/devtools/client/memory/initializer.js
 browser/chrome/devtools/modules/devtools/client/projecteditor/lib/helpers/readdir.js
 browser/chrome/devtools/modules/devtools/client/shared/theme-switching.js
 browser/chrome/devtools/modules/devtools/client/themes/common.css
 browser/chrome/devtools/modules/devtools/client/themes/toolbars.css
 browser/chrome/devtools/modules/devtools/client/themes/variables.css
 browser/chrome/devtools/skin/common.css
 browser/chrome/devtools/skin/toolbars.css
 browser/chrome/devtools/skin/images/command-scratchpad.svg
--- a/build/checksums.py
+++ b/build/checksums.py
@@ -1,95 +1,80 @@
 #!/usr/bin/python
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import with_statement
 
 from optparse import OptionParser
+import hashlib
 import logging
 import os
-try:
-    import hashlib
-except ImportError:
-    hashlib = None
+
+logger = logging.getLogger('checksums.py')
 
 
-def digest_file(filename, digest, chunk_size=1024):
+def digest_file(filename, digest, chunk_size=131072):
     '''Produce a checksum for the file specified by 'filename'.  'filename'
     is a string path to a file that is opened and read in this function.  The
     checksum algorithm is specified by 'digest' and is a valid OpenSSL
     algorithm.  If the digest used is not valid or Python's hashlib doesn't
     work, the None object will be returned instead.  The size of blocks
     that this function will read from the file object it opens based on
     'filename' can be specified by 'chunk_size', which defaults to 1K'''
     assert not os.path.isdir(filename), 'this function only works with files'
-    logger = logging.getLogger('checksums.py')
-    if hashlib is not None:
-        logger.debug('Creating new %s object' % digest)
-        h = hashlib.new(digest)
-        with open(filename, 'rb') as f:
-            while True:
-                data = f.read(chunk_size)
-                if not data:
-                    logger.debug('Finished reading in file')
-                    break
-                h.update(data)
-        hash = h.hexdigest()
-        logger.debug('Hash for %s is %s' % (filename, hash))
-        return hash
-    else:
-        # In this case we could subprocess.Popen and .communicate with
-        # sha1sum or md5sum
-        logger.warn('The python module for hashlib is missing!')
-        return None
+
+    logger.debug('Creating new %s object' % digest)
+    h = hashlib.new(digest)
+    with open(filename, 'rb') as f:
+        while True:
+            data = f.read(chunk_size)
+            if not data:
+                logger.debug('Finished reading in file')
+                break
+            h.update(data)
+    hash = h.hexdigest()
+    logger.debug('Hash for %s is %s' % (filename, hash))
+    return hash
 
 
-def process_files(files, output_filename, digests, strip):
-    '''This function takes a list of file names, 'files'.  It will then
-    compute the checksum for each of the files by opening the files.
+def process_files(dirs, output_filename, digests):
+    '''This function takes a list of directory names, 'drs'. It will then
+    compute the checksum for each of the files in these by by opening the files.
     Once each file is read and its checksum is computed, this function
     will write the information to the file specified by 'output_filename'.
     The path written in the output file will have anything specified by 'strip'
     removed from the path.  The output file is closed before returning nothing
     The algorithm to compute checksums with can be specified by 'digests'
     and needs to be a list of valid OpenSSL algorithms.
 
     The output file is written in the format:
         <hash> <algorithm> <filesize> <filepath>
     Example:
         d1fa09a<snip>e4220 sha1 14250744 firefox-4.0b6pre.en-US.mac64.dmg
     '''
 
-    logger = logging.getLogger('checksums.py')
     if os.path.exists(output_filename):
         logger.debug('Overwriting existing checksums file "%s"' %
                      output_filename)
     else:
         logger.debug('Creating a new checksums file "%s"' % output_filename)
     with open(output_filename, 'w+') as output:
-        for file in files:
-            if os.path.isdir(file):
-                logger.warn('%s is a directory, skipping' % file)
-            else:
-                for digest in digests:
-                    hash = digest_file(file, digest)
-                    if hash is None:
-                        logger.warn('Unable to generate a hash for %s. ' +
-                                    'Skipping.' % file)
-                        continue
-                    if file.startswith(strip):
-                        short_file = file[len(strip):]
-                        short_file = short_file.lstrip('/')
-                    else:
-                        short_file = file
-                    print >>output, '%s %s %s %s' % (hash, digest,
-                                                     os.path.getsize(file),
-                                                     short_file)
+        for d in dirs:
+            for root, dirs, files in os.walk(d):
+                for f in files:
+                    full = os.path.join(root, f)
+                    rel = os.path.relpath(full, d)
+
+                    for digest in digests:
+                        hash = digest_file(full, digest)
+
+                        output.write('%s %s %s %s\n' % (
+                            hash, digest, os.path.getsize(full), rel))
 
 
 def setup_logging(level=logging.DEBUG):
     '''This function sets up the logging module using a speficiable logging
     module logging level.  The default log level is DEBUG.
 
     The output is in the format:
         <level> - <message>
@@ -115,48 +100,36 @@ def main():
                       action='append', dest='digests')
     parser.add_option('-o', '--output', help='output file to use',
                       action='store', dest='outfile', default='checksums')
     parser.add_option('-v', '--verbose',
                       help='Be noisy (takes precedence over quiet)',
                       action='store_true', dest='verbose', default=False)
     parser.add_option('-q', '--quiet', help='Be quiet', action='store_true',
                       dest='quiet', default=False)
-    parser.add_option('-s', '--strip',
-                      help='strip this path from the filenames',
-                      dest='strip', default=os.getcwd())
+
     options, args = parser.parse_args()
 
     # Figure out which logging level to use
     if options.verbose:
         loglevel = logging.DEBUG
     elif options.quiet:
         loglevel = logging.ERROR
     else:
         loglevel = logging.INFO
 
     # Set up logging
     setup_logging(loglevel)
-    logger = logging.getLogger('checksums.py')
 
     # Validate the digest type to use
     if not options.digests:
         options.digests = ['sha1']
-    try:
-        for digest in options.digests:
-            hashlib.new(digest)
-    except ValueError as ve:
-        logger.error('Could not create a "%s" hash object (%s)' %
-                     (digest, ve.args[0]))
-        exit(1)
 
-    # Validate the files to checksum
-    files = []
     for i in args:
-        if os.path.exists(i):
-            files.append(i)
-        else:
-            logger.info('File "%s" was not found on the filesystem' % i)
-    process_files(files, options.outfile, options.digests, options.strip)
+        if not os.path.isdir(i):
+            logger.error('%s is not a directory' % i)
+            exit(1)
+
+    process_files(args, options.outfile, options.digests)
 
 
 if __name__ == '__main__':
     main()
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -363,23 +363,24 @@ def shell(value, mozillabuild):
 
 
 # Python 3
 # ========
 
 option(env='PYTHON3', nargs=1, help='Python 3 interpreter (3.5 or later)')
 
 
-@depends('PYTHON3')
+@depends('PYTHON3', 'MOZ_AUTOMATION')
 @checking('for Python 3',
           callback=lambda x: '%s (%s)' % (x.path, x.str_version) if x else 'no')
 @imports(_from='__builtin__', _import='Exception')
+@imports('platform')
 @imports(_from='mozbuild.pythonutil', _import='find_python3_executable')
 @imports(_from='mozbuild.pythonutil', _import='python_executable_version')
-def python3(env_python):
+def python3(env_python, automation):
     python = env_python[0] if env_python else None
 
     # If Python given by environment variable, it must work.
     if python:
         try:
             version = python_executable_version(python).version
         except Exception as e:
             raise FatalCheckError('could not determine version of PYTHON '
@@ -387,21 +388,32 @@ def python3(env_python):
 
         if version < (3, 5, 0):
             raise FatalCheckError('PYTHON3 must point to Python 3.5 or newer; '
                                   '%d.%d found' % (version[0], version[1]))
     else:
         # Fall back to the search routine.
         python, version = find_python3_executable(min_version='3.5.0')
 
-        if not python:
+        # The API returns a bytes whereas everything in configure is unicode.
+        if python:
+            python = python.decode('utf-8')
+
+    # Outside of automation, require Python 3.5.
+    # In automation, only require where it is known to be installed.
+    require = not automation or platform.system() == 'Linux'
+
+    if not python:
+        if not require:
             return None
 
-        # The API returns a bytes whereas everything in configure is unicode.
-        python = python.decode('utf-8')
+        raise FatalCheckError('Python 3.5 or newer is required to build. '
+                              'Ensure a `python3.x` executable is in your '
+                              'PATH or define PYTHON3 to point to a Python '
+                              '3.5 executable.')
 
     return namespace(
         path=python,
         version=version,
         str_version='.'.join(str(v) for v in version),
     )
 
 
--- a/devtools/client/framework/components/toolbox-tabs.js
+++ b/devtools/client/framework/components/toolbox-tabs.js
@@ -7,32 +7,34 @@ const { Component, createFactory } = req
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {findDOMNode} = require("devtools/client/shared/vendor/react-dom");
 const {button, div} = dom;
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
+const { ToolboxTabsOrderManager } = require("devtools/client/framework/toolbox-tabs-order-manager");
 
 // 26px is chevron devtools button width.(i.e. tools-chevronmenu)
 const CHEVRON_BUTTON_WIDTH = 26;
 
 class ToolboxTabs extends Component {
   // See toolbox-toolbar propTypes for details on the props used here.
   static get propTypes() {
     return {
       currentToolId: PropTypes.string,
       focusButton: PropTypes.func,
       focusedButton: PropTypes.string,
       highlightedTools: PropTypes.object,
       panelDefinitions: PropTypes.array,
       selectTool: PropTypes.func,
       toolbox: PropTypes.object,
       L10N: PropTypes.object,
+      onTabsOrderUpdated: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       // Array of overflowed tool id.
@@ -41,16 +43,18 @@ class ToolboxTabs extends Component {
 
     // Map with tool Id and its width size. This lifecycle is out of React's
     // lifecycle. If a tool is registered, ToolboxTabs will add target tool id
     // to this map. ToolboxTabs will never remove tool id from this cache.
     this._cachedToolTabsWidthMap = new Map();
 
     this._resizeTimerId = null;
     this.resizeHandler = this.resizeHandler.bind(this);
+
+    this._tabsOrderManager = new ToolboxTabsOrderManager(props.onTabsOrderUpdated);
   }
 
   componentDidMount() {
     window.addEventListener("resize", this.resizeHandler);
     this.updateCachedToolTabsWidthMap();
     this.updateOverflowedTabs();
   }
 
@@ -64,16 +68,20 @@ class ToolboxTabs extends Component {
 
   componentDidUpdate(prevProps, prevState) {
     if (this.shouldUpdateToolboxTabs(prevProps, this.props)) {
       this.updateCachedToolTabsWidthMap();
       this.updateOverflowedTabs();
     }
   }
 
+  componentWillUnmount() {
+    this._tabsOrderManager.destroy();
+  }
+
   /**
    * Check if two array of ids are the same or not.
    */
   equalToolIdArray(prevPanels, nextPanels) {
     if (prevPanels.length !== nextPanels.length) {
       return false;
     }
 
@@ -226,16 +234,18 @@ class ToolboxTabs extends Component {
       currentToolId,
       focusButton,
       focusedButton,
       highlightedTools,
       panelDefinitions,
       selectTool,
     } = this.props;
 
+    this._tabsOrderManager.setOverflowedTabs(this.state.overflowedTabIds);
+
     let tabs = panelDefinitions.map(panelDefinition => {
       // Don't display overflowed tab.
       if (!this.state.overflowedTabIds.includes(panelDefinition.id)) {
         return ToolboxTab({
           key: panelDefinition.id,
           currentToolId,
           focusButton,
           focusedButton,
@@ -248,17 +258,18 @@ class ToolboxTabs extends Component {
     });
 
     return div(
       {
         className: "toolbox-tabs-wrapper"
       },
       div(
         {
-          className: "toolbox-tabs"
+          className: "toolbox-tabs",
+          onMouseDown: (e) => this._tabsOrderManager.onMouseDown(e),
         },
         tabs,
         (this.state.overflowedTabIds.length > 0)
           ? this.renderToolsChevronButton() : null
       )
     );
   }
 }
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -67,16 +67,18 @@ class ToolboxToolbar extends Component {
       focusButton: PropTypes.func,
       // Hold off displaying the toolbar until enough information is ready for
       // it to render nicely.
       canRender: PropTypes.bool,
       // Localization interface.
       L10N: PropTypes.object,
       // The devtools toolbox
       toolbox: PropTypes.object,
+      // Call back function to detect tabs order updated.
+      onTabsOrderUpdated: PropTypes.func.isRequired,
     };
   }
 
   /**
    * The render function is kept fairly short for maintainability. See the individual
    * render functions for how each of the sections is rendered.
    */
   render() {
--- a/devtools/client/framework/moz.build
+++ b/devtools/client/framework/moz.build
@@ -22,14 +22,15 @@ DevToolsModules(
     'sidebar.js',
     'source-map-url-service.js',
     'target-from-url.js',
     'target.js',
     'toolbox-highlighter-utils.js',
     'toolbox-host-manager.js',
     'toolbox-hosts.js',
     'toolbox-options.js',
+    'toolbox-tabs-order-manager.js',
     'toolbox.js',
     'ToolboxProcess.jsm',
 )
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools: Framework')
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -108,16 +108,17 @@ skip-if = e10s # Bug 1069044 - destroyIn
 [browser_toolbox_telemetry_close.js]
 [browser_toolbox_textbox_context_menu.js]
 [browser_toolbox_theme.js]
 [browser_toolbox_theme_registration.js]
 [browser_toolbox_toggle.js]
 [browser_toolbox_tool_ready.js]
 [browser_toolbox_tool_remote_reopen.js]
 [browser_toolbox_toolbar_overflow.js]
+[browser_toolbox_toolbar_reorder_by_dnd.js]
 [browser_toolbox_toolbar_reorder_by_width.js]
 [browser_toolbox_tools_per_toolbox_registration.js]
 [browser_toolbox_view_source_01.js]
 [browser_toolbox_view_source_02.js]
 [browser_toolbox_view_source_03.js]
 [browser_toolbox_view_source_04.js]
 [browser_toolbox_window_reload_target.js]
 [browser_toolbox_window_shortcuts.js]
--- a/devtools/client/framework/test/browser_browser_toolbox.js
+++ b/devtools/client/framework/test/browser_browser_toolbox.js
@@ -24,30 +24,33 @@ add_task(async function() {
       ["devtools.debugger.remote-timeout", 120000]
     ]};
     SpecialPowers.pushPrefEnv(options, done);
   });
 
   // Wait for a notification sent by a script evaluated in the webconsole
   // of the browser toolbox.
   let onCustomMessage = new Promise(done => {
-    Services.obs.addObserver(function listener() {
+    Services.obs.addObserver(function listener(target, aTop, data) {
       Services.obs.removeObserver(listener, "browser-toolbox-console-works");
-      done();
+      done(data === "true");
     }, "browser-toolbox-console-works");
   });
 
   // Be careful, this JS function is going to be executed in the addon toolbox,
   // which lives in another process. So do not try to use any scope variable!
   let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   let testScript = function() {
     toolbox.selectTool("webconsole")
       .then(console => {
+        // This is for checking Browser Toolbox doesn't have a close button.
+        let hasCloseButton = !!toolbox.doc.getElementById("toolbox-close");
         let { jsterm } = console.hud;
-        let js = "Services.obs.notifyObservers(null, 'browser-toolbox-console-works', null);";
+        let js = "Services.obs.notifyObservers(null, 'browser-toolbox-console-works', " +
+            hasCloseButton + ");";
         return jsterm.execute(js);
       })
       .then(() => toolbox.destroy());
   };
   env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
   registerCleanupFunction(() => {
     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
   });
@@ -60,15 +63,16 @@ add_task(async function() {
     closePromise = new Promise(onClose => {
       info("Opening the browser toolbox\n");
       BrowserToolboxProcess.init(onClose, onRun);
     });
   });
   ok(true, "Browser toolbox started\n");
   is(BrowserToolboxProcess.getBrowserToolboxSessionState(), true, "Has session state");
 
-  await onCustomMessage;
+  let hasCloseButton = await onCustomMessage;
   ok(true, "Received the custom message");
+  ok(!hasCloseButton, "Browser toolbox doesn't have a close button");
 
   await closePromise;
   ok(true, "Browser toolbox process just closed");
   is(BrowserToolboxProcess.getBrowserToolboxSessionState(), false, "No session state after closing");
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_toolbar_reorder_by_dnd.js
@@ -0,0 +1,182 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following reordering operation:
+// * DragAndDrop the target component to back
+// * DragAndDrop the target component to front
+// * DragAndDrop the target component over the starting of the tab
+// * DragAndDrop the target component over the ending of the tab
+// * Mouse was out from the document while dragging
+// * Select overflowed item, then DnD that
+//
+// This test is on the assumption which default toolbar has following tools.
+//   * inspector
+//   * webconsole
+//   * jsdebugger
+//   * styleeditor
+//   * performance
+//   * memory
+//   * netmonitor
+//   * storage
+
+const { Toolbox } = require("devtools/client/framework/toolbox");
+
+const TEST_STARTING_ORDER = ["inspector", "webconsole", "jsdebugger", "styleeditor",
+                             "performance", "memory", "netmonitor", "storage"];
+const TEST_DATA = [
+  {
+    description: "DragAndDrop the target component to back",
+    dragTarget: "toolbox-tab-webconsole",
+    dropTarget: "toolbox-tab-jsdebugger",
+    expectedOrder: ["inspector", "jsdebugger", "webconsole", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage"],
+  },
+  {
+    description: "DragAndDrop the target component to front",
+    dragTarget: "toolbox-tab-webconsole",
+    dropTarget: "toolbox-tab-inspector",
+    expectedOrder: ["webconsole", "inspector", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage"],
+  },
+  {
+    description: "DragAndDrop the target component over the starting of the tab",
+    dragTarget: "toolbox-tab-netmonitor",
+    passedTargets: ["toolbox-tab-memory", "toolbox-tab-performance",
+                    "toolbox-tab-styleeditor", "toolbox-tab-jsdebugger",
+                    "toolbox-tab-webconsole", "toolbox-tab-inspector"],
+    dropTarget: "toolbox-buttons-start",
+    expectedOrder: ["netmonitor", "inspector", "webconsole", "jsdebugger",
+                    "styleeditor", "performance", "memory", "storage"],
+  },
+  {
+    description: "DragAndDrop the target component over the ending of the tab",
+    dragTarget: "toolbox-tab-webconsole",
+    passedTargets: ["toolbox-tab-jsdebugger", "toolbox-tab-styleeditor",
+                    "toolbox-tab-performance", "toolbox-tab-memory",
+                    "toolbox-tab-netmonitor", "toolbox-tab-storage"],
+    dropTarget: "toolbox-buttons-end",
+    expectedOrder: ["inspector", "jsdebugger", "styleeditor", "performance",
+                    "memory", "netmonitor", "storage", "webconsole", ],
+  },
+  {
+    description: "Mouse was out from the document while dragging",
+    dragTarget: "toolbox-tab-webconsole",
+    passedTargets: ["toolbox-tab-inspector"],
+    dropTarget: null,
+    expectedOrder: ["webconsole", "inspector", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage"],
+  },
+];
+
+add_task(async function() {
+  const originalPreference = Services.prefs.getCharPref("devtools.toolbox.tabsOrder");
+  const tab = await addTab("about:blank");
+  const toolbox = await openToolboxForTab(tab, "inspector", Toolbox.HostType.BOTTOM);
+
+  for (const testData of TEST_DATA) {
+    info(`Test for '${ testData.description }'`);
+    prepareTest(toolbox);
+    await dnd(toolbox, testData.dragTarget, testData.dropTarget, testData.passedTargets);
+    assertTabsOrder(toolbox, testData.expectedOrder);
+    assertSelectedTab(toolbox, testData.dragTarget);
+    assertPreferenceOrder(testData.expectedOrder);
+  }
+
+  info("Test with overflowing tabs");
+  prepareTest(toolbox);
+  const { originalWidth, originalHeight } = await resizeWindow(toolbox, 800);
+  await toolbox.selectTool("storage");
+  const dragTarget = "toolbox-tab-storage";
+  const dropTarget = "toolbox-tab-inspector";
+  const expectedOrder = ["storage", "inspector", "webconsole", "jsdebugger",
+                         "styleeditor", "performance", "memory", "netmonitor"];
+  await dnd(toolbox, dragTarget, dropTarget);
+  assertSelectedTab(toolbox, dragTarget);
+  assertPreferenceOrder(expectedOrder);
+
+  await resizeWindow(toolbox, originalWidth, originalHeight);
+  Services.prefs.setCharPref("devtools.toolbox.tabsOrder", originalPreference);
+});
+
+function prepareTest(toolbox) {
+  Services.prefs.setCharPref("devtools.toolbox.tabsOrder", TEST_STARTING_ORDER.join(","));
+  ok(!toolbox.doc.getElementById("tools-chevron-menu-button"),
+     "The size of the screen being too small");
+
+  const ids = [...toolbox.doc.querySelectorAll(".devtools-tab")]
+                .map(tabElement => tabElement.dataset.id);
+  is(ids.join(","), TEST_STARTING_ORDER.join(","),
+     "The order on the toolbar should be correct");
+}
+
+function assertTabsOrder(toolbox, expectedOrder) {
+  info("Check the order of the tabs on the toolbar");
+  const currentIds = [...toolbox.doc.querySelectorAll(".devtools-tab")]
+                       .map(tabElement => tabElement.dataset.id);
+  is(currentIds.join(","), expectedOrder.join(","),
+     "The order on the toolbar should be correct");
+}
+
+function assertSelectedTab(toolbox, dragTarget) {
+  info("Check whether the drag target was selected");
+  const dragTargetEl = toolbox.doc.getElementById(dragTarget);
+  ok(dragTargetEl.classList.contains("selected"), "The dragged tool should be selected");
+}
+
+function assertPreferenceOrder(expectedOrder) {
+  info("Check the order in DevTools preference for tabs order");
+  is(Services.prefs.getCharPref("devtools.toolbox.tabsOrder"), expectedOrder.join(","),
+     "The preference should be correct");
+}
+
+async function dnd(toolbox, dragTarget, dropTarget, passedTargets = []) {
+  info(`Drag ${ dragTarget } to ${ dropTarget }`);
+  const dragTargetEl = toolbox.doc.getElementById(dragTarget);
+
+  const onReady = dragTargetEl.classList.contains("selected")
+                    ? Promise.resolve() : toolbox.once("select");
+  EventUtils.synthesizeMouseAtCenter(dragTargetEl,
+                                     { type: "mousedown" },
+                                     dragTargetEl.ownerGlobal);
+  await onReady;
+
+  for (const passedTarget of passedTargets) {
+    info(`Via ${ passedTarget }`);
+    const passedTargetEl = toolbox.doc.getElementById(passedTarget);
+    EventUtils.synthesizeMouseAtCenter(passedTargetEl,
+                                       { type: "mousemove" },
+                                       passedTargetEl.ownerGlobal);
+  }
+
+  if (dropTarget) {
+    const dropTargetEl = toolbox.doc.getElementById(dropTarget);
+    EventUtils.synthesizeMouseAtCenter(dropTargetEl,
+                                       { type: "mousemove" },
+                                       dropTargetEl.ownerGlobal);
+    EventUtils.synthesizeMouseAtCenter(dropTargetEl,
+                                       { type: "mouseup" },
+                                       dropTargetEl.ownerGlobal);
+  } else {
+    const containerEl = toolbox.doc.getElementById("toolbox-container");
+    EventUtils.synthesizeMouse(containerEl, 0, 0,
+                               { type: "mouseout" }, containerEl.ownerGlobal);
+  }
+}
+
+async function resizeWindow(toolbox, width, height) {
+  const hostWindow = toolbox.win.parent;
+  const originalWidth = hostWindow.outerWidth;
+  const originalHeight = hostWindow.outerHeight;
+  const toWidth = width || originalWidth;
+  const toHeight = height || originalHeight;
+
+  const onResize = once(hostWindow, "resize");
+  hostWindow.resizeTo(toWidth, toHeight);
+  await onResize;
+
+  return { originalWidth, originalHeight };
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/toolbox-tabs-order-manager.js
@@ -0,0 +1,163 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Services = require("Services");
+const PREFERENCE_NAME = "devtools.toolbox.tabsOrder";
+
+/**
+ * Manage the order of devtools tabs.
+ */
+class ToolboxTabsOrderManager {
+  constructor(onOrderUpdated) {
+    this.onOrderUpdated = onOrderUpdated;
+    this.overflowedTabIds = [];
+
+    this.onMouseDown = this.onMouseDown.bind(this);
+    this.onMouseMove = this.onMouseMove.bind(this);
+    this.onMouseOut = this.onMouseOut.bind(this);
+    this.onMouseUp = this.onMouseUp.bind(this);
+
+    Services.prefs.addObserver(PREFERENCE_NAME, this.onOrderUpdated);
+  }
+
+  destroy() {
+    Services.prefs.removeObserver(PREFERENCE_NAME, this.onOrderUpdated);
+    this.onMouseUp();
+  }
+
+  setOverflowedTabs(overflowedTabIds) {
+    this.overflowedTabIds = overflowedTabIds;
+  }
+
+  onMouseDown(e) {
+    if (!e.target.classList.contains("devtools-tab")) {
+      return;
+    }
+
+    this.dragStartX = e.pageX;
+    this.dragTarget = e.target;
+    this.previousPageX = e.pageX;
+    this.toolboxContainerElement = this.dragTarget.closest("#toolbox-container");
+    this.toolboxTabsElement = this.dragTarget.closest(".toolbox-tabs");
+    this.isOrderUpdated = false;
+
+    this.dragTarget.ownerDocument.addEventListener("mousemove", this.onMouseMove);
+    this.dragTarget.ownerDocument.addEventListener("mouseout", this.onMouseOut);
+    this.dragTarget.ownerDocument.addEventListener("mouseup", this.onMouseUp);
+
+    this.toolboxContainerElement.classList.add("tabs-reordering");
+  }
+
+  onMouseMove(e) {
+    const tabsElement = this.toolboxTabsElement;
+    const diffPageX = e.pageX - this.previousPageX;
+    const dragTargetCenterX =
+      this.dragTarget.offsetLeft + diffPageX + this.dragTarget.clientWidth / 2;
+    let isDragTargetPreviousSibling = false;
+
+    for (const tabElement of tabsElement.querySelectorAll(".devtools-tab")) {
+      if (tabElement === this.dragTarget) {
+        isDragTargetPreviousSibling = true;
+        continue;
+      }
+
+      const anotherElementCenterX =
+        tabElement.offsetLeft + tabElement.clientWidth / 2;
+
+      if (Math.abs(dragTargetCenterX - anotherElementCenterX) <
+          tabElement.clientWidth / 3) {
+        const xBefore = this.dragTarget.offsetLeft;
+
+        if (isDragTargetPreviousSibling) {
+          tabsElement.insertBefore(this.dragTarget, tabElement.nextSibling);
+        } else {
+          tabsElement.insertBefore(this.dragTarget, tabElement);
+        }
+
+        const xAfter = this.dragTarget.offsetLeft;
+        this.dragStartX += xAfter - xBefore;
+
+        this.isOrderUpdated = true;
+        break;
+      }
+    }
+
+    let distance = e.pageX - this.dragStartX;
+
+    if ((!this.dragTarget.previousSibling && distance < 0) ||
+        ((!this.dragTarget.nextSibling ||
+          this.dragTarget.nextSibling.id === "tools-chevron-menu-button") &&
+          distance > 0)) {
+      // If the drag target is already edge of the tabs and the mouse will make the
+      // element to move to same direction more, keep the position.
+      distance = 0;
+    }
+
+    this.dragTarget.style.left = `${ distance }px`;
+    this.previousPageX = e.pageX;
+  }
+
+  onMouseOut(e) {
+    if (e.pageX <= 0 || this.dragTarget.ownerDocument.width <= e.pageX ||
+        e.pageY <= 0 || this.dragTarget.ownerDocument.height <= e.pageY) {
+      this.onMouseUp();
+    }
+  }
+
+  onMouseUp() {
+    if (!this.dragTarget) {
+      // The case in here has two type:
+      // 1. Although destroy method was called, it was not during reordering.
+      // 2. Although mouse event occur, destroy method was called during reordering.
+      return;
+    }
+
+    if (this.isOrderUpdated) {
+      const ids =
+        [...this.toolboxTabsElement.querySelectorAll(".devtools-tab")]
+          .map(tabElement => tabElement.dataset.id)
+          .concat(this.overflowedTabIds);
+      const pref = ids.join(",");
+      Services.prefs.setCharPref(PREFERENCE_NAME, pref);
+    }
+
+    this.dragTarget.ownerDocument.removeEventListener("mousemove", this.onMouseMove);
+    this.dragTarget.ownerDocument.removeEventListener("mouseout", this.onMouseOut);
+    this.dragTarget.ownerDocument.removeEventListener("mouseup", this.onMouseUp);
+
+    this.toolboxContainerElement.classList.remove("tabs-reordering");
+    this.dragTarget.style.left = null;
+    this.dragTarget = null;
+    this.toolboxContainerElement = null;
+    this.toolboxTabsElement = null;
+  }
+}
+
+function sortPanelDefinitions(definitions) {
+  const pref = Services.prefs.getCharPref(PREFERENCE_NAME, "");
+
+  if (!pref) {
+    definitions.sort(definition => {
+      return -1 * (definition.ordinal == undefined || definition.ordinal < 0
+        ? Number.MAX_VALUE
+        : definition.ordinal
+      );
+    });
+  }
+
+  const toolIds = pref.split(",");
+
+  return definitions.sort((a, b) => {
+    let orderA = toolIds.indexOf(a.id);
+    let orderB = toolIds.indexOf(b.id);
+    orderA = orderA < 0 ? Number.MAX_VALUE : orderA;
+    orderB = orderB < 0 ? Number.MAX_VALUE : orderB;
+    return orderA - orderB;
+  });
+}
+
+module.exports.ToolboxTabsOrderManager = ToolboxTabsOrderManager;
+module.exports.sortPanelDefinitions = sortPanelDefinitions;
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -71,16 +71,18 @@ loader.lazyRequireGetter(this, "viewSour
 loader.lazyRequireGetter(this, "StyleSheetsFront",
   "devtools/shared/fronts/stylesheets", true);
 loader.lazyRequireGetter(this, "buildHarLog",
   "devtools/client/netmonitor/src/har/har-builder-utils", true);
 loader.lazyRequireGetter(this, "getKnownDeviceFront",
   "devtools/shared/fronts/device", true);
 loader.lazyRequireGetter(this, "NetMonitorAPI",
   "devtools/client/netmonitor/src/api", true);
+loader.lazyRequireGetter(this, "sortPanelDefinitions",
+  "devtools/client/framework/toolbox-tabs-order-manager", true);
 
 loader.lazyGetter(this, "domNodeConstants", () => {
   return require("devtools/shared/dom-node-constants");
 });
 
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/client/netmonitor/src/har/toolbox-overlay").register;
 });
@@ -142,16 +144,17 @@ function Toolbox(target, selectedTool, h
   this._applyServiceWorkersTestingSettings =
     this._applyServiceWorkersTestingSettings.bind(this);
   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
   this._onFocus = this._onFocus.bind(this);
   this._onBrowserMessage = this._onBrowserMessage.bind(this);
   this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
   this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this);
   this._onPerformanceFrontEvent = this._onPerformanceFrontEvent.bind(this);
+  this._onTabsOrderUpdated = this._onTabsOrderUpdated.bind(this);
   this._onToolbarFocus = this._onToolbarFocus.bind(this);
   this._onToolbarArrowKeypress = this._onToolbarArrowKeypress.bind(this);
   this._onPickerClick = this._onPickerClick.bind(this);
   this._onPickerKeypress = this._onPickerKeypress.bind(this);
   this._onPickerStarted = this._onPickerStarted.bind(this);
   this._onPickerStopped = this._onPickerStopped.bind(this);
   this._onInspectObject = this._onInspectObject.bind(this);
   this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this);
@@ -258,23 +261,18 @@ Toolbox.prototype = {
     }
   },
 
   /**
    * Combines the built-in panel definitions and the additional tool definitions that
    * can be set by add-ons.
    */
   _combineAndSortPanelDefinitions() {
-    const definitions = [...this._panelDefinitions, ...this.getVisibleAdditionalTools()];
-    definitions.sort(definition => {
-      return -1 * (definition.ordinal == undefined || definition.ordinal < 0
-        ? MAX_ORDINAL
-        : definition.ordinal
-      );
-    });
+    let definitions = [...this._panelDefinitions, ...this.getVisibleAdditionalTools()];
+    definitions = sortPanelDefinitions(definitions);
     this.component.setPanelDefinitions(definitions);
   },
 
   lastUsedToolId: null,
 
   /**
    * Returns a *copy* of the _toolPanels collection.
    *
@@ -1077,16 +1075,17 @@ Toolbox.prototype = {
 
   /**
    * Build the options for changing hosts. Called every time
    * the host changes.
    */
   _buildDockOptions: function() {
     if (!this._target.isLocalTab) {
       this.component.setDockOptionsEnabled(false);
+      this.component.setCanCloseToolbox(false);
       return;
     }
 
     this.component.setDockOptionsEnabled(true);
     this.component.setCanCloseToolbox(this.hostType !== Toolbox.HostType.WINDOW);
 
     let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
 
@@ -1144,17 +1143,18 @@ Toolbox.prototype = {
     const element = this.React.createElement(this.ToolboxController, {
       L10N,
       currentToolId: this.currentToolId,
       selectTool: this.selectTool,
       toggleSplitConsole: this.toggleSplitConsole,
       toggleNoAutohide: this.toggleNoAutohide,
       closeToolbox: this.destroy,
       focusButton: this._onToolbarFocus,
-      toolbox: this
+      toolbox: this,
+      onTabsOrderUpdated: this._onTabsOrderUpdated,
     });
 
     this.component = this.ReactDOM.render(element, this._componentMount);
   },
 
   /**
    * Reset tabindex attributes across all focusable elements inside the toolbar.
    * Only have one element with tabindex=0 at a time to make sure that tabbing
@@ -1910,16 +1910,20 @@ Toolbox.prototype = {
     if (originalTarget.nodeType !== 1 ||
         originalTarget.baseURI === webconsoleURL) {
       return;
     }
 
     this._lastFocusedElement = originalTarget;
   },
 
+  _onTabsOrderUpdated: function() {
+    this._combineAndSortPanelDefinitions();
+  },
+
   /**
    * Opens the split console.
    *
    * @returns {Promise} a promise that resolves once the tool has been
    *          loaded and focused.
    */
   openSplitConsole: function() {
     this._splitConsole = true;
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -71,17 +71,16 @@ devtools.jar:
     content/performance/views/details-abstract-subview.js (performance/views/details-abstract-subview.js)
     content/performance/views/details-waterfall.js (performance/views/details-waterfall.js)
     content/performance/views/details-js-call-tree.js (performance/views/details-js-call-tree.js)
     content/performance/views/details-js-flamegraph.js (performance/views/details-js-flamegraph.js)
     content/performance/views/details-memory-call-tree.js (performance/views/details-memory-call-tree.js)
     content/performance/views/details-memory-flamegraph.js (performance/views/details-memory-flamegraph.js)
     content/performance/views/recordings.js (performance/views/recordings.js)
     content/memory/memory.xhtml (memory/memory.xhtml)
-    content/memory/initializer.js (memory/initializer.js)
     content/commandline/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml)
     content/commandline/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
     content/framework/toolbox-window.xul (framework/toolbox-window.xul)
     content/framework/toolbox-options.xhtml (framework/toolbox-options.xhtml)
 *   content/framework/toolbox.xul (framework/toolbox.xul)
     content/framework/toolbox-init.js (framework/toolbox-init.js)
     content/framework/options-panel.css (framework/options-panel.css)
     content/framework/toolbox-process-window.xul (framework/toolbox-process-window.xul)
--- a/devtools/client/memory/initializer.js
+++ b/devtools/client/memory/initializer.js
@@ -1,70 +1,69 @@
 /* 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/. */
 
 /* exported initialize, destroy, Promise */
 
 "use strict";
 
-const BrowserLoaderModule = {};
-ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
-const { require } = BrowserLoaderModule.BrowserLoader({
-  baseURI: "resource://devtools/client/memory/",
-  window
-});
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 const App = createFactory(require("devtools/client/memory/app"));
 const Store = require("devtools/client/memory/store");
 const { assert } = require("devtools/shared/DevToolsUtils");
-const Promise = require("Promise");
+
+// Shared variables used by several methods of this module.
+let root, store, unsubscribe;
 
-/**
- * The current target, toolbox, MemoryFront, and HeapAnalysesClient,
- * set by this tool's host.
- */
-var gToolbox, gFront, gHeapAnalysesClient;
+const initialize = async function() {
+  // Exposed by panel.js
+  let { gFront, gToolbox, gHeapAnalysesClient } = window;
 
-/**
- * Variables set by `initialize()`
- */
-var gStore, gRoot, gApp, gProvider, unsubscribe, isHighlighted;
+  root = document.querySelector("#app");
+  store = Store();
+  const app = createElement(App, {
+    toolbox: gToolbox,
+    front: gFront,
+    heapWorker: gHeapAnalysesClient
+  });
+  const provider = createElement(Provider, { store }, app);
+  ReactDOM.render(provider, root);
+  unsubscribe = store.subscribe(onStateChange);
 
-var initialize = async function() {
-  gRoot = document.querySelector("#app");
-  gStore = Store();
-  gApp = createElement(App,
-    { toolbox: gToolbox, front: gFront, heapWorker: gHeapAnalysesClient });
-  gProvider = createElement(Provider, { store: gStore }, gApp);
-  ReactDOM.render(gProvider, gRoot);
-  unsubscribe = gStore.subscribe(onStateChange);
+  // Exposed for tests.
+  window.gStore = store;
 };
 
-var destroy = async function() {
-  const ok = ReactDOM.unmountComponentAtNode(gRoot);
+const destroy = async function() {
+  const ok = ReactDOM.unmountComponentAtNode(root);
   assert(ok, "Should successfully unmount the memory tool's top level React component");
 
   unsubscribe();
+};
 
-  gStore = gRoot = gApp = gProvider = unsubscribe = isHighlighted = null;
-};
+// Current state
+let isHighlighted;
 
 /**
  * Fired on any state change, currently only handles toggling
  * the highlighting of the tool when recording allocations.
  */
 function onStateChange() {
-  let isRecording = gStore.getState().allocations.recording;
+  let { gToolbox } = window;
+
+  let isRecording = store.getState().allocations.recording;
   if (isRecording === isHighlighted) {
     return;
   }
 
   if (isRecording) {
     gToolbox.highlightTool("memory");
   } else {
     gToolbox.unhighlightTool("memory");
   }
 
   isHighlighted = isRecording;
 }
+
+module.exports = { initialize, destroy };
--- a/devtools/client/memory/memory.xhtml
+++ b/devtools/client/memory/memory.xhtml
@@ -20,21 +20,16 @@
     <div id="app"></div>
 
     <script type="application/javascript"
             src="chrome://devtools/content/shared/theme-switching.js"
             defer="true">
     </script>
 
     <script type="application/javascript"
-            src="initializer.js"
-            defer="true">
-    </script>
-
-    <script type="application/javascript"
             src="chrome://devtools/content/shared/vendor/d3.js"
             defer="true">
     </script>
 
     <script type="application/javascript"
             src="chrome://devtools/content/shared/vendor/dagre-d3.js"
             defer="true">
     </script>
--- a/devtools/client/memory/moz.build
+++ b/devtools/client/memory/moz.build
@@ -11,16 +11,17 @@ DIRS += [
     'components',
     'reducers',
 ]
 
 DevToolsModules(
     'app.js',
     'constants.js',
     'dominator-tree-lazy-children.js',
+    'initializer.js',
     'models.js',
     'panel.js',
     'reducers.js',
     'store.js',
     'utils.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
--- a/devtools/client/memory/panel.js
+++ b/devtools/client/memory/panel.js
@@ -1,22 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const { MemoryFront } = require("devtools/shared/fronts/memory");
+const { Cu } = require("chrome");
 const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");
 
 function MemoryPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
 
+  const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+  const browserRequire = BrowserLoader({
+    baseURI: "resource://devtools/client/memory/",
+    window: this.panelWin
+  }).require;
+  this.initializer = browserRequire("devtools/client/memory/initializer");
+
   EventEmitter.decorate(this);
 }
 
 MemoryPanel.prototype = {
   async open() {
     if (this._opening) {
       return this._opening;
     }
@@ -27,17 +35,17 @@ MemoryPanel.prototype = {
     const rootForm = await this.target.root;
     this.panelWin.gFront = new MemoryFront(this.target.client,
                                            this.target.form,
                                            rootForm);
     this.panelWin.gHeapAnalysesClient = new HeapAnalysesClient();
 
     await this.panelWin.gFront.attach();
 
-    this._opening = this.panelWin.initialize().then(() => {
+    this._opening = this.initializer.initialize().then(() => {
       this.isReady = true;
       this.emit("ready");
       return this;
     });
 
     return this._opening;
   },
 
@@ -50,17 +58,17 @@ MemoryPanel.prototype = {
   async destroy() {
     // Make sure this panel is not already destroyed.
     if (this._destroyer) {
       return this._destroyer;
     }
 
     await this.panelWin.gFront.detach();
 
-    this._destroyer = this.panelWin.destroy().then(() => {
+    this._destroyer = this.initializer.destroy().then(() => {
       // Destroy front to ensure packet handler is removed from client
       this.panelWin.gFront.destroy();
       this.panelWin.gHeapAnalysesClient.destroy();
       this.panelWin = null;
       this._opening = null;
       this.isReady = false;
       this.emit("destroyed");
     });
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -24,16 +24,17 @@ pref("devtools.toolbox.footer.height", 2
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
 pref("devtools.toolbox.previousHost", "side");
 pref("devtools.toolbox.selectedTool", "webconsole");
 pref("devtools.toolbox.sideEnabled", true);
 pref("devtools.toolbox.zoomValue", "1");
 pref("devtools.toolbox.splitconsoleEnabled", false);
 pref("devtools.toolbox.splitconsoleHeight", 100);
+pref("devtools.toolbox.tabsOrder", "");
 
 // Toolbox Button preferences
 pref("devtools.command-button-pick.enabled", true);
 pref("devtools.command-button-frames.enabled", true);
 pref("devtools.command-button-splitconsole.enabled", true);
 pref("devtools.command-button-paintflashing.enabled", false);
 pref("devtools.command-button-scratchpad.enabled", false);
 pref("devtools.command-button-responsive.enabled", true);
--- a/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js
+++ b/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js
@@ -3,16 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Test to verify that indexedDBs with duplicate names (different types / paths)
 // work as expected.
 
 "use strict";
 
 add_task(async function() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["dom.indexedDB.storageOption.enabled", true]]
+  });
+
   const TESTPAGE = MAIN_DOMAIN + "storage-indexeddb-duplicate-names.html";
 
   setPermission(TESTPAGE, "indexedDB");
 
   await openTabAndSetupStorage(TESTPAGE);
 
   await checkState([
     [
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -291,8 +291,24 @@
 
 /**
  * Enrure that selected toolbox panel's contents are keyboard accessible as they
  * are explicitly made not to be when hidden (default).
  */
 .toolbox-panel[selected] * {
   -moz-user-focus: normal;
 }
+
+/* Toolbox tabs reordering */
+#toolbox-container.tabs-reordering > .theme-body {
+  pointer-events: none;
+}
+
+#toolbox-container.tabs-reordering .devtools-tab:not(.selected):hover .devtools-tab-line {
+  background: transparent;
+  opacity: 0;
+  transition: none;
+}
+
+#toolbox-container.tabs-reordering .devtools-tab.selected {
+  background-color: var(--theme-toolbar-hover);
+  z-index: 1;
+}
--- a/dom/base/DOMPrefsInternal.h
+++ b/dom/base/DOMPrefsInternal.h
@@ -36,16 +36,17 @@ DOM_PREF(StreamsEnabled, "dom.streams.en
 DOM_PREF(OffscreenCanvasEnabled, "gfx.offscreencanvas.enabled")
 DOM_PREF(WebkitBlinkDirectoryPickerEnabled, "dom.webkitBlink.dirPicker.enabled")
 DOM_PREF(NetworkInformationEnabled, "dom.netinfo.enabled")
 DOM_PREF(FetchObserverEnabled, "dom.fetchObserver.enabled")
 DOM_PREF(ResistFingerprintingEnabled, "privacy.resistFingerprinting")
 DOM_PREF(EnableAutoDeclineCanvasPrompts, "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts")
 DOM_PREF(DevToolsEnabled, "devtools.enabled")
 DOM_PREF(PerformanceObserverEnabled, "dom.enable_performance_observer")
+DOM_PREF(IndexedDBStorageOptionsEnabled, "dom.indexedDB.storageOption.enabled")
 
 DOM_WEBIDL_PREF(ImageBitmapExtensionsEnabled)
 DOM_WEBIDL_PREF(DOMCachesEnabled)
 DOM_WEBIDL_PREF(NotificationEnabledInServiceWorkers)
 DOM_WEBIDL_PREF(NotificationRIEnabled)
 DOM_WEBIDL_PREF(ServiceWorkersEnabled)
 DOM_WEBIDL_PREF(StorageManagerEnabled)
 DOM_WEBIDL_PREF(PromiseRejectionEventsEnabled)
--- a/dom/html/test/test_bug1166138.html
+++ b/dom/html/test/test_bug1166138.html
@@ -11,20 +11,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1166138">Mozilla Bug 1166138</a>
   <p id="display"></p>
   <div id="content" style="display: none">
   </div>
 
-  <img srcset="file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x"
-       src="file_bug1166138_def.png"
-       onload="onLoad()">
-
   <script type="application/javascript">
     var img1x = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_1x.png";
     var img2x = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_2x.png";
     var imgdef = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_def.png";
     var onLoadCallback = null;
     var done = false;
 
     var startPromise = new Promise((a) => {
@@ -121,10 +117,15 @@ https://bugzilla.mozilla.org/show_bug.cg
       await spin(true);
       is(newImage.currentSrc, img1x, "new image after switching to 1x");
       is(image.currentSrc, img1x, "old image after switching to 1x");
 
       // Clear the listener
       done = true;
     });
   </script>
+
+  <img srcset="file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x"
+       src="file_bug1166138_def.png"
+       onload="onLoad()">
+
 </body>
 </html>
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -696,21 +696,37 @@ IDBFactory::OpenInternal(JSContext* aCx,
   PersistenceType persistenceType;
 
   bool isInternal = principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo;
   if (!isInternal && principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
     nsCString origin = principalInfo.get_ContentPrincipalInfo().originNoSuffix();
     isInternal = QuotaManager::IsOriginInternal(origin);
   }
 
+  // Allow storage attributes for add-ons independent of the pref.
+  // This works in the main thread only, workers don't have the principal.
+  bool isAddon = false;
+  if (NS_IsMainThread()) {
+    // aPrincipal is passed inconsistently, so even when we are already on
+    // the main thread, we may have been passed a null aPrincipal.
+    nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(principalInfo);
+    if (principal) {
+      nsAutoString addonId;
+      Unused << NS_WARN_IF(NS_FAILED(principal->GetAddonId(addonId)));
+      isAddon = !addonId.IsEmpty();
+    }
+  }
+
   if (isInternal) {
     // Chrome privilege and internal origins always get persistent storage.
     persistenceType = PERSISTENCE_TYPE_PERSISTENT;
+  } else if (isAddon || DOMPrefs::IndexedDBStorageOptionsEnabled()) {
+    persistenceType = PersistenceTypeFromStorage(aStorageType);
   } else {
-    persistenceType = PersistenceTypeFromStorage(aStorageType);
+    persistenceType = PERSISTENCE_TYPE_DEFAULT;
   }
 
   DatabaseMetadata& metadata = commonParams.metadata();
   metadata.name() = aName;
   metadata.persistenceType() = persistenceType;
 
   FactoryRequestParams params;
   if (aDeleting) {
--- a/dom/indexedDB/test/browser.ini
+++ b/dom/indexedDB/test/browser.ini
@@ -1,9 +1,11 @@
 [DEFAULT]
+prefs =
+  dom.indexedDB.storageOption.enabled=true
 skip-if = (buildapp != "browser")
 support-files =
   head.js
   browser_forgetThisSiteAdd.html
   browser_forgetThisSiteGet.html
   browserHelpers.js
   browser_permissionsPrompt.html
   browser_permissionsSharedWorker.html
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -1,13 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [DEFAULT]
+prefs =
+  dom.indexedDB.storageOption.enabled=true
 support-files =
   bfcache_page1.html
   bfcache_page2.html
   blob_worker_crash_iframe.html
   !/dom/events/test/event_leak_utils.js
   error_events_abort_transactions_iframe.html
   event_propagation_iframe.html
   exceptions_in_events_iframe.html
--- a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
+++ b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
@@ -2,16 +2,21 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
 function* testSteps()
 {
+  Services.prefs.setBoolPref("dom.indexedDB.storageOption.enabled", true);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("dom.indexedDB.storageOption.enabled");
+  });
+
   const openParams = [
     // This one lives in storage/default/http+++localhost
     { url: "http://localhost", dbName: "dbA", dbVersion: 1 },
 
     // This one lives in storage/default/http+++www.mozilla.org
     { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
 
     // This one lives in storage/default/http+++www.mozilla.org+8080
--- a/dom/indexedDB/test/unit/test_metadata2Restore.js
+++ b/dom/indexedDB/test/unit/test_metadata2Restore.js
@@ -2,16 +2,21 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
 function* testSteps()
 {
+  Services.prefs.setBoolPref("dom.indexedDB.storageOption.enabled", true);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("dom.indexedDB.storageOption.enabled");
+  });
+
   const openParams = [
     // This one lives in storage/permanent/chrome
     // The .metadata-v2 file was intentionally removed for this origin directory
     // to test restoring.
     { dbName: "dbA",
       dbOptions: { version: 1, storage: "persistent" } },
 
     // This one lives in storage/temporary/http+++localhost
--- a/dom/indexedDB/test/unit/test_metadataRestore.js
+++ b/dom/indexedDB/test/unit/test_metadataRestore.js
@@ -2,16 +2,21 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
 function* testSteps()
 {
+  Services.prefs.setBoolPref("dom.indexedDB.storageOption.enabled", true);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("dom.indexedDB.storageOption.enabled");
+  });
+
   const openParams = [
     // This one lives in storage/permanent/chrome
     { dbName: "dbA",
       dbOptions: { version: 1, storage: "persistent" } },
 
     // This one lives in storage/temporary/http+++localhost
     { url: "http://localhost", dbName: "dbB",
       dbOptions: { version: 1, storage: "temporary" } },
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_storageOption_pref.js
@@ -0,0 +1,101 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/*
+ * Tests that the dom.indexedDB.storageOption.enabled pref is able to
+ * control whether the storage option is accepted by indexedDB.open().
+ */
+
+var testGenerator = testSteps();
+
+function* testSteps()
+{
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("dom.indexedDB.storageOption.enabled");
+  });
+
+  const name = "Splendid Test";
+  const version = 1;
+  const origin = "https://example.com";
+
+  // Avoid trying to show permission prompts on a window.
+  let uri = Services.io.newURI(origin);
+  Services.perms.add(uri, "indexedDB", Ci.nsIPermissionManager.ALLOW_ACTION);
+
+  const objectStoreName = "Foo";
+  const data = { key: 1, value: "bar" };
+
+  // Turn the storage option off, content databases should be "default".
+  Services.prefs.setBoolPref("dom.indexedDB.storageOption.enabled", false);
+
+  // Open a database with content privileges.
+  let principal = getPrincipal(origin);
+  let request = indexedDB.openForPrincipal(principal, name,
+    { version, storage: "persistent" });
+
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Got correct event type");
+
+  let db = event.target.result;
+  db.onerror = errorHandler;
+
+  let objectStore = db.createObjectStore(objectStoreName, { });
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  is(db.name, name, "Correct name");
+  is(db.version, version, "Correct version");
+  is(db.storage, "default", "Correct persistence type");
+
+  objectStore = db.transaction([objectStoreName], "readwrite")
+                  .objectStore(objectStoreName);
+
+  request = objectStore.get(data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, undefined, "Got no data");
+
+  request = objectStore.add(data.value, data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, data.key, "Got correct key");
+
+  // Turn the storage option on, content databases should be able to get
+  // "persistent" now. Because persistent storage is separate from default
+  // storage, we will not find the database we just created and will
+  // receive an upgradeneeded event.
+  Services.prefs.setBoolPref("dom.indexedDB.storageOption.enabled", true);
+
+  request = indexedDB.openForPrincipal(principal, name,
+    { version, storage: "persistent" });
+
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  db = event.target.result;
+  db.onerror = errorHandler;
+
+  is(event.type, "upgradeneeded", "Got correct event type");
+
+  is(db.name, name, "Correct name");
+  is(db.version, version, "Correct version");
+  is(db.storage, "persistent", "Correct persistence type");
+
+  // A new database was created (because it changed persistence type).
+  is(db.objectStoreNames.length, 0, "Got no data");
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -55,16 +55,18 @@ skip-if = true
 [test_mutableFileUpgrade.js]
 [test_oldDirectories.js]
 [test_quotaExceeded_recovery.js]
 [test_readwriteflush_disabled.js]
 [test_schema18upgrade.js]
 [test_schema21upgrade.js]
 [test_schema23upgrade.js]
 [test_snappyUpgrade.js]
+[test_storageOption_pref.js]
+skip-if = os == "android"
 [test_storagePersistentUpgrade.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"
 [test_view_put_get_values.js]
 [test_wasm_cursors.js]
 [test_wasm_getAll.js]
 skip-if = coverage # bug 1336727
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -502,17 +502,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.use-iosurface-textures",            UseIOSurfaceTextures, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-mutex-on-present",              UseMutexOnPresent, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-surfacetexture-textures",       UseSurfaceTextureTextures, bool, false);
 
   DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms",   CollectScrollTransforms, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
 
   DECL_GFX_PREF(Once, "gfx.webrender.all",                     WebRenderAll, bool, false);
-  DECL_GFX_PREF(Once, "gfx.webrender.async-scene-build",       WebRenderAsyncSceneBuild, bool, false);
+  DECL_OVERRIDE_PREF(Once, "gfx.webrender.async-scene-build",  WebRenderAsyncSceneBuild, gfxPrefs::WebRenderAll());
   DECL_GFX_PREF(Once, "gfx.webrender.enabled",                 WebRenderEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.blob-images",             WebRenderBlobImages, bool, true);
   DECL_GFX_PREF(Live, "gfx.webrender.blob.invalidation",       WebRenderBlobInvalidation, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.highlight-painted-layers",WebRenderHighlightPaintedLayers, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.hit-test",                WebRenderHitTest, bool, true);
 
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
--- a/ipc/chromium/src/base/shared_memory.h
+++ b/ipc/chromium/src/base/shared_memory.h
@@ -108,16 +108,24 @@ class SharedMemory {
   //   return ok;
   // Note that the memory is unmapped by calling this method, regardless of the
   // return value.
   bool GiveToProcess(ProcessId target_pid,
                      SharedMemoryHandle* new_handle) {
     return ShareToProcessCommon(target_pid, new_handle, true);
   }
 
+#ifdef OS_POSIX
+  // If named POSIX shm is being used, append the prefix (including
+  // the leading '/') that would be used by a process with the given
+  // pid to the given string and return true.  If not, return false.
+  // (This is public so that the Linux sandboxing code can use it.)
+  static bool AppendPosixShmPrefix(std::string* str, pid_t pid);
+#endif
+
  private:
   bool ShareToProcessCommon(ProcessId target_pid,
                             SharedMemoryHandle* new_handle,
                             bool close_self);
 
 #if defined(OS_WIN)
   HANDLE             mapped_file_;
 #elif defined(OS_POSIX)
--- a/ipc/chromium/src/base/shared_memory_posix.cc
+++ b/ipc/chromium/src/base/shared_memory_posix.cc
@@ -7,21 +7,25 @@
 #include "base/shared_memory.h"
 
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "base/file_util.h"
+#ifdef ANDROID
+#include <linux/ashmem.h>
+#endif
+
+#include "base/eintr_wrapper.h"
 #include "base/logging.h"
-#include "base/platform_thread.h"
 #include "base/string_util.h"
-#include "mozilla/UniquePtr.h"
+#include "mozilla/Atomics.h"
+#include "prenv.h"
 
 namespace base {
 
 SharedMemory::SharedMemory()
     : mapped_file_(-1),
       memory_(NULL),
       read_only_(false),
       max_size_(0) {
@@ -44,69 +48,102 @@ bool SharedMemory::IsHandleValid(const S
   return handle.fd >= 0;
 }
 
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() {
   return SharedMemoryHandle();
 }
 
-namespace {
-
-// A class to handle auto-closing of FILE*'s.
-class ScopedFILEClose {
- public:
-  inline void operator()(FILE* x) const {
-    if (x) {
-      fclose(x);
-    }
+// static
+bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid)
+{
+#if defined(ANDROID) || defined(SHM_ANON)
+  return false;
+#else
+  *str += '/';
+#ifdef OS_LINUX
+  // The Snap package environment doesn't provide a private /dev/shm
+  // (it's used for communication with services like PulseAudio);
+  // instead AppArmor is used to restrict access to it.  Anything with
+  // this prefix is allowed:
+  static const char* const kSnap = PR_GetEnv("SNAP_NAME");
+  if (kSnap) {
+    StringAppendF(str, "snap.%s.", kSnap);
   }
-};
-
-typedef mozilla::UniquePtr<FILE, ScopedFILEClose> ScopedFILE;
-
+#endif // OS_LINUX
+  // Hopefully the "implementation defined" name length limit is long
+  // enough for this.
+  StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
+  return true;
+#endif // !ANDROID && !SHM_ANON
 }
 
 bool SharedMemory::Create(size_t size) {
   read_only_ = false;
 
   DCHECK(size > 0);
   DCHECK(mapped_file_ == -1);
 
-  ScopedFILE file_closer;
-  FILE *fp;
-
-  FilePath path;
-  fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
-
-  // Deleting the file prevents anyone else from mapping it in
-  // (making it private), and prevents the need for cleanup (once
-  // the last fd is closed, it is truly freed).
-  file_util::Delete(path);
-
-  if (fp == NULL)
-    return false;
-  file_closer.reset(fp);  // close when we go out of scope
+  int fd;
+  bool needs_truncate = true;
 
-  // Set the file size.
-  //
-  // According to POSIX, (f)truncate can be used to extend files;
-  // previously this required the XSI option but as of the 2008
-  // edition it's required for everything.  (Linux documents that this
-  // may fail on non-"native" filesystems like FAT, but /dev/shm
-  // should always be tmpfs.)
-  if (ftruncate(fileno(fp), size) != 0)
+#ifdef ANDROID
+  // Android has its own shared memory facility:
+  fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
+  if (fd < 0) {
+    CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
+    return false;
+  }
+  if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
+    CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
+    close(fd);
     return false;
-  // This probably isn't needed.
-  if (fseeko(fp, size, SEEK_SET) != 0)
-    return false;
+  }
+  needs_truncate = false;
+#elif defined(SHM_ANON)
+  // FreeBSD (or any other Unix that might decide to implement this
+  // nice, simple API):
+  fd = shm_open(SHM_ANON, O_RDWR, 0600);
+#else
+  // Generic Unix: shm_open + shm_unlink
+  do {
+    // The names don't need to be unique, but it saves time if they
+    // usually are.
+    static mozilla::Atomic<size_t> sNameCounter;
+    std::string name;
+    CHECK(AppendPosixShmPrefix(&name, getpid()));
+    StringAppendF(&name, "%zu", sNameCounter++);
+    // O_EXCL means the names being predictable shouldn't be a problem.
+    fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
+    if (fd >= 0) {
+      if (shm_unlink(name.c_str()) != 0) {
+        // This shouldn't happen, but if it does: assume the file is
+        // in fact leaked, and bail out now while it's still 0-length.
+        DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
+        return false;
+      }
+    }
+  } while (fd < 0 && errno == EEXIST);
+#endif
 
-  mapped_file_ = dup(fileno(fp));
-  DCHECK(mapped_file_ >= 0);
+  if (fd < 0) {
+    CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
+    return false;
+  }
 
+  if (needs_truncate) {
+    if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
+      CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
+      close(fd);
+      return false;
+    }
+  }
+
+  mapped_file_ = fd;
   max_size_ = size;
   return true;
 }
 
 bool SharedMemory::Map(size_t bytes) {
   if (mapped_file_ == -1)
     return false;
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -496,16 +496,17 @@ GetPseudoType(nsAtom* aPseudo)
 
 already_AddRefed<ComputedStyle>
 nsComputedDOMStyle::DoGetComputedStyleNoFlush(Element* aElement,
                                               nsAtom* aPseudo,
                                               nsIPresShell* aPresShell,
                                               StyleType aStyleType)
 {
   MOZ_ASSERT(aElement, "NULL element");
+
   // If the content has a pres shell, we must use it.  Otherwise we'd
   // potentially mix rule trees by using the wrong pres shell's style
   // set.  Using the pres shell from the content also means that any
   // content that's actually *in* a document will get the style from the
   // correct document.
   nsIPresShell* presShell = nsContentUtils::GetPresShellForContent(aElement);
   bool inDocWithShell = true;
   if (!presShell) {
@@ -516,16 +517,24 @@ nsComputedDOMStyle::DoGetComputedStyleNo
     }
   }
 
   CSSPseudoElementType pseudoType = GetPseudoType(aPseudo);
   if (aPseudo && pseudoType >= CSSPseudoElementType::Count) {
     return nullptr;
   }
 
+  if (aElement->IsInNativeAnonymousSubtree() && !aElement->IsInComposedDoc()) {
+    // Normal web content can't access NAC, but Accessibility, DevTools and
+    // Editor use this same API and this may get called for anonymous content.
+    // Computing the style of a pseudo-element that doesn't have a parent doesn't
+    // really make sense.
+    return nullptr;
+  }
+
   // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
   // check is needed due to bug 135040 (to avoid using
   // mPrimaryFrame). Remove it once that's fixed.
   if (inDocWithShell &&
       aStyleType == eAll &&
       !aElement->IsHTMLElement(nsGkAtoms::area)) {
     nsIFrame* frame = nullptr;
     if (aPseudo == nsCSSPseudoElements::before) {
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -144,17 +144,17 @@ VARCACHE_PREF(
 //---------------------------------------------------------------------------
 // Layout prefs
 //---------------------------------------------------------------------------
 
 // Is parallel CSS parsing enabled?
 VARCACHE_PREF(
   "layout.css.parsing.parallel",
    layout_css_parsing_parallel,
-  bool, false
+  bool, true
 )
 
 // Is support for the font-display @font-face descriptor enabled?
 VARCACHE_PREF(
   "layout.css.font-display.enabled",
    layout_css_font_display_enabled,
   bool, true
 )
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -133,16 +133,18 @@ pref("dom.indexedDB.enabled", true);
 // Whether or not indexedDB experimental features are enabled.
 pref("dom.indexedDB.experimental", false);
 // Enable indexedDB logging.
 pref("dom.indexedDB.logging.enabled", true);
 // Detailed output in log messages.
 pref("dom.indexedDB.logging.details", true);
 // Enable profiler marks for indexedDB events.
 pref("dom.indexedDB.logging.profiler-marks", false);
+// Enable passing the "storage" option to indexedDB.open.
+pref("dom.indexedDB.storageOption.enabled", false);
 
 // Whether or not File Handle is enabled.
 pref("dom.fileHandle.enabled", true);
 
 // Whether window.onappinstalled from "W3C Web Manifest" is enabled
 pref("dom.manifest.onappinstalled", false);
 
 // Whether or not selection events are enabled
@@ -849,17 +851,17 @@ pref("gfx.webrender.dcomp-win.enabled", 
 pref("gfx.webrender.program-binary", true);
 #endif
 
 #ifdef XP_MACOSX
 pref("gfx.compositor.glcontext.opaque", false);
 #endif
 
 pref("gfx.webrender.highlight-painted-layers", false);
-pref("gfx.webrender.async-scene-build", false);
+pref("gfx.webrender.async-scene-build", 2);
 pref("gfx.webrender.blob-images", true);
 pref("gfx.webrender.blob.invalidation", true);
 pref("gfx.webrender.hit-test", true);
 
 // WebRender debugging utilities.
 pref("gfx.webrender.debug.texture-cache", false);
 pref("gfx.webrender.debug.render-targets", false);
 pref("gfx.webrender.debug.alpha-primitives", false);
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -517,18 +517,18 @@ UsesExternalProtocolHandler(const char* 
         NS_LITERAL_CSTRING("chrome").Equals(aScheme) ||
         NS_LITERAL_CSTRING("resource").Equals(aScheme)) {
         // Don't allow file:, chrome: or resource: URIs to be handled with
         // nsExternalProtocolHandler, since internally we rely on being able to
         // use and read from these URIs.
         return false;
     }
 
-    for (unsigned int i = 0; i < NS_N(gForcedExternalSchemes); i++) {
-      if (!nsCRT::strcasecmp(gForcedExternalSchemes[i], aScheme)) {
+    for (const auto & forcedExternalScheme : gForcedExternalSchemes) {
+      if (!nsCRT::strcasecmp(forcedExternalScheme, aScheme)) {
         return true;
       }
     }
 
     nsAutoCString pref("network.protocol-handler.external.");
     pref += aScheme;
 
     return Preferences::GetBool(pref.get(), false);
--- a/python/mozboot/mozboot/archlinux.py
+++ b/python/mozboot/mozboot/archlinux.py
@@ -18,16 +18,17 @@ class ArchlinuxBootstrapper(StyloInstall
     '''Archlinux experimental bootstrapper.'''
 
     SYSTEM_PACKAGES = [
         'autoconf2.13',
         'base-devel',
         'nodejs',
         'python2',
         'python2-setuptools',
+        'python',  # This is Python 3 on Arch.
         'unzip',
         'zip',
     ]
 
     BROWSER_PACKAGES = [
         'alsa-lib',
         'dbus-glib',
         'desktop-file-utils',
--- a/python/mozboot/mozboot/debian.py
+++ b/python/mozboot/mozboot/debian.py
@@ -95,17 +95,32 @@ class DebianBootstrapper(StyloInstall, B
         self.packages = self.COMMON_PACKAGES + self.DISTRO_PACKAGES
         if self.distro == 'Debian' or self.distro == 'debian':
             self.packages += self.DEBIAN_PACKAGES
         self.browser_packages = self.BROWSER_COMMON_PACKAGES + self.BROWSER_DISTRO_PACKAGES
         self.mobile_android_packages = self.MOBILE_ANDROID_COMMON_PACKAGES + \
             self.MOBILE_ANDROID_DISTRO_PACKAGES
 
     def install_system_packages(self):
-        self.apt_install(*self.packages)
+        # Python 3 may not be present on all distros. Search for it and
+        # install if found.
+        packages = list(self.packages)
+
+        have_python3 = any([self.which('python3'), self.which('python3.6'),
+                            self.which('python3.5')])
+
+        if not have_python3:
+            python3_packages = self.check_output([
+                'apt-cache', 'pkgnames', 'python3'])
+            python3_packages = python3_packages.splitlines()
+
+            if 'python3' in python3_packages:
+                packages.extend(['python3', 'python3-dev'])
+
+        self.apt_install(*packages)
 
     def install_browser_packages(self):
         self.ensure_browser_packages()
 
     def install_browser_artifact_mode_packages(self):
         self.ensure_browser_packages(artifact_mode=True)
 
     def install_mobile_android_packages(self):
--- a/python/mozbuild/mozbuild/action/dump_env.py
+++ b/python/mozbuild/mozbuild/action/dump_env.py
@@ -1,10 +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/.
 
 # We invoke a Python program to dump our environment in order to get
 # native paths printed on Windows so that these paths can be incorporated
 # into Python configure's environment.
 import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from shellutil import quote
+
 for key, value in os.environ.items():
-    print('%s=%s' % (key, value))
+    print('%s=%s' % (key, quote(value)))
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SandboxBrokerPolicyFactory.h"
 #include "SandboxInfo.h"
 #include "SandboxLogging.h"
 
+#include "base/shared_memory.h"
 #include "mozilla/Array.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SandboxSettings.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/SandboxLaunch.h"
 #include "mozilla/dom/ContentChild.h"
@@ -182,17 +183,16 @@ AddLdconfigPaths(SandboxBroker::Policy* 
 }
 
 SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
 {
   // Policy entries that are the same in every process go here, and
   // are cached over the lifetime of the factory.
 #if defined(MOZ_CONTENT_SANDBOX)
   SandboxBroker::Policy* policy = new SandboxBroker::Policy;
-  policy->AddDir(rdwrcr, "/dev/shm");
   // Write permssions
   //
   // Bug 1308851: NVIDIA proprietary driver when using WebGL
   policy->AddFilePrefix(rdwr, "/dev", "nvidia");
 
   // Bug 1312678: radeonsi/Intel with DRI when using WebGL
   policy->AddDir(rdwr, "/dev/dri");
 
@@ -501,16 +501,25 @@ SandboxBrokerPolicyFactory::GetContentPo
 #endif
   }
 
   if (allowAlsa) {
     // Bug 1309098: ALSA support
     policy->AddDir(rdwr, "/dev/snd");
   }
 
+  if (allowPulse) {
+    policy->AddDir(rdwrcr, "/dev/shm");
+  } else {
+    std::string shmPath("/dev/shm");
+    if (base::SharedMemory::AppendPosixShmPrefix(&shmPath, aPid)) {
+      policy->AddPrefix(rdwrcr, shmPath.c_str());
+    }
+  }
+
 #ifdef MOZ_WIDGET_GTK
   if (const auto userDir = g_get_user_runtime_dir()) {
     // Bug 1321134: DConf's single bit of shared memory
     // The leaf filename is "user" by default, but is configurable.
     nsPrintfCString shmPath("%s/dconf/", userDir);
     policy->AddPrefix(rdwrcr, shmPath.get());
     policy->AddAncestors(shmPath.get());
     if (allowPulse) {
--- a/taskcluster/ci/source-test/file-metadata.yml
+++ b/taskcluster/ci/source-test/file-metadata.yml
@@ -20,11 +20,12 @@ bugzilla-components:
         command: >
             cd /builds/worker/checkouts/gecko
             && ./mach file-info bugzilla-automation /builds/worker/artifacts
     worker:
         artifacts:
             - type: directory
               path: /builds/worker/artifacts
               name: public
+        max-run-time: 2700
     when:
         files-changed:
             - '**'
--- a/testing/talos/talos/pageloader/chrome/pageloader.js
+++ b/testing/talos/talos/pageloader/chrome/pageloader.js
@@ -204,20 +204,21 @@ function plInit() {
         // However, when it loads a URI that requires a different remote type,
         // we lose the load listener and the injected tpRecordTime.remote,
         //
         // It also probably means that per test (or, in fact, per pageloader browser
         // instance which adds the load listener and injects tpRecordTime), all the
         // pages should be able to load in the same mode as the initial page - due
         // to this reinitialization on the switch.
         let remoteType = E10SUtils.getRemoteTypeForURI(pageUrls[0], true);
+        let tabbrowser = browserWindow.gBrowser;
         if (remoteType) {
-          browserWindow.XULBrowserWindow.forceInitialBrowserRemote(remoteType);
+          tabbrowser.updateBrowserRemoteness(tabbrowser.initialBrowser, true, { remoteType });
         } else {
-          browserWindow.XULBrowserWindow.forceInitialBrowserNonRemote(null);
+          tabbrowser.updateBrowserRemoteness(tabbrowser.initialBrowser, false);
         }
 
         browserWindow.resizeTo(winWidth, winHeight);
         browserWindow.moveTo(0, 0);
         browserWindow.focus();
         content = browserWindow.getBrowser();
         content.selectedBrowser.messageManager.loadFrameScript("chrome://pageloader/content/utils.js", false, true);
 
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -138,25 +138,30 @@ test-packages-manifest:
 	$(NSINSTALL) -D $(dir $(MOZ_TEST_PACKAGES_FILE))
 	$(PYTHON) $(topsrcdir)/build/gen_test_packages_manifest.py \
       --jsshell $(JSSHELL_NAME) \
       --dest-file '$(MOZ_TEST_PACKAGES_FILE)' \
       $(call PKG_ARG,common,zip) \
       $(foreach pkg,$(TEST_PKGS_ZIP),$(call PKG_ARG,$(pkg),zip)) \
       $(foreach pkg,$(TEST_PKGS_TARGZ),$(call PKG_ARG,$(pkg),tar.gz))
 
+ifdef UPLOAD_PATH
+test_archive_dir = $(UPLOAD_PATH)
+else
+test_archive_dir = $(DIST)/$(PKG_PATH)
+endif
+
 package-tests-prepare-dest:
-	@rm -f '$(DIST)/$(PKG_PATH)$(TEST_PACKAGE)'
-	$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
+	$(NSINSTALL) -D $(test_archive_dir)
 
 define package_archive
 package-tests-$(1): stage-all package-tests-prepare-dest
 	$$(call py_action,test_archive, \
 		$(1) \
-		'$$(abspath $$(DIST))/$$(PKG_PATH)/$$(PKG_BASENAME).$(1).tests.$(2)')
+		'$$(abspath $$(test_archive_dir))/$$(PKG_BASENAME).$(1).tests.$(2)')
 package-tests: package-tests-$(1)
 endef
 
 $(foreach name,$(TEST_PKGS_ZIP),$(eval $(call package_archive,$(name),zip)))
 $(foreach name,$(TEST_PKGS_TARGZ),$(eval $(call package_archive,$(name),tar.gz)))
 
 ifeq ($(MOZ_BUILD_APP),mobile/android)
 stage-all: stage-android
--- a/toolkit/components/places/SyncedBookmarksMirror.jsm
+++ b/toolkit/components/places/SyncedBookmarksMirror.jsm
@@ -459,33 +459,33 @@ class SyncedBookmarksMirror {
     // mirror, and we're holding the Sync lock at this point.
     MirrorLog.debug("Building remote tree from mirror");
     let { result: remoteTree, time: remoteTreeTiming } = await withTiming(
       "Fetch remote tree",
       () => this.fetchRemoteTree(remoteTimeSeconds)
     );
     applyStats.remoteTree = { time: remoteTreeTiming,
                               count: remoteTree.guidCount };
-    if (MirrorLog.level <= Log.Level.Trace) {
-      MirrorLog.trace("Built remote tree from mirror\n" +
+    if (MirrorLog.level <= Log.Level.Debug) {
+      MirrorLog.debug("Built remote tree from mirror\n" +
                       remoteTree.toASCIITreeString());
     }
 
     let observersToNotify = new BookmarkObserverRecorder(this.db);
 
     let changeRecords = await this.db.executeTransaction(async () => {
       MirrorLog.debug("Building local tree from Places");
       let { result: localTree, time: localTreeTiming } = await withTiming(
         "Fetch local tree",
         () => this.fetchLocalTree(localTimeSeconds)
       );
       applyStats.localTree = { time: localTreeTiming,
                                count: localTree.guidCount };
-      if (MirrorLog.level <= Log.Level.Trace) {
-        MirrorLog.trace("Built local tree from Places\n" +
+      if (MirrorLog.level <= Log.Level.Debug) {
+        MirrorLog.debug("Built local tree from Places\n" +
                         localTree.toASCIITreeString());
       }
 
       MirrorLog.debug("Fetching content info for new mirror items");
       let {
         result: newRemoteContents,
         time: remoteContentsTiming,
       } = await withTiming(
@@ -518,18 +518,18 @@ class SyncedBookmarksMirror {
         ));
         applyStats.merge = { time };
       } finally {
         for (let { value, extra } of merger.summarizeTelemetryEvents()) {
           this.recordTelemetryEvent("mirror", "merge", value, extra);
         }
       }
 
-      if (MirrorLog.level <= Log.Level.Trace) {
-        MirrorLog.trace([
+      if (MirrorLog.level <= Log.Level.Debug) {
+        MirrorLog.debug([
           "Built new merged tree",
           mergedRoot.toASCIITreeString(),
           ...merger.deletionsToStrings(),
         ].join("\n"));
       }
 
       // The merged tree should know about all items mentioned in the local
       // and remote trees. Otherwise, it's incomplete, and we'll corrupt
@@ -588,17 +588,17 @@ class SyncedBookmarksMirror {
 
       return changeRecords;
     });
 
     MirrorLog.debug("Replaying recorded observer notifications");
     try {
       await observersToNotify.notifyAll();
     } catch (ex) {
-      MirrorLog.error("Error notifying Places observers", ex);
+      MirrorLog.warn("Error notifying Places observers", ex);
     }
 
     for (let value in applyStats) {
       let extra = normalizeExtraTelemetryFields(applyStats[value]);
       if (extra) {
         this.recordTelemetryEvent("mirror", "apply", value, extra);
       }
     }
@@ -1281,28 +1281,28 @@ class SyncedBookmarksMirror {
    *        The root of the merged bookmark tree.
    * @param {Object[]} localDeletions
    *        `{ guid, level }` tuples for items to remove from Places and flag as
    *        merged.
    * @param {String[]} remoteDeletions
    *        Remotely deleted GUIDs that should be flagged as merged.
    */
   async updateLocalItemsInPlaces(mergedRoot, localDeletions, remoteDeletions) {
-    MirrorLog.debug("Setting up merge states table");
+    MirrorLog.trace("Setting up merge states table");
     let mergeStatesParams = Array.from(mergedRoot.mergeStatesParams());
     if (mergeStatesParams.length) {
       await this.db.execute(`
         INSERT INTO mergeStates(localGuid, mergedGuid, parentGuid, level,
                                 position, valueState, structureState)
         VALUES(IFNULL(:localGuid, :mergedGuid), :mergedGuid, :parentGuid,
                :level, :position, :valueState, :structureState)`,
         mergeStatesParams);
     }
 
-    MirrorLog.debug("Inserting new URLs into Places");
+    MirrorLog.trace("Inserting new URLs into Places");
     await this.db.execute(`
       INSERT OR IGNORE INTO moz_places(url, url_hash, rev_host, hidden,
                                        frecency, guid)
       SELECT u.url, u.hash, u.revHost, 0,
              (CASE v.kind WHEN :queryKind THEN 0 ELSE -1 END),
              IFNULL((SELECT h.guid FROM moz_places h
                      WHERE h.url_hash = u.hash AND
                            h.url = u.url), u.guid)
@@ -1311,29 +1311,29 @@ class SyncedBookmarksMirror {
       JOIN mergeStates r ON r.mergedGuid = v.guid
       WHERE r.valueState = :valueState`,
       { queryKind: SyncedBookmarksMirror.KIND.QUERY,
         valueState: BookmarkMergeState.TYPE.REMOTE });
     await this.db.execute(`DELETE FROM moz_updatehostsinsert_temp`);
 
     // Deleting from `itemsToMerge` fires the `insertNewLocalItems` and
     // `updateExistingLocalItems` triggers.
-    MirrorLog.debug("Updating value states for local bookmarks");
+    MirrorLog.trace("Updating value states for local bookmarks");
     await this.db.execute(`DELETE FROM itemsToMerge`);
 
     // Update the structure. The mirror stores structure info in a separate
     // table, like iOS, while Places stores structure info on children. We don't
     // check the parent's merge state here because our merged tree might
     // diverge from the server if we're missing children, or moved children
     // without parents to "unfiled". In that case, we *don't* want to reupload
     // the new local structure to the server.
-    MirrorLog.debug("Updating structure states for local bookmarks");
+    MirrorLog.trace("Updating structure states for local bookmarks");
     await this.db.execute(`DELETE FROM structureToMerge`);
 
-    MirrorLog.debug("Removing remotely deleted items from Places");
+    MirrorLog.trace("Removing remotely deleted items from Places");
     for (let chunk of PlacesSyncUtils.chunkArray(localDeletions,
       SQLITE_MAX_VARIABLE_NUMBER)) {
 
       let guids = chunk.map(({ guid }) => guid);
 
       // Record item removed notifications.
       await this.db.execute(`
         WITH
@@ -1379,17 +1379,17 @@ class SyncedBookmarksMirror {
       await this.db.execute(`
         UPDATE items SET
           needsMerge = 0
         WHERE needsMerge AND
               guid IN (SELECT guid FROM itemsRemoved
                        WHERE NOT isUntagging)`);
     }
 
-    MirrorLog.debug("Flagging remotely deleted items as merged");
+    MirrorLog.trace("Flagging remotely deleted items as merged");
     for (let chunk of PlacesSyncUtils.chunkArray(remoteDeletions,
       SQLITE_MAX_VARIABLE_NUMBER)) {
 
       await this.db.execute(`
         UPDATE items SET
           needsMerge = 0
         WHERE needsMerge AND
               guid IN (${new Array(chunk.length).fill("?").join(",")})`,
@@ -1399,17 +1399,17 @@ class SyncedBookmarksMirror {
 
   /**
    * Records Places observer notifications for removed, added, moved, and
    * changed items.
    *
    * @param {BookmarkObserverRecorder} observersToNotify
    */
   async noteObserverChanges(observersToNotify) {
-    MirrorLog.debug("Recording observer notifications for removed items");
+    MirrorLog.trace("Recording observer notifications for removed items");
     // `ORDER BY v.level DESC` sorts deleted children before parents, to ensure
     // that we update caches in the correct order (bug 1297941). We also order
     // by parent and position so that the notifications are well-ordered for
     // tests.
     let removedItemRows = await this.db.execute(`
       SELECT v.itemId AS id, v.parentId, v.parentGuid, v.position, v.type,
              h.url, v.guid, v.isUntagging
       FROM itemsRemoved v
@@ -1424,17 +1424,17 @@ class SyncedBookmarksMirror {
         urlHref: row.getResultByName("url"),
         guid: row.getResultByName("guid"),
         parentGuid: row.getResultByName("parentGuid"),
         isUntagging: row.getResultByName("isUntagging"),
       };
       observersToNotify.noteItemRemoved(info);
     }
 
-    MirrorLog.debug("Recording observer notifications for changed GUIDs");
+    MirrorLog.trace("Recording observer notifications for changed GUIDs");
     let changedGuidRows = await this.db.execute(`
       SELECT b.id, b.lastModified, b.type, b.guid AS newGuid,
              c.oldGuid, p.id AS parentId, p.guid AS parentGuid
       FROM guidsChanged c
       JOIN moz_bookmarks b ON b.id = c.itemId
       JOIN moz_bookmarks p ON p.id = b.parent
       ORDER BY c.level, p.id, b.position`);
     for await (let row of yieldingIterator(changedGuidRows)) {
@@ -1445,17 +1445,17 @@ class SyncedBookmarksMirror {
         newGuid: row.getResultByName("newGuid"),
         oldGuid: row.getResultByName("oldGuid"),
         parentId: row.getResultByName("parentId"),
         parentGuid: row.getResultByName("parentGuid"),
       };
       observersToNotify.noteGuidChanged(info);
     }
 
-    MirrorLog.debug("Recording observer notifications for new items");
+    MirrorLog.trace("Recording observer notifications for new items");
     let newItemRows = await this.db.execute(`
       SELECT b.id, p.id AS parentId, b.position, b.type, h.url,
              IFNULL(b.title, "") AS title, b.dateAdded, b.guid,
              p.guid AS parentGuid, n.isTagging
       FROM itemsAdded n
       JOIN moz_bookmarks b ON b.guid = n.guid
       JOIN moz_bookmarks p ON p.id = b.parent
       LEFT JOIN moz_places h ON h.id = b.fk
@@ -1471,17 +1471,17 @@ class SyncedBookmarksMirror {
         dateAdded: row.getResultByName("dateAdded"),
         guid: row.getResultByName("guid"),
         parentGuid: row.getResultByName("parentGuid"),
         isTagging: row.getResultByName("isTagging"),
       };
       observersToNotify.noteItemAdded(info);
     }
 
-    MirrorLog.debug("Recording observer notifications for moved items");
+    MirrorLog.trace("Recording observer notifications for moved items");
     let movedItemRows = await this.db.execute(`
       SELECT b.id, b.guid, b.type, p.id AS newParentId, c.oldParentId,
              p.guid AS newParentGuid, c.oldParentGuid,
              b.position AS newPosition, c.oldPosition
       FROM itemsMoved c
       JOIN moz_bookmarks b ON b.id = c.itemId
       JOIN moz_bookmarks p ON p.id = b.parent
       ORDER BY c.level, newParentId, newPosition`);
@@ -1495,17 +1495,17 @@ class SyncedBookmarksMirror {
         newParentGuid: row.getResultByName("newParentGuid"),
         oldParentGuid: row.getResultByName("oldParentGuid"),
         newPosition: row.getResultByName("newPosition"),
         oldPosition: row.getResultByName("oldPosition"),
       };
       observersToNotify.noteItemMoved(info);
     }
 
-    MirrorLog.debug("Recording observer notifications for changed items");
+    MirrorLog.trace("Recording observer notifications for changed items");
     let changedItemRows = await this.db.execute(`
       SELECT b.id, b.guid, b.lastModified, b.type,
              IFNULL(b.title, "") AS newTitle,
              IFNULL(c.oldTitle, "") AS oldTitle,
              h.url AS newURL, i.url AS oldURL,
              p.id AS parentId, p.guid AS parentGuid
       FROM itemsChanged c
       JOIN moz_bookmarks b ON b.id = c.itemId
@@ -1524,31 +1524,31 @@ class SyncedBookmarksMirror {
         newURLHref: row.getResultByName("newURL"),
         oldURLHref: row.getResultByName("oldURL"),
         parentId: row.getResultByName("parentId"),
         parentGuid: row.getResultByName("parentGuid"),
       };
       observersToNotify.noteItemChanged(info);
     }
 
-    MirrorLog.debug("Recording observer notifications for changed annos");
+    MirrorLog.trace("Recording observer notifications for changed annos");
     let annoRows = await this.db.execute(`
       SELECT itemId, annoName, wasRemoved FROM annosChanged
       ORDER BY itemId`);
     for await (let row of yieldingIterator(annoRows)) {
       let id = row.getResultByName("itemId");
       let name = row.getResultByName("annoName");
       if (row.getResultByName("wasRemoved")) {
         observersToNotify.noteAnnoRemoved(id, name);
       } else {
         observersToNotify.noteAnnoSet(id, name);
       }
     }
 
-    MirrorLog.debug("Recording notifications for changed keywords");
+    MirrorLog.trace("Recording notifications for changed keywords");
     let keywordsChangedRows = await this.db.execute(`
       SELECT EXISTS(SELECT 1 FROM itemsAdded WHERE keywordChanged) OR
              EXISTS(SELECT 1 FROM itemsChanged WHERE keywordChanged)
              AS keywordsChanged`);
     observersToNotify.shouldInvalidateKeywords =
       !!keywordsChangedRows[0].getResultByName("keywordsChanged");
   }
 
@@ -2794,17 +2794,17 @@ async function inflateTree(tree, pseudoT
 
 // Executes a function and returns a `{ result, time }` tuple, where `result` is
 // the function's return value, and `time` is the time taken to execute the
 // function.
 async function withTiming(name, func) {
   let startTime = Cu.now();
   let result = await func();
   let elapsedTime = Cu.now() - startTime;
-  MirrorLog.debug(`${name} took ${elapsedTime.toFixed(3)}ms`);
+  MirrorLog.trace(`${name} took ${elapsedTime.toFixed(3)}ms`);
   return { result, time: elapsedTime };
 }
 
 /**
  * Content info for an item in the local or remote tree. This is used to dedupe
  * NEW local items to remote items that don't exist locally. See `makeDupeKey`
  * for how we determine if two items are dupes.
  */
@@ -4577,17 +4577,17 @@ class BookmarkObserverRecorder {
     await this.notifyAnnoObservers();
     if (this.shouldInvalidateLivemarks) {
       await PlacesUtils.livemarks.invalidateCachedLivemarks();
     }
     await this.updateFrecencies();
   }
 
   async updateFrecencies() {
-    MirrorLog.debug("Recalculating frecencies for new URLs");
+    MirrorLog.trace("Recalculating frecencies for new URLs");
     await this.db.execute(`
       UPDATE moz_places SET
         frecency = CALCULATE_FRECENCY(id)
       WHERE frecency = -1`);
   }
 
   noteItemAdded(info) {
     let uri = info.urlHref ? Services.io.newURI(info.urlHref) : null;
@@ -4670,32 +4670,32 @@ class BookmarkObserverRecorder {
     }
     this.annoObserverNotifications.push({
       name: "onItemAnnotationRemoved",
       args: [id, name, PlacesUtils.bookmarks.SOURCES.SYNC],
     });
   }
 
   async notifyBookmarkObservers() {
-    MirrorLog.debug("Notifying bookmark observers");
+    MirrorLog.trace("Notifying bookmark observers");
     let observers = PlacesUtils.bookmarks.getObservers();
     for (let observer of observers) {
       this.notifyObserver(observer, "onBeginUpdateBatch");
       for await (let info of yieldingIterator(this.bookmarkObserverNotifications)) {
         if (info.isTagging && observer.skipTags) {
           continue;
         }
         this.notifyObserver(observer, info.name, info.args);
       }
       this.notifyObserver(observer, "onEndUpdateBatch");
     }
   }
 
   async notifyAnnoObservers() {
-    MirrorLog.debug("Notifying anno observers");
+    MirrorLog.trace("Notifying anno observers");
     let observers = PlacesUtils.annotations.getObservers();
     for (let observer of observers) {
       let wrapped = yieldingIterator(this.annoObserverNotifications);
       for await (let { name, args } of wrapped) {
         this.notifyObserver(observer, name, args);
       }
     }
   }
--- a/toolkit/components/reader/test/browser.ini
+++ b/toolkit/components/reader/test/browser.ini
@@ -10,13 +10,19 @@ support-files =
   readerModeArticleHiddenNodes.html
 [browser_readerMode_with_anchor.js]
 support-files =
   readerModeArticle.html
 uses-unsafe-cpows = true
 [browser_bug1124271_readerModePinnedTab.js]
 support-files =
   readerModeArticle.html
+[browser_bug1453818_samesite_cookie.js]
+support-files =
+  getCookies.html
+  linkToGetCookies.html
+  setSameSiteCookie.html
+  setSameSiteCookie.html^headers^
 [browser_readerMode_readingTime.js]
 support-files =
   readerModeArticle.html
   readerModeArticleShort.html
   readerModeArticleMedium.html
new file mode 100644
--- /dev/null
+++ b/toolkit/components/reader/test/browser_bug1453818_samesite_cookie.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_ORIGIN1 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
+const TEST_ORIGIN2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.org");
+
+async function clickLink(browser) {
+  info("Waiting for the page to load after clicking the link...");
+  let pageLoaded = BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
+  await ContentTask.spawn(browser, null, async function() {
+    let link = content.document.getElementById("link");
+    ok(link, "The link element was found.");
+    link.click();
+  });
+  await pageLoaded;
+}
+
+async function checkCookiePresent(browser) {
+  await ContentTask.spawn(browser, null, async function() {
+    let cookieSpan = content.document.getElementById("cookieSpan");
+    ok(cookieSpan, "cookieSpan element should be in document");
+    is(cookieSpan.textContent, "foo=bar", "The SameSite cookie was sent correctly.");
+  });
+}
+
+async function checkCookie(sameSiteEnabled, browser) {
+  if (sameSiteEnabled) {
+    info("Check that the SameSite cookie was not sent.");
+    await ContentTask.spawn(browser, null, async function() {
+      let cookieSpan = content.document.getElementById("cookieSpan");
+      ok(cookieSpan, "cookieSpan element should be in document");
+      is(cookieSpan.textContent, "", "The SameSite cookie was blocked correctly.");
+    });
+  } else {
+    info("Check that the SameSite cookie was sent.");
+    await checkCookiePresent(browser);
+  }
+}
+
+async function runTest(sameSiteEnabled) {
+  await SpecialPowers.pushPrefEnv({
+    set: [["network.cookie.same-site.enabled", sameSiteEnabled],
+          ["reader.parse-on-load.enabled", true]],
+  });
+
+  info("Set a SameSite=strict cookie.");
+  await BrowserTestUtils.withNewTab(TEST_ORIGIN1 + "setSameSiteCookie.html", () => {});
+
+  info("Check that the cookie has been correctly set.");
+  await BrowserTestUtils.withNewTab(TEST_ORIGIN1 + "getCookies.html", async function(browser) {
+    await checkCookiePresent(browser);
+  });
+
+  info("Open a cross-origin page with a link to the domain that set the cookie.");
+  {
+    let browser;
+    let pageLoaded;
+    let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+      let t = BrowserTestUtils.addTab(gBrowser, TEST_ORIGIN2 + "linkToGetCookies.html");
+      gBrowser.selectedTab = t;
+      browser = gBrowser.selectedBrowser;
+      pageLoaded = BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
+      return t;
+    }, false);
+
+    info("Waiting for the page to load in normal mode...");
+    await pageLoaded;
+
+    await clickLink(browser);
+    await checkCookie(sameSiteEnabled, browser);
+    await BrowserTestUtils.removeTab(tab);
+  }
+
+  info("Open the cross-origin page again.");
+  await BrowserTestUtils.withNewTab(TEST_ORIGIN2 + "linkToGetCookies.html", async function(browser) {
+    let pageShown = BrowserTestUtils.waitForContentEvent(browser, "AboutReaderContentReady");
+    let readerButton = document.getElementById("reader-mode-button");
+    ok(readerButton, "readerButton should be available");
+    readerButton.click();
+
+    info("Waiting for the page to be displayed in reader mode...");
+    await pageShown;
+
+    await clickLink(browser);
+    await checkCookie(sameSiteEnabled, browser);
+  });
+}
+
+add_task(async function() {
+  await runTest(true);
+});
+
+add_task(async function() {
+  await runTest(false);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/reader/test/getCookies.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+    </head>
+    <body>
+        <p>Cookie: <span id="cookieSpan">(none yet)</span></p>
+        <br>
+        <script>
+        let cookieSpan = document.getElementById("cookieSpan");
+        cookieSpan.textContent = document.cookie;
+        </script>
+    </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/reader/test/linkToGetCookies.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+    </head>
+    <body>
+        <article>
+            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</p>
+
+            <p><a href="http://example.com/browser/toolkit/components/reader/test/getCookies.html" id="link">Cross-origin link to getCookies.html</a></p>
+        </article>
+    </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/reader/test/setSameSiteCookie.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+    </head>
+    <body>
+        <p>This page just set a cookie with the <code>SameSite</code> attribute.</p>
+    </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/reader/test/setSameSiteCookie.html^headers^
@@ -0,0 +1,1 @@
+Set-Cookie: foo=bar; Path='/' ; SameSite=strict
--- a/toolkit/modules/WindowDraggingUtils.jsm
+++ b/toolkit/modules/WindowDraggingUtils.jsm
@@ -56,36 +56,39 @@ WindowDraggingElement.prototype = {
            this._elem.localName == "panel";
   },
   handleEvent(aEvent) {
     let isPanel = this.isPanel();
     switch (aEvent.type) {
       case "mousedown":
         if (!this.shouldDrag(aEvent))
           return;
-
-        if (/^gtk/i.test(AppConstants.MOZ_WIDGET_TOOLKIT)) {
-          // On GTK, there is a toolkit-level function which handles
-          // window dragging, which must be used.
-          this._window.beginWindowMove(aEvent, isPanel ? this._elem : null);
-          break;
-        }
-        if (isPanel) {
-          let screenRect = this._elem.getOuterScreenRect();
-          this._deltaX = aEvent.screenX - screenRect.left;
-          this._deltaY = aEvent.screenY - screenRect.top;
-        } else {
-          this._deltaX = aEvent.screenX - this._window.screenX;
-          this._deltaY = aEvent.screenY - this._window.screenY;
+        if (!/^gtk/i.test(AppConstants.MOZ_WIDGET_TOOLKIT)) {
+          if (isPanel) {
+            let screenRect = this._elem.getOuterScreenRect();
+            this._deltaX = aEvent.screenX - screenRect.left;
+            this._deltaY = aEvent.screenY - screenRect.top;
+          } else {
+            this._deltaX = aEvent.screenX - this._window.screenX;
+            this._deltaY = aEvent.screenY - this._window.screenY;
+          }
         }
         this._draggingWindow = true;
         this._window.addEventListener("mousemove", this);
         this._window.addEventListener("mouseup", this);
         break;
       case "mousemove":
+        if (/^gtk/i.test(AppConstants.MOZ_WIDGET_TOOLKIT)) {
+          // On GTK, there is a toolkit-level function which handles
+          // window dragging. We want to start moving the window
+          // on the first mousemove event after mousedown.
+          this._window.beginWindowMove(aEvent, isPanel ? this._elem : null);
+          this._window.removeEventListener("mousemove", this);
+          break;
+        }
         if (this._draggingWindow) {
           let toDrag = this.isPanel() ? this._elem : this._window;
           toDrag.moveTo(aEvent.screenX - this._deltaX, aEvent.screenY - this._deltaY);
         }
         break;
       case "mouseup":
         if (this._draggingWindow) {
           this._draggingWindow = false;
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -131,33 +131,28 @@ ifeq (bundle,$(MOZ_FS_LAYOUT))
 endif
 	$(NSINSTALL) -D $(DESTDIR)$(installdir)
 	(cd $(DIST)/$(MOZ_PKG_DIR) && $(TAR) --exclude=precomplete $(TAR_CREATE_FLAGS) - .) | \
 	  (cd $(DESTDIR)$(installdir) && tar -xf -)
 	$(NSINSTALL) -D $(DESTDIR)$(bindir)
 	$(RM) -f $(DESTDIR)$(bindir)/$(MOZ_APP_NAME)
 	ln -s $(installdir)/$(MOZ_APP_NAME) $(DESTDIR)$(bindir)
 
-checksum:
+upload:
+	$(PYTHON) -u $(MOZILLA_DIR)/build/upload.py --base-path $(DIST) $(UPLOAD_FILES)
 	mkdir -p `dirname $(CHECKSUM_FILE)`
 	@$(PYTHON) $(MOZILLA_DIR)/build/checksums.py \
 		-o $(CHECKSUM_FILE) \
 		$(CHECKSUM_ALGORITHM_PARAM) \
-		-s $(call QUOTED_WILDCARD,$(DIST)) \
-		$(UPLOAD_FILES)
+		$(UPLOAD_PATH)
 	@echo 'CHECKSUM FILE START'
 	@cat $(CHECKSUM_FILE)
 	@echo 'CHECKSUM FILE END'
 	$(SIGN_CHECKSUM_CMD)
-
-
-upload: checksum
-	$(PYTHON) -u $(MOZILLA_DIR)/build/upload.py --base-path $(DIST) \
-		$(UPLOAD_FILES) \
-		$(CHECKSUM_FILES)
+	$(PYTHON) -u $(MOZILLA_DIR)/build/upload.py --base-path $(DIST) $(CHECKSUM_FILES)
 
 # source-package creates a source tarball from the files in MOZ_PKG_SRCDIR,
 # which is either set to a clean checkout or defaults to $topsrcdir
 source-package:
 	@echo 'Generate the sourcestamp file'
 	# Make sure to have repository information available and then generate the
 	# sourcestamp file.
 	$(MAKE) -C $(DEPTH) 'source-repo.h'
--- a/toolkit/mozapps/installer/upload-files.mk
+++ b/toolkit/mozapps/installer/upload-files.mk
@@ -381,25 +381,16 @@ endif
 
 UPLOAD_FILES= \
   $(call QUOTED_WILDCARD,$(DIST)/$(PACKAGE)) \
   $(call QUOTED_WILDCARD,$(INSTALLER_PACKAGE)) \
   $(call QUOTED_WILDCARD,$(DIST)/$(COMPLETE_MAR)) \
   $(call QUOTED_WILDCARD,$(DIST)/$(LANGPACK)) \
   $(call QUOTED_WILDCARD,$(wildcard $(DIST)/$(PARTIAL_MAR))) \
   $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(TEST_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(CPP_TEST_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(XPC_TEST_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(MOCHITEST_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(TALOS_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(AWSY_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(REFTEST_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(WP_TEST_PACKAGE)) \
-  $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(GTEST_PACKAGE)) \
   $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip) \
   $(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(GENERATED_SOURCE_FILE_PACKAGE)) \
   $(call QUOTED_WILDCARD,$(MOZ_SOURCESTAMP_FILE)) \
   $(call QUOTED_WILDCARD,$(MOZ_BUILDINFO_FILE)) \
   $(call QUOTED_WILDCARD,$(MOZ_BUILDID_INFO_TXT_FILE)) \
   $(call QUOTED_WILDCARD,$(MOZ_MOZINFO_FILE)) \
   $(call QUOTED_WILDCARD,$(MOZ_TEST_PACKAGES_FILE)) \
   $(call QUOTED_WILDCARD,$(PKG_JSSHELL)) \
--- a/tools/lint/spell/__init__.py
+++ b/tools/lint/spell/__init__.py
@@ -25,16 +25,18 @@ your PATH or set the CODESPELL environme
 
 https://github.com/lucasdemarchi/codespell or your system's package manager.
 """.strip()
 
 results = []
 
 CODESPELL_FORMAT_REGEX = re.compile(r'(.*):(.*): (.*) ==> (.*)$')
 
+here = os.path.abspath(os.path.dirname(__file__))
+
 
 class CodespellProcess(ProcessHandlerMixin):
     def __init__(self, config, *args, **kwargs):
         self.config = config
         kwargs['processOutputLine'] = [self.process_line]
         ProcessHandlerMixin.__init__(self, *args, **kwargs)
 
     def process_line(self, line):
@@ -93,24 +95,27 @@ def lint(paths, config, fix=None, **lint
 
     if not binary:
         print(CODESPELL_NOT_FOUND)
         if 'MOZ_AUTOMATION' in os.environ:
             return 1
         return []
 
     config['root'] = lintargs['root']
+    exclude_list = os.path.join(here, 'exclude-list.txt')
     cmd_args = [binary,
                 '--disable-colors',
                 # Silence some warnings:
                 # 1: disable warnings about wrong encoding
                 # 2: disable warnings about binary file
                 # 4: shut down warnings about automatic fixes
                 #    that were disabled in dictionary.
                 '--quiet-level=4',
+                '--ignore-words=' + exclude_list,
+                '--skip=exclude-list.txt',
                 ]
 
 # Disabled for now because of
 # https://github.com/lucasdemarchi/codespell/issues/314
 #    if fix:
 #        cmd_args.append('--write-changes')
 
     base_command = cmd_args + paths
new file mode 100644
--- /dev/null
+++ b/tools/lint/spell/exclude-list.txt
@@ -0,0 +1,6 @@
+cas
+optin
+aparent
+acount
+te
+
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -7557,28 +7557,30 @@ void nsWindow::SetWindowTranslucencyInne
     HideWindowChrome(true);
   } else if (mHideChrome && mTransparencyMode == eTransparencyTransparent) {
     // if we're switching out of transparent, re-enable our parent's chrome.
     HideWindowChrome(false);
   }
 
   LONG_PTR style = ::GetWindowLongPtrW(hWnd, GWL_STYLE),
     exStyle = ::GetWindowLongPtr(hWnd, GWL_EXSTYLE);
- 
-   if (parent->mIsVisible)
-     style |= WS_VISIBLE;
-   if (parent->mSizeMode == nsSizeMode_Maximized)
-     style |= WS_MAXIMIZE;
-   else if (parent->mSizeMode == nsSizeMode_Minimized)
-     style |= WS_MINIMIZE;
-
-   if (aMode == eTransparencyTransparent)
-     exStyle |= WS_EX_LAYERED;
-   else
-     exStyle &= ~WS_EX_LAYERED;
+
+  if (parent->mIsVisible) {
+    style |= WS_VISIBLE;
+    if (parent->mSizeMode == nsSizeMode_Maximized) {
+      style |= WS_MAXIMIZE;
+    } else if (parent->mSizeMode == nsSizeMode_Minimized) {
+      style |= WS_MINIMIZE;
+    }
+  }
+
+  if (aMode == eTransparencyTransparent)
+    exStyle |= WS_EX_LAYERED;
+  else
+    exStyle &= ~WS_EX_LAYERED;
 
   VERIFY_WINDOW_STYLE(style);
   ::SetWindowLongPtrW(hWnd, GWL_STYLE, style);
   ::SetWindowLongPtrW(hWnd, GWL_EXSTYLE, exStyle);
 
   if (HasGlass())
     memset(&mGlassMargins, 0, sizeof mGlassMargins);
   mTransparencyMode = aMode;
--- a/xpfe/appshell/nsIXULBrowserWindow.idl
+++ b/xpfe/appshell/nsIXULBrowserWindow.idl
@@ -45,17 +45,16 @@ interface nsIXULBrowserWindow : nsISuppo
                                 in nsIDOMNode linkNode,
                                 in boolean isAppTab);
 
   /**
    * Find the initial browser of the window and set its remote attributes.
    * This can be used to ensure that there is a browser in a new window of the
    * correct type when it first spawns.
    */
-  void forceInitialBrowserRemote(in AString aRemoteType);
   void forceInitialBrowserNonRemote(in mozIDOMWindowProxy openerWindow);
 
   /**
    * Determines whether a load should continue.
    *
    * @param aDocShell
    *        The docshell performing the load.
    * @param aURI