Merge fx-team to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 13 Jan 2015 17:44:56 -0800
changeset 223672 63006936ab99b4ea5db20d1d7d67ce6c7f16ebf5
parent 223645 3100ceecc1bb0dfd1500ea08d5d2ae64294b3180 (current diff)
parent 223671 dfc93c68f9c799f93c52f99740baf88623054ab3 (diff)
child 223678 906a97d224d6ec663c39e8e7192647f5dd80e732
child 223732 b2d1d57c25b9c3a2b484884792aa6bfb99a6db31
child 223744 146058338f8582290c7ba0bc8de9756c2a93da97
push id28100
push userkwierso@gmail.com
push dateWed, 14 Jan 2015 01:44:55 +0000
treeherdermozilla-central@63006936ab99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
63006936ab99 / 38.0a1 / 20150114030202 / files
nightly linux64
63006936ab99 / 38.0a1 / 20150114030202 / files
nightly mac
63006936ab99 / 38.0a1 / 20150114030202 / files
nightly win32
63006936ab99 / 38.0a1 / 20150114030202 / files
nightly win64
63006936ab99 / 38.0a1 / 20150114030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c a=merge
b2g/installer/package-manifest.in
browser/components/places/BrowserPlaces.manifest
browser/components/places/PlacesProtocolHandler.js
browser/installer/package-manifest.in
browser/themes/linux/browser.css
dom/base/nsFrameLoader.cpp
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -372,17 +372,16 @@
 @BINPATH@/components/fuelApplication.manifest
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
 @BINPATH@/components/BrowserComponents.manifest
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
-@BINPATH@/components/BrowserPlaces.manifest
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsTryToClose.manifest
 @BINPATH@/components/nsTryToClose.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
 @BINPATH@/components/nsLoginManagerPrompter.js
 @BINPATH@/components/NetworkGeolocationProvider.manifest
@@ -498,17 +497,16 @@
 @BINPATH@/components/nsLivemarkService.js
 @BINPATH@/components/nsTaggingService.js
 @BINPATH@/components/nsPlacesDBFlush.js
 @BINPATH@/components/nsPlacesAutoComplete.manifest
 @BINPATH@/components/nsPlacesAutoComplete.js
 @BINPATH@/components/UnifiedComplete.manifest
 @BINPATH@/components/UnifiedComplete.js
 @BINPATH@/components/nsPlacesExpiration.js
-@BINPATH@/components/PlacesProtocolHandler.js
 @BINPATH@/components/PlacesCategoriesStarter.js
 @BINPATH@/components/nsDefaultCLH.manifest
 @BINPATH@/components/nsDefaultCLH.js
 @BINPATH@/components/nsContentPrefService.manifest
 @BINPATH@/components/nsContentPrefService.js
 @BINPATH@/components/nsContentDispatchChooser.manifest
 @BINPATH@/components/nsContentDispatchChooser.js
 @BINPATH@/components/nsHandlerService.manifest
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1372,16 +1372,18 @@ pref("devtools.inspector.activeSidebar",
 pref("devtools.inspector.markupPreview", false);
 pref("devtools.inspector.remote", false);
 // Expand pseudo-elements by default in the rule-view
 pref("devtools.inspector.show_pseudo_elements", true);
 // The default size for image preview tooltips in the rule-view/computed-view/markup-view
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
+// Show all native anonymous content (like controls in <video> tags)
+pref("devtools.inspector.showAllAnonymousContent", false);
 
 // DevTools default color unit
 pref("devtools.defaultColorUnit", "hex");
 
 // Enable the Responsive UI tool
 pref("devtools.responsiveUI.no-reload-notification", false);
 
 // Enable the Debugger
@@ -1776,17 +1778,17 @@ pref("media.gmp-gmpopenh264.provider.ena
 pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
 
 #ifdef NIGHTLY_BUILD
 pref("browser.polaris.enabled", false);
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
 
 #ifdef NIGHTLY_BUILD
-pref("browser.tabs.remote.autostart.1", false);
+pref("browser.tabs.remote.autostart.1", true);
 #endif
 
 // Temporary pref to allow printing in e10s windows on some platforms.
 #ifdef UNIX_BUT_NOT_MAC
 pref("print.enable_e10s_testing", false);
 #else
 pref("print.enable_e10s_testing", true);
 #endif
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -545,16 +545,17 @@
               <menuitem id="menu_pageInfo"
                         accesskey="&pageInfoCmd.accesskey;"
                         label="&pageInfoCmd.label;"
 #ifndef XP_WIN
                         key="key_viewInfo"
 #endif
                         command="View:PageInfo"/>
               <menu id="menu_mirrorTabCmd"
+                    hidden="true"
                     accesskey="&mirrorTabCmd.accesskey;"
                     label="&mirrorTabCmd.label;">
                 <menupopup id="menu_mirrorTab-popup"
                            onpopupshowing="populateMirrorTabMenu(this)"/>
               </menu>
 #ifndef XP_UNIX
               <menuseparator id="prefSep"/>
               <menuitem id="menu_preferences"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2935,17 +2935,17 @@ function getMeOutOfHere() {
 
 function BrowserFullScreen()
 {
   window.fullScreen = !window.fullScreen;
 }
 
 function mirrorShow(popup) {
   let services = CastingApps.getServicesForMirroring();
-  popup.ownerDocument.getElementById("menu_mirrorTabCmd").disabled = !services.length;
+  popup.ownerDocument.getElementById("menu_mirrorTabCmd").hidden = !services.length;
 }
 
 function mirrorMenuItemClicked(event) {
   gBrowser.selectedBrowser.messageManager.sendAsyncMessage("SecondScreen:tab-mirror",
                                                            {service: event.originalTarget._service});
 }
 
 function populateMirrorTabMenu(popup) {
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
@@ -249,10 +249,25 @@ let tests = [
       PopupNotifications._update();
     },
     onShown: function (popup) {
       checkPopup(popup, this.notifyObj2);
       this.notification1.remove();
       this.notification2.remove();
     },
     onHidden: function(popup) { }
+  },
+  // The anchor icon should be shown for notifications in background windows.
+  { id: "Test#13",
+    run: function() {
+      let notifyObj = new BasicNotification(this.id);
+      notifyObj.options.dismissed = true;
+      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
+      whenDelayedStartupFinished(win, function() {
+        showNotification(notifyObj);
+        let anchor = document.getElementById("default-notification-icon");
+        is(anchor.getAttribute("showing"), "true", "the anchor is shown");
+        win.close();
+        goNext();
+      });
+    }
   }
 ];
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -274,16 +274,18 @@ loop.store.ActiveRoomStore = (function()
       this.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
     },
 
     /**
      * Handles the action that signifies when media permission has been
      * granted and starts joining the room.
      */
     gotMediaPermission: function() {
+      this.setStoreState({roomState: ROOM_STATES.JOINING});
+
       this._mozLoop.rooms.join(this._storeState.roomToken,
         function(error, responseData) {
           if (error) {
             this.dispatchAction(new sharedActions.RoomFailure({error: error}));
             return;
           }
 
           this.dispatchAction(new sharedActions.JoinedRoom({
@@ -456,17 +458,18 @@ loop.store.ActiveRoomStore = (function()
 
       this._sdkDriver.disconnectSession();
 
       if (this._timeout) {
         clearTimeout(this._timeout);
         delete this._timeout;
       }
 
-      if (this._storeState.roomState === ROOM_STATES.JOINED ||
+      if (this._storeState.roomState === ROOM_STATES.JOINING ||
+          this._storeState.roomState === ROOM_STATES.JOINED ||
           this._storeState.roomState === ROOM_STATES.SESSION_CONNECTED ||
           this._storeState.roomState === ROOM_STATES.HAS_PARTICIPANTS) {
         this._mozLoop.rooms.leave(this._storeState.roomToken,
           this._storeState.sessionToken);
       }
 
       this.setStoreState({roomState: nextState || ROOM_STATES.ENDED});
     },
--- a/browser/components/loop/content/shared/js/feedbackViews.js
+++ b/browser/components/loop/content/shared/js/feedbackViews.js
@@ -70,17 +70,17 @@ loop.shared.views.FeedbackView = (functi
       return {pending: false};
     },
 
     _getCategories: function() {
       return {
         audio_quality: l10n.get("feedback_category_audio_quality"),
         video_quality: l10n.get("feedback_category_video_quality"),
         disconnected : l10n.get("feedback_category_was_disconnected"),
-        confusing:     l10n.get("feedback_category_confusing"),
+        confusing:     l10n.get("feedback_category_confusing2"),
         other:         l10n.get("feedback_category_other2")
       };
     },
 
     _getCategoryFields: function() {
       var categories = this._getCategories();
       return Object.keys(categories).map(function(category, key) {
         return (
@@ -137,17 +137,17 @@ loop.shared.views.FeedbackView = (functi
         happy: false,
         category: this.state.category,
         description: this.state.description
       }));
     },
 
     render: function() {
       return (
-        React.createElement(FeedbackLayout, {title: l10n.get("feedback_what_makes_you_sad"), 
+        React.createElement(FeedbackLayout, {title: l10n.get("feedback_category_list_heading"), 
                         reset: this.props.reset}, 
           React.createElement("form", {onSubmit: this.handleFormSubmit}, 
             this._getCategoryFields(), 
             React.createElement("p", null, 
               React.createElement("input", {type: "text", ref: "description", name: "description", 
                 className: "feedback-description", 
                 onChange: this.handleDescriptionFieldChange, 
                 value: this.state.description, 
--- a/browser/components/loop/content/shared/js/feedbackViews.jsx
+++ b/browser/components/loop/content/shared/js/feedbackViews.jsx
@@ -70,17 +70,17 @@ loop.shared.views.FeedbackView = (functi
       return {pending: false};
     },
 
     _getCategories: function() {
       return {
         audio_quality: l10n.get("feedback_category_audio_quality"),
         video_quality: l10n.get("feedback_category_video_quality"),
         disconnected : l10n.get("feedback_category_was_disconnected"),
-        confusing:     l10n.get("feedback_category_confusing"),
+        confusing:     l10n.get("feedback_category_confusing2"),
         other:         l10n.get("feedback_category_other2")
       };
     },
 
     _getCategoryFields: function() {
       var categories = this._getCategories();
       return Object.keys(categories).map(function(category, key) {
         return (
@@ -137,17 +137,17 @@ loop.shared.views.FeedbackView = (functi
         happy: false,
         category: this.state.category,
         description: this.state.description
       }));
     },
 
     render: function() {
       return (
-        <FeedbackLayout title={l10n.get("feedback_what_makes_you_sad")}
+        <FeedbackLayout title={l10n.get("feedback_category_list_heading")}
                         reset={this.props.reset}>
           <form onSubmit={this.handleFormSubmit}>
             {this._getCategoryFields()}
             <p>
               <input type="text" ref="description" name="description"
                 className="feedback-description"
                 onChange={this.handleDescriptionFieldChange}
                 value={this.state.description}
--- a/browser/components/loop/content/shared/js/roomStates.js
+++ b/browser/components/loop/content/shared/js/roomStates.js
@@ -11,16 +11,18 @@ loop.store.ROOM_STATES = {
     // The initial state of the room
     INIT: "room-init",
     // The store is gathering the room data
     GATHER: "room-gather",
     // The store has got the room data
     READY: "room-ready",
     // Obtaining media from the user
     MEDIA_WAIT: "room-media-wait",
+    // Joining the room is taking place
+    JOINING: "room-joining",
     // The room is known to be joined on the loop-server
     JOINED: "room-joined",
     // The room is connected to the sdk server.
     SESSION_CONNECTED: "room-session-connected",
     // There are participants in the room.
     HAS_PARTICIPANTS: "room-has-participants",
     // There was an issue with the room
     FAILED: "room-failed",
--- a/browser/components/loop/content/shared/js/websocket.js
+++ b/browser/components/loop/content/shared/js/websocket.js
@@ -82,17 +82,19 @@ loop.CallConnectionWebSocket = (function
 
     /**
      * Closes the websocket. This shouldn't be the normal action as the server
      * will normally close the socket. Only in bad error cases, or where we need
      * to close the socket just before closing the window (to avoid an error)
      * should we call this.
      */
     close: function() {
-      this.socket.close();
+      if (this.socket) {
+        this.socket.close();
+      }
     },
 
     _clearConnectionFlags: function() {
       clearTimeout(this.connectDetails.timeout);
       delete this.connectDetails;
     },
 
     /**
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -89,16 +89,17 @@ loop.standaloneRoomViews = (function(moz
           return (
             React.createElement("div", {className: "room-inner-info-area"}, 
               React.createElement("p", {className: "prompt-media-message"}, 
                 msg
               )
             )
           );
         }
+        case ROOM_STATES.JOINING:
         case ROOM_STATES.JOINED:
         case ROOM_STATES.SESSION_CONNECTED: {
           return (
             React.createElement("div", {className: "room-inner-info-area"}, 
               React.createElement("p", {className: "empty-room-message"}, 
                 mozL10n.get("rooms_only_occupant_label")
               )
             )
@@ -289,17 +290,17 @@ loop.standaloneRoomViews = (function(moz
       document.body.classList.add("is-standalone-room");
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.activeRoomStore);
     },
 
     /**
-     * Watches for when we transition to JOINED room state, so we can request
+     * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -89,16 +89,17 @@ loop.standaloneRoomViews = (function(moz
           return (
             <div className="room-inner-info-area">
               <p className="prompt-media-message">
                 {msg}
               </p>
             </div>
           );
         }
+        case ROOM_STATES.JOINING:
         case ROOM_STATES.JOINED:
         case ROOM_STATES.SESSION_CONNECTED: {
           return (
             <div className="room-inner-info-area">
               <p className="empty-room-message">
                 {mozL10n.get("rooms_only_occupant_label")}
               </p>
             </div>
@@ -289,17 +290,17 @@ loop.standaloneRoomViews = (function(moz
       document.body.classList.add("is-standalone-room");
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.activeRoomStore);
     },
 
     /**
-     * Watches for when we transition to JOINED room state, so we can request
+     * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
--- a/browser/components/loop/standalone/content/l10n/en-US/loop.properties
+++ b/browser/components/loop/standalone/content/l10n/en-US/loop.properties
@@ -63,22 +63,22 @@ vendor_alttext={{vendorShortname}} logo
 call_url_creation_date_label=(from {{call_url_creation_date}})
 call_progress_getting_media_description={{clientShortname}} requires access to your camera and microphone.
 call_progress_getting_media_title=Waiting for media…
 call_progress_connecting_description=Connecting…
 call_progress_ringing_description=Ringing…
 fxos_app_needed=Please install the {{fxosAppName}} app from the Firefox Marketplace.
 
 feedback_call_experience_heading2=How was your conversation?
-feedback_what_makes_you_sad=What makes you sad?
 feedback_thank_you_heading=Thank you for your feedback!
+feedback_category_list_heading=What made you sad?
 feedback_category_audio_quality=Audio quality
 feedback_category_video_quality=Video quality
 feedback_category_was_disconnected=Was disconnected
-feedback_category_confusing=Confusing
+feedback_category_confusing2=Confusing controls
 feedback_category_other2=Other
 feedback_custom_category_text_placeholder=What went wrong?
 feedback_submit_button=Submit
 feedback_back_button=Back
 ## LOCALIZATION NOTE (feedback_window_will_close_in2):
 ## Gaia l10n format; see https://github.com/mozilla-b2g/gaia/blob/f108c706fae43cd61628babdd9463e7695b2496e/apps/email/locales/email.en-US.properties#L387
 ## In this item, don't translate the part between {{..}}
 feedback_window_will_close_in2={[ plural(countdown) ]}
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -350,16 +350,22 @@ describe("loop.store.ActiveRoomStore", f
     });
   });
 
   describe("#gotMediaPermission", function() {
     beforeEach(function() {
       store.setStoreState({roomToken: "tokenFake"});
     });
 
+    it("should set the room state to JOINING", function() {
+      store.gotMediaPermission();
+
+      expect(store.getStoreState().roomState).eql(ROOM_STATES.JOINING);
+    });
+
     it("should call rooms.join on mozLoop", function() {
       store.gotMediaPermission();
 
       sinon.assert.calledOnce(fakeMozLoop.rooms.join);
       sinon.assert.calledWith(fakeMozLoop.rooms.join, "tokenFake");
     });
 
     it("should dispatch `JoinedRoom` on success", function() {
@@ -672,16 +678,27 @@ describe("loop.store.ActiveRoomStore", f
     it("should call mozLoop.rooms.leave", function() {
       store.windowUnload();
 
       sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
       sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
         "fakeToken", "1627384950");
     });
 
+    it("should call mozLoop.rooms.leave if the room state is JOINING",
+      function() {
+        store.setStoreState({roomState: ROOM_STATES.JOINING});
+
+        store.windowUnload();
+
+        sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
+        sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
+          "fakeToken", "1627384950");
+      });
+
     it("should set the state to CLOSING", function() {
       store.windowUnload();
 
       expect(store._storeState.roomState).eql(ROOM_STATES.CLOSING);
     });
   });
 
   describe("#leaveRoom", function() {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2464,29 +2464,29 @@ let DefaultBrowserCheck = {
       this._notification = notificationBox.appendNotification(promptMessage, "default-browser",
                                                               iconURL, priority, buttons,
                                                               callback);
     } else {
       // Modal prompt
       let promptTitle = shellBundle.getString("setDefaultBrowserTitle");
       let promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
                                                          [brandShortName]);
-      let dontAskLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
-                                                        [brandShortName]);
+      let askLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
+                                                    [brandShortName]);
 
       let ps = Services.prompt;
-      let dontAsk = { value: false };
+      let shouldAsk = { value: true };
       let buttonFlags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
                         (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1) +
                         ps.BUTTON_POS_0_DEFAULT;
       let rv = ps.confirmEx(win, promptTitle, promptMessage, buttonFlags,
-                            yesButton, notNowButton, null, dontAskLabel, dontAsk);
+                            yesButton, notNowButton, null, askLabel, shouldAsk);
       if (rv == 0) {
         this.setAsDefault();
-      } else if (dontAsk.value) {
+      } else if (!shouldAsk.value) {
         ShellService.shouldCheckDefaultBrowser = false;
       }
     }
   },
 
   _onNotificationEvent: function(eventType) {
     if (eventType == "removed") {
       let doc = this._notification.ownerDocument;
@@ -2497,17 +2497,17 @@ let DefaultBrowserCheck = {
     }
   },
 };
 
 #ifdef E10S_TESTING_ONLY
 let E10SUINotification = {
   // Increase this number each time we want to roll out an
   // e10s testing period to Nightly users.
-  CURRENT_NOTICE_COUNT: 3,
+  CURRENT_NOTICE_COUNT: 4,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
     let skipE10sChecks = false;
     try {
       skipE10sChecks = (UpdateChannel.get() != "nightly") ||
                        Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
deleted file mode 100644
--- a/browser/components/places/BrowserPlaces.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-component {6bcb9bde-9018-4443-a071-c32653469597} PlacesProtocolHandler.js
-contract @mozilla.org/network/protocol;1?name=place {6bcb9bde-9018-4443-a071-c32653469597}
deleted file mode 100644
--- a/browser/components/places/PlacesProtocolHandler.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * vim: sw=2 ts=2 sts=2 et
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Components.utils.import("resource://gre/modules/NetUtil.jsm");
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const SCHEME = "place";
-const URL = "chrome://browser/content/places/content-ui/controller.xhtml";
-
-function PlacesProtocolHandler() {}
-
-PlacesProtocolHandler.prototype = {
-  scheme: SCHEME,
-  defaultPort: -1,
-  protocolFlags: Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
-                 Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE |
-                 Ci.nsIProtocolHandler.URI_NORELATIVE |
-                 Ci.nsIProtocolHandler.URI_NOAUTH,
-
-  newURI: function PPH_newURI(aSpec, aOriginCharset, aBaseUri) {
-    let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
-    uri.spec = aSpec;
-    return uri;
-  },
-
-  newChannel: function PPH_newChannel(aUri) {
-    let chan = NetUtil.newChannel(URL);
-    chan.originalURI = aUri;
-    return chan;
-  },
-
-  allowPort: function PPH_allowPort(aPort, aScheme) {
-    return false;
-  },
-
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIProtocolHandler
-  ]),
-
-  classID: Components.ID("{6bcb9bde-9018-4443-a071-c32653469597}")
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PlacesProtocolHandler]);
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -872,26 +872,31 @@ var gEditItemOverlay = {
                                   this._folderTree.columns.getFirstColumn());
   },
 
   // nsIDOMEventListener
   handleEvent: function EIO_nsIDOMEventListener(aEvent) {
     switch (aEvent.type) {
     case "CheckboxStateChange":
       // Update the tags field when items are checked/unchecked in the listbox
-      var tags = this._getTagsArrayFromTagField();
+      let tags = this._getTagsArrayFromTagField();
+      let tagCheckbox = aEvent.target;
+
+      let curTagIndex = tags.indexOf(tagCheckbox.label);
 
-      if (aEvent.target.checked) {
-        if (tags.indexOf(aEvent.target.label) == -1)
-          tags.push(aEvent.target.label);
+      let tagsSelector = this._element("tagsSelector");
+      tagsSelector.selectedItem = tagCheckbox;
+
+      if (tagCheckbox.checked) {
+        if (curTagIndex == -1)
+          tags.push(tagCheckbox.label);
       }
       else {
-        var indexOfItem = tags.indexOf(aEvent.target.label);
-        if (indexOfItem != -1)
-          tags.splice(indexOfItem, 1);
+        if (curTagIndex != -1)
+          tags.splice(curTagIndex, 1);
       }
       this._element("tagsField").value = tags.join(", ");
       this._updateTags();
       break;
     case "blur":
       let replaceFn = (str, firstLetter) => firstLetter.toUpperCase();
       let nodeName = aEvent.target.id.replace(/editBMPanel_(\w)/, replaceFn);
       this["on" + nodeName + "Blur"]();
--- a/browser/components/places/moz.build
+++ b/browser/components/places/moz.build
@@ -5,16 +5,11 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 
 JAR_MANIFESTS += ['jar.mn']
 
-EXTRA_COMPONENTS += [
-    'BrowserPlaces.manifest',
-    'PlacesProtocolHandler.js',
-]
-
 EXTRA_PP_JS_MODULES += [
     'PlacesUIUtils.jsm',
 ]
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -19,16 +19,22 @@ var gSearchPane = {
         document.location.hash = "";
       return;
     }
 
     gEngineView = new EngineView(new EngineStore());
     document.getElementById("engineList").view = gEngineView;
     this.buildDefaultEngineDropDown();
 
+    window.addEventListener("click", this, false);
+    window.addEventListener("command", this, false);
+    window.addEventListener("dragstart", this, false);
+    window.addEventListener("keypress", this, false);
+    window.addEventListener("select", this, false);
+
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
     window.addEventListener("unload", () => {
       Services.obs.removeObserver(this, "browser-search-engine-modified", false);
     });
   },
 
   buildDefaultEngineDropDown: function() {
     // This is called each time something affects the list of engines.
@@ -58,16 +64,59 @@ var gSearchPane = {
         item.setAttribute("image", uri);
       }
       item.engine = e;
       if (e.name == currentEngine)
         list.selectedItem = item;
     });
   },
 
+  handleEvent: function(aEvent) {
+    switch (aEvent.type) {
+      case "click":
+        if (aEvent.target.id == "addEngines" && aEvent.button == 0) {
+          Services.wm.getMostRecentWindow('navigator:browser')
+                     .BrowserSearch.loadAddEngines();
+        }
+        break;
+      case "command":
+        switch (aEvent.target.id) {
+          case "":
+            if (aEvent.target.parentNode &&
+                aEvent.target.parentNode.parentNode &&
+                aEvent.target.parentNode.parentNode.id == "defaultEngine") {
+              gSearchPane.setDefaultEngine();
+            }
+            break;
+          case "restoreDefaultSearchEngines":
+            gSearchPane.onRestoreDefaults();
+            break;
+          case "removeEngineButton":
+            gSearchPane.remove();
+            break;
+        }
+        break;
+      case "dragstart":
+        if (aEvent.target.id == "engineChildren") {
+          onDragEngineStart(aEvent);
+        }
+        break;
+      case "keypress":
+        if (aEvent.target.id == "engineList") {
+          gSearchPane.onTreeKeyPress(aEvent);
+        }
+        break;
+      case "select":
+        if (aEvent.target.id == "engineList") {
+          gSearchPane.onTreeSelect();
+        }
+        break;
+    }
+  },
+
   observe: function(aEngine, aTopic, aVerb) {
     if (aTopic == "browser-search-engine-modified") {
       aEngine.QueryInterface(Components.interfaces.nsISearchEngine);
       switch (aVerb) {
       case "engine-added":
         gEngineView._engineStore.addEngine(aEngine);
         gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
         gSearchPane.buildDefaultEngineDropDown();
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -23,53 +23,50 @@
           data-category="paneSearch">
       <label class="header-name">&paneSearch.title;</label>
     </hbox>
 
     <!-- Default Search Engine -->
     <groupbox id="defaultEngineGroup" align="start" data-category="paneSearch">
       <caption label="&defaultSearchEngine.label;"/>
       <label>&chooseYourDefaultSearchEngine.label;</label>
-      <menulist id="defaultEngine" oncommand="gSearchPane.setDefaultEngine();">
+      <menulist id="defaultEngine">
         <menupopup/>
       </menulist>
       <checkbox id="suggestionsInSearchFieldsCheckbox"
                 label="&provideSearchSuggestions.label;"
                 accesskey="&provideSearchSuggestions.accesskey;"
                 preference="browser.search.suggest.enabled"/>
     </groupbox>
 
     <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
       <caption label="&oneClickSearchEngines.label;"/>
       <label>&chooseWhichOneToDisplay.label;</label>
 
       <tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
-            seltype="single" onselect="gSearchPane.onTreeSelect();"
-            onkeypress="gSearchPane.onTreeKeyPress(event);">
-        <treechildren id="engineChildren" flex="1"
-                      ondragstart="onDragEngineStart(event);"/>
+            seltype="single">
+        <treechildren id="engineChildren" flex="1"/>
         <treecols>
-          <treecol id="engineShown" type="checkbox" style="min-width: 26px;" editable="true"/>
+          <treecol id="engineShown" type="checkbox" editable="true"/>
           <treecol id="engineName" flex="4" label="&engineNameColumn.label;"/>
           <treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"/>
         </treecols>
       </tree>
 
       <hbox>
         <button id="restoreDefaultSearchEngines"
                 label="&restoreDefaultSearchEngines.label;"
                 accesskey="&restoreDefaultSearchEngines.accesskey;"
-                oncommand="gSearchPane.onRestoreDefaults();"/>
+                />
         <spacer flex="1"/>
         <button id="removeEngineButton"
                 label="&removeEngine.label;"
                 accesskey="&removeEngine.accesskey;"
                 disabled="true"
-                oncommand="gSearchPane.remove();"/>
+                />
       </hbox>
 
       <separator class="thin"/>
 
-      <hbox pack="start" style="margin-bottom: 1em">
-        <label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"
-               onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
+      <hbox id="addEnginesBox" pack="start">
+        <label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"/>
       </hbox>
     </groupbox>
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -5,21 +5,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Used to detect minification for automatic pretty printing
 const SAMPLE_SIZE = 50; // no of lines
 const INDENT_COUNT_THRESHOLD = 5; // percentage
 const CHARACTER_LIMIT = 250; // line character limit
 
-// Maps known URLs to friendly source group names
+// Maps known URLs to friendly source group names and put them at the
+// bottom of source list.
 const KNOWN_SOURCE_GROUPS = {
   "Add-on SDK": "resource://gre/modules/commonjs/",
 };
 
+KNOWN_SOURCE_GROUPS[L10N.getStr("evalGroupLabel")] = "eval";
+
 /**
  * Functions handling the sources UI.
  */
 function SourcesView() {
   dumpn("SourcesView was instantiated");
 
   this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
   this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
@@ -165,24 +168,32 @@ SourcesView.prototype = Heritage.extend(
       }
     });
   },
 
   _parseUrl: function(aSource) {
     let fullUrl = aSource.url || aSource.introductionUrl;
     let url = fullUrl.split(" -> ").pop();
     let label = aSource.addonPath ? aSource.addonPath : SourceUtils.getSourceLabel(url);
+    let group;
 
     if (!aSource.url && aSource.introductionUrl) {
-      label += ' > eval';
+      label += ' > ' + aSource.introductionType;
+      group = L10N.getStr("evalGroupLabel");
+    }
+    else if(aSource.addonID) {
+      group = aSource.addonID;
+    }
+    else {
+      group = SourceUtils.getSourceGroup(url);
     }
 
     return {
       label: label,
-      group: aSource.addonID ? aSource.addonID : SourceUtils.getSourceGroup(url),
+      group: group,
       unicodeUrl: NetworkHelper.convertToUnicode(unescape(fullUrl))
     };
   },
 
   /**
    * Adds a breakpoint to this sources container.
    *
    * @param object aBreakpointClient
--- a/browser/devtools/debugger/test/browser_dbg_sources-eval-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_sources-eval-01.js
@@ -23,12 +23,17 @@ function test() {
       is(gSources.values.length, 1, "Should have 1 source");
 
       let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
       callInTab(gTab, "evalSource");
       yield newSource;
 
       is(gSources.values.length, 2, "Should have 2 sources");
 
+      let item = gSources.getItemForAttachment(e => e.label.indexOf("> eval") !== -1);
+      ok(item, "Source label is incorrect.");
+      is(item.attachment.group, gDebugger.L10N.getStr('evalGroupLabel'),
+         'Source group is incorrect');
+
       yield closeDebuggerAndFinish(gPanel);
     });
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_sources-eval-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_sources-eval-02.js
@@ -27,17 +27,18 @@ function test() {
       let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
       callInTab(gTab, "evalSourceWithSourceURL");
       yield newSource;
 
       is(gSources.values.length, 2, "Should have 2 sources");
 
       let item = gSources.getItemForAttachment(e => e.label == "bar.js");
       ok(item, "Source label is incorrect.");
-      ok(item.attachment.group === 'http://example.com', 'Source group is incorrect');
+      is(item.attachment.group, 'http://example.com',
+         'Source group is incorrect');
 
       let shown = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
       gSources.selectedItem = item;
       yield shown;
 
       ok(gEditor.getText().indexOf('bar = function() {') === 0,
          'Correct source is shown');
 
--- a/browser/devtools/framework/toolbox-process-window.js
+++ b/browser/devtools/framework/toolbox-process-window.js
@@ -46,16 +46,17 @@ let connect = Task.async(function*() {
   });
 });
 
 // Certain options should be toggled since we can assume chrome debugging here
 function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
   Services.prefs.setBoolPref("devtools.profiler.ui.show-platform-data", true);
   Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", false);
+  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
 }
 
 window.addEventListener("load", function() {
   let cmdClose = document.getElementById("toolbox-cmd-close");
   cmdClose.addEventListener("command", onCloseCommand);
   setPrefDefaults();
   connect().catch(Cu.reportError);
 });
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1498,17 +1498,19 @@ Toolbox.prototype = {
   /**
    * Initialize the inspector/walker/selection/highlighter fronts.
    * Returns a promise that resolves when the fronts are initialized
    */
   initInspector: function() {
     if (!this._initInspector) {
       this._initInspector = Task.spawn(function*() {
         this._inspector = InspectorFront(this._target.client, this._target.form);
-        this._walker = yield this._inspector.getWalker();
+        this._walker = yield this._inspector.getWalker(
+          {showAllAnonymousContent: Services.prefs.getBoolPref("devtools.inspector.showAllAnonymousContent")}
+        );
         this._selection = new Selection(this._walker);
 
         if (this.highlighterUtils.isRemoteHighlightable()) {
           this.walker.on("highlighter-ready", this._highlighterReady);
           this.walker.on("highlighter-hide", this._highlighterHidden);
 
           let autohide = !gDevTools.testing;
           this._highlighter = yield this._inspector.getHighlighter(autohide);
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -29,16 +29,17 @@ support-files =
   lib_jquery_1.7_min.js
   lib_jquery_1.11.1_min.js
   lib_jquery_2.1.1_min.js
 
 [browser_markupview_anonymous_01.js]
 [browser_markupview_anonymous_02.js]
 skip-if = e10s # scratchpad.xul is not loading in e10s window
 [browser_markupview_anonymous_03.js]
+[browser_markupview_anonymous_04.js]
 [browser_markupview_copy_image_data.js]
 [browser_markupview_css_completion_style_attribute.js]
 [browser_markupview_events.js]
 skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
 [browser_markupview_events-overflow.js]
 skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
 [browser_markupview_events_jquery_1.0.js]
 skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
--- a/browser/devtools/markupview/test/browser_markupview_anonymous_01.js
+++ b/browser/devtools/markupview/test/browser_markupview_anonymous_01.js
@@ -22,9 +22,23 @@ add_task(function*() {
 
   info ("Checking the normal child element");
   let span = children.nodes[1];
   yield isEditingMenuEnabled(span, inspector);
 
   info ("Checking the ::after pseudo element");
   let after = children.nodes[2];
   yield isEditingMenuDisabled(after, inspector);
+
+  let native = yield getNodeFront("#native", inspector);
+
+  // Markup looks like: <div><video controls /></div>
+  let nativeChildren = yield inspector.walker.children(native);
+  is (nativeChildren.nodes.length, 1, "Children returned from walker");
+
+  info ("Checking the video element");
+  let video = nativeChildren.nodes[0];
+  ok (!video.isAnonymous, "<video> is not anonymous");
+
+  let videoChildren = yield inspector.walker.children(video);
+  is (videoChildren.nodes.length, 0,
+    "No native children returned from walker for <video> by default");
 });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_anonymous_04.js
@@ -0,0 +1,36 @@
+/* vim: set 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 native anonymous content in the markupview with devtools.inspector.showAllAnonymousContent
+// set to true
+const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
+
+add_task(function*() {
+  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
+
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  let native = yield getNodeFront("#native", inspector);
+
+  // Markup looks like: <div><video controls /></div>
+  let nativeChildren = yield inspector.walker.children(native);
+  is (nativeChildren.nodes.length, 1, "Children returned from walker");
+
+  info ("Checking the video element");
+  let video = nativeChildren.nodes[0];
+  ok (!video.isAnonymous, "<video> is not anonymous");
+
+  let videoChildren = yield inspector.walker.children(video);
+  is (videoChildren.nodes.length, 3, "<video> has native anonymous children");
+
+  for (let node of videoChildren.nodes) {
+    ok (node.isAnonymous, "Child is anonymous");
+    ok (!node._form.isXBLAnonymous, "Child is not XBL anonymous");
+    ok (!node._form.isShadowAnonymous, "Child is not shadow anonymous");
+    ok (node._form.isNativeAnonymous, "Child is native anonymous");
+    yield isEditingMenuDisabled(node, inspector);
+  }
+});
--- a/browser/devtools/markupview/test/doc_markup_anonymous.html
+++ b/browser/devtools/markupview/test/doc_markup_anonymous.html
@@ -15,16 +15,18 @@
     }
   </style>
 </head>
 <body>
   <div id="pseudo"><span>middle</span></div>
 
   <div id="shadow">light dom</div>
 
+  <div id="native"><video controls></video></div>
+
   <script>
   var host = document.querySelector('#shadow');
   if (host.createShadowRoot) {
     var root = host.createShadowRoot();
     root.innerHTML = '<h3>Shadow DOM</h3><select multiple></select>';
   }
   </script>
 </body>
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -27,16 +27,17 @@ registerCleanupFunction(() => gDevTools.
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.markup.pagesize");
   Services.prefs.clearUserPref("dom.webcomponents.enabled");
+  Services.prefs.clearUserPref("devtools.inspector.showAllAnonymousContent");
 });
 
 // Auto close the toolbox and close the test tabs when the test ends
 registerCleanupFunction(function*() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   yield gDevTools.closeToolbox(target);
 
   while (gBrowser.tabs.length > 1) {
--- a/browser/devtools/performance/modules/io.js
+++ b/browser/devtools/performance/modules/io.js
@@ -13,17 +13,18 @@ loader.lazyImporter(this, "FileUtils",
 loader.lazyImporter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
 // This identifier string is used to tentatively ascertain whether or not
 // a JSON loaded from disk is actually something generated by this tool.
 // It isn't, of course, a definitive verification, but a Good Enough™
 // approximation before continuing the import. Don't localize this.
 const PERF_TOOL_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
-const PERF_TOOL_SERIALIZER_VERSION = 1;
+const PERF_TOOL_SERIALIZER_LEGACY_VERSION = 1;
+const PERF_TOOL_SERIALIZER_CURRENT_VERSION = 2;
 
 /**
  * Helpers for importing/exporting JSON.
  */
 let PerformanceIO = {
   /**
    * Gets a nsIScriptableUnicodeConverter instance with a default UTF-8 charset.
    * @return object
@@ -46,17 +47,17 @@ let PerformanceIO = {
    * @return object
    *         A promise that is resolved once streaming finishes, or rejected
    *         if there was an error.
    */
   saveRecordingToFile: function(recordingData, file) {
     let deferred = promise.defer();
 
     recordingData.fileType = PERF_TOOL_SERIALIZER_IDENTIFIER;
-    recordingData.version = PERF_TOOL_SERIALIZER_VERSION;
+    recordingData.version = PERF_TOOL_SERIALIZER_CURRENT_VERSION;
 
     let string = JSON.stringify(recordingData);
     let inputStream = this.getUnicodeConverter().convertToInputStream(string);
     let outputStream = FileUtils.openSafeFileOutputStream(file);
 
     NetUtil.asyncCopy(inputStream, outputStream, deferred.resolve);
     return deferred.promise;
   },
@@ -83,20 +84,69 @@ let PerformanceIO = {
       } catch (e) {
         deferred.reject(new Error("Could not read recording data file."));
         return;
       }
       if (recordingData.fileType != PERF_TOOL_SERIALIZER_IDENTIFIER) {
         deferred.reject(new Error("Unrecognized recording data file."));
         return;
       }
-      if (recordingData.version != PERF_TOOL_SERIALIZER_VERSION) {
+      if (!isValidSerializerVersion(recordingData.version)) {
         deferred.reject(new Error("Unsupported recording data file version."));
         return;
       }
+      if (recordingData.version === PERF_TOOL_SERIALIZER_LEGACY_VERSION) {
+        recordingData = convertLegacyData(recordingData);
+      }
       deferred.resolve(recordingData);
     });
 
     return deferred.promise;
   }
 };
 
 exports.PerformanceIO = PerformanceIO;
+
+/**
+ * Returns a boolean indicating whether or not the passed in `version`
+ * is supported by this serializer.
+ *
+ * @param number version
+ * @return boolean
+ */
+function isValidSerializerVersion (version) {
+  return !!~[
+    PERF_TOOL_SERIALIZER_LEGACY_VERSION,
+    PERF_TOOL_SERIALIZER_CURRENT_VERSION
+  ].indexOf(version);
+}
+
+
+/**
+ * Takes recording data (with version `1`, from the original profiler tool), and
+ * massages the data to be line with the current performance tool's property names
+ * and values.
+ *
+ * @param object legacyData
+ * @return object
+ */
+function convertLegacyData (legacyData) {
+  let { profilerData, ticksData, recordingDuration } = legacyData;
+
+  // The `profilerData` stays, and the previously unrecorded fields
+  // just are empty arrays.
+  let data = {
+    markers: [],
+    frames: [],
+    memory: [],
+    ticks: ticksData,
+    profilerData: profilerData,
+    // Data from the original profiler won't contain `interval` fields,
+    // but a recording duration, as well as the current time, which can be used
+    // to infer the interval startTime and endTime.
+    interval: {
+      startTime: profilerData.currentTime - recordingDuration,
+      endTime: profilerData.currentTime
+    }
+  };
+
+  return data;
+}
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -34,8 +34,9 @@ support-files =
 [browser_perf-overview-selection-03.js]
 [browser_perf-shared-connection-02.js]
 [browser_perf-shared-connection-03.js]
 # [browser_perf-shared-connection-04.js] bug 1077464
 [browser_perf-ui-recording.js]
 [browser_perf_recordings-io-01.js]
 [browser_perf_recordings-io-02.js]
 [browser_perf_recordings-io-03.js]
+[browser_perf_recordings-io-04.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-04.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the performance tool can import profiler data from the
+ * original profiler tool.
+ */
+
+let test = Task.async(function*() {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, PerformanceController } = panel.panelWin;
+
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  // Get data from the current profiler
+  let data = PerformanceController.getAllData();
+
+  // Create a structure from the data that mimics the old profiler's data.
+  // Different name for `ticks`, different way of storing time,
+  // and no memory, markers data.
+  let oldProfilerData = {
+    recordingDuration: data.interval.endTime - data.interval.startTime,
+    ticksData: data.ticks,
+    profilerData: data.profilerData,
+    fileType: "Recorded Performance Data",
+    version: 1
+  };
+
+  // Save recording as an old profiler data.
+  let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+  yield asyncCopy(oldProfilerData, file);
+
+  // Import recording.
+
+  let rerendered = waitForWidgetsRendered(panel);
+  let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+  yield PerformanceController.importRecording("", file);
+
+  yield imported;
+  ok(true, "The original profiler data appears to have been successfully imported.");
+
+  yield rerendered;
+  ok(true, "The imported data was re-rendered.");
+
+  // Verify imported recording.
+
+  let importedData = PerformanceController.getAllData();
+
+  is(importedData.startTime, data.startTime,
+    "The imported legacy data was successfully converted for the current tool (1).");
+  is(importedData.endTime, data.endTime,
+    "The imported legacy data was successfully converted for the current tool (2).");
+  is(importedData.markers.toSource(), [].toSource(),
+    "The imported legacy data was successfully converted for the current tool (3).");
+  is(importedData.memory.toSource(), [].toSource(),
+    "The imported legacy data was successfully converted for the current tool (4).");
+  is(importedData.ticks.toSource(), data.ticks.toSource(),
+    "The imported legacy data was successfully converted for the current tool (5).");
+  is(importedData.profilerData.toSource(), data.profilerData.toSource(),
+    "The imported legacy data was successfully converted for the current tool (6).");
+
+  yield teardown(panel);
+  finish();
+});
+
+function getUnicodeConverter() {
+  let className = "@mozilla.org/intl/scriptableunicodeconverter";
+  let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  return converter;
+}
+
+function asyncCopy(data, file) {
+  let deferred = Promise.defer();
+
+  let string = JSON.stringify(data);
+  let inputStream = getUnicodeConverter().convertToInputStream(string);
+  let outputStream = FileUtils.openSafeFileOutputStream(file);
+
+  NetUtil.asyncCopy(inputStream, outputStream, status => {
+    if (!Components.isSuccessCode(status)) {
+      deferred.reject(new Error("Could not save data to file."));
+    }
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -141,16 +141,17 @@ skip-if = e10s # Bug 1042253 - webconsol
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests (expectUncaughtException)
 [browser_console.js]
 [browser_console_addonsdk_loader_exception.js]
 [browser_console_clear_on_reload.js]
 [browser_console_click_focus.js]
 [browser_console_consolejsm_output.js]
 [browser_console_dead_objects.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_console_copy_entire_message_context_menu.js]
 [browser_console_error_source_click.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_console_filters.js]
 [browser_console_iframe_messages.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_console_keyboard_accessibility.js]
 [browser_console_log_inspectable_object.js]
 [browser_console_native_getters.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_console_copy_entire_message_context_menu.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test copying of the entire console message when right-clicked
+// with no other text selected. See Bug 1100562.
+
+function test() {
+  let hud;
+  let outputNode;
+  let contextMenu;
+
+  const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
+  const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/"));
+
+  Task.spawn(runner).then(finishTest);
+
+  function* runner() {
+    const {tab} = yield loadTab(TEST_URI);
+    hud = yield openConsole(tab);
+    outputNode = hud.outputNode;
+    contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu");
+
+    registerCleanupFunction(() => {
+      hud = outputNode = contextMenu = null;
+    });
+
+    hud.jsterm.clearOutput();
+    content.console.log("bug 1100562");
+
+    let [results] = yield waitForMessages({
+      webconsole: hud,
+      messages: [{
+        text: "bug 1100562",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_LOG,
+      }]
+    });
+
+    outputNode.focus();
+    let message = [...results.matched][0];
+    message.scrollIntoView();
+
+    yield waitForContextMenu(contextMenu, message, copyFromPopup, testContextMenuCopy);
+
+    function copyFromPopup() {
+      let copyItem = contextMenu.querySelector("#cMenu_copy");
+      copyItem.doCommand();
+
+      let controller = top.document.commandDispatcher.getControllerForCommand("cmd_copy");
+      is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+    }
+
+    function testContextMenuCopy() {
+      waitForClipboard((str) => { return message.textContent.trim() == str.trim(); },
+        () => { goDoCommand("cmd_copy") },
+        () => {}, () => {}
+      );
+    }
+
+    yield closeConsole(tab);
+  }
+}
\ No newline at end of file
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -2837,16 +2837,19 @@ WebConsoleFrame.prototype = {
 
   /**
    * Copies the selected items to the system clipboard.
    *
    * @param object aOptions
    *        - linkOnly:
    *        An optional flag to copy only URL without timestamp and
    *        other meta-information. Default is false.
+   *        - contextmenu:
+   *        An optional flag to copy the last clicked item which brought
+   *        up the context menu if nothing is selected. Default is false.
    */
   copySelectedItems: function WCF_copySelectedItems(aOptions)
   {
     aOptions = aOptions || { linkOnly: false, contextmenu: false };
 
     // Gather up the selected items and concatenate their clipboard text.
     let strings = [];
 
@@ -2859,17 +2862,17 @@ WebConsoleFrame.prototype = {
       // Ensure the selected item hasn't been filtered by type or string.
       if (!item.classList.contains("filtered-by-type") &&
           !item.classList.contains("filtered-by-string")) {
         let timestampString = l10n.timestampString(item.timestamp);
         if (aOptions.linkOnly) {
           strings.push(item.url);
         }
         else {
-          strings.push("[" + timestampString + "] " + item.clipboardText);
+          strings.push(item.clipboardText);
         }
       }
     }
 
     clipboardHelper.copyString(strings.join("\n"), this.document);
   },
 
   /**
@@ -4778,16 +4781,24 @@ CommandController.prototype = {
     this.owner.openSelectedItemInTab();
   },
 
   copyURL: function CommandController_copyURL()
   {
     this.owner.copySelectedItems({ linkOnly: true, contextmenu: true });
   },
 
+  /**
+   * Copies the last clicked message.
+   */
+  copyLastClicked: function CommandController_copy()
+  {
+    this.owner.copySelectedItems({ linkOnly: false, contextmenu: true });
+  },
+
   supportsCommand: function CommandController_supportsCommand(aCommand)
   {
     if (!this.owner || !this.owner.output) {
       return false;
     }
     return this.isCommandEnabled(aCommand);
   },
 
@@ -4796,16 +4807,22 @@ CommandController.prototype = {
     switch (aCommand) {
       case "consoleCmd_openURL":
       case "consoleCmd_copyURL": {
         // Only enable URL-related actions if node is Net Activity.
         let selectedItem = this.owner.output.getSelectedMessages(1)[0] ||
                            this.owner._contextMenuHandler.lastClickedMessage;
         return selectedItem && "url" in selectedItem;
       }
+      case "cmd_copy": {
+        // Only copy if we right-clicked the console and there's no selected text.
+        // With text selected, we want to fall back onto the default copy behavior.
+        return this.owner._contextMenuHandler.lastClickedMessage &&
+              !this.owner.output.getSelectedMessages(1)[0];
+      }
       case "consoleCmd_clearOutput":
       case "cmd_selectAll":
       case "cmd_find":
         return true;
       case "cmd_fontSizeEnlarge":
       case "cmd_fontSizeReduce":
       case "cmd_fontSizeReset":
       case "cmd_close":
@@ -4821,16 +4838,19 @@ CommandController.prototype = {
         this.openURL();
         break;
       case "consoleCmd_copyURL":
         this.copyURL();
         break;
       case "consoleCmd_clearOutput":
         this.owner.jsterm.clearOutput(true);
         break;
+      case "cmd_copy":
+        this.copyLastClicked();
+        break;
       case "cmd_find":
         this.owner.filterBox.focus();
         break;
       case "cmd_selectAll":
         this.selectAll();
         break;
       case "cmd_fontSizeEnlarge":
         this.owner.changeFontSize("+");
--- a/browser/devtools/webide/content/addons.js
+++ b/browser/devtools/webide/content/addons.js
@@ -42,18 +42,17 @@ function BuildItem(addon, type) {
   function onAddonUpdate(event, arg) {
     switch (event) {
       case "update":
         progress.removeAttribute("value");
         li.setAttribute("status", addon.status);
         status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
         break;
       case "failure":
-        console.error(arg);
-        window.alert(arg);
+        window.parent.UI.reportError("error_operationFail", arg);
         break;
       case "progress":
         if (arg == -1) {
           progress.removeAttribute("value");
         } else {
           progress.value = arg;
         }
         break;
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -373,17 +373,16 @@
 @RESPATH@/browser/components/WebContentConverter.js
 @RESPATH@/browser/components/BrowserComponents.manifest
 @RESPATH@/browser/components/nsBrowserContentHandler.js
 @RESPATH@/browser/components/nsBrowserGlue.js
 @RESPATH@/browser/components/nsSetDefaultBrowser.manifest
 @RESPATH@/browser/components/nsSetDefaultBrowser.js
 @RESPATH@/browser/components/BrowserDownloads.manifest
 @RESPATH@/browser/components/DownloadsStartup.js
-@RESPATH@/browser/components/BrowserPlaces.manifest
 @RESPATH@/browser/components/devtools-clhandler.manifest
 @RESPATH@/browser/components/devtools-clhandler.js
 @RESPATH@/browser/components/webideCli.js
 @RESPATH@/browser/components/webideComponents.manifest
 @RESPATH@/browser/components/Experiments.manifest
 @RESPATH@/browser/components/ExperimentsService.js
 @RESPATH@/browser/components/translation.manifest
 @RESPATH@/components/Downloads.manifest
@@ -448,17 +447,16 @@
 @RESPATH@/components/toolkitplaces.manifest
 @RESPATH@/components/nsLivemarkService.js
 @RESPATH@/components/nsTaggingService.js
 @RESPATH@/components/nsPlacesAutoComplete.manifest
 @RESPATH@/components/nsPlacesAutoComplete.js
 @RESPATH@/components/UnifiedComplete.manifest
 @RESPATH@/components/UnifiedComplete.js
 @RESPATH@/components/nsPlacesExpiration.js
-@RESPATH@/browser/components/PlacesProtocolHandler.js
 @RESPATH@/components/PlacesCategoriesStarter.js
 @RESPATH@/components/ColorAnalyzer.js
 @RESPATH@/components/PageThumbsProtocol.js
 @RESPATH@/components/nsDefaultCLH.manifest
 @RESPATH@/components/nsDefaultCLH.js
 @RESPATH@/components/nsContentPrefService.manifest
 @RESPATH@/components/nsContentPrefService.js
 @RESPATH@/components/nsContentDispatchChooser.manifest
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -310,8 +310,10 @@ watchExpressionsSeparatorLabel=\ →
 functionSearchSeparatorLabel=←
 
 # LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears
 # as a description in the notification panel popup, when multiple debuggers are
 # open in separate tabs and the user tries to resume them in the wrong order.
 # The substitution parameter is the URL of the last paused window that must be
 # resumed first.
 resumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S
+
+evalGroupLabel=Evaluated Sources
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -266,22 +266,22 @@ legal_text_privacy = Privacy Notice
 
 ## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
 ## These 2 strings are displayed before and after a 'Telefonica'
 ## logo.
 powered_by_beforeLogo=Powered by
 powered_by_afterLogo=
 
 feedback_call_experience_heading2=How was your conversation?
-feedback_what_makes_you_sad=What makes you sad?
 feedback_thank_you_heading=Thank you for your feedback!
+feedback_category_list_heading=What made you sad?
 feedback_category_audio_quality=Audio quality
 feedback_category_video_quality=Video quality
 feedback_category_was_disconnected=Was disconnected
-feedback_category_confusing=Confusing
+feedback_category_confusing2=Confusing controls
 feedback_category_other2=Other
 feedback_custom_category_text_placeholder=What went wrong?
 feedback_submit_button=Submit
 feedback_back_button=Back
 ## LOCALIZATION NOTE (feedback_window_will_close_in2):
 ## Semicolon-separated list of plural forms. See:
 ## http://developer.mozilla.org/en/docs/Localization_and_Plurals
 ## In this item, don't translate the part between {{..}}
--- a/browser/locales/generic/profile/bookmarks.html.in
+++ b/browser/locales/generic/profile/bookmarks.html.in
@@ -14,14 +14,14 @@
 <DL><p>
     <DT><H3 PERSONAL_TOOLBAR_FOLDER="true" ID="rdf:#$FvPhC3">@bookmarks_toolbarfolder@</H3>
 <DD>@bookmarks_toolbarfolder_description@
     <DL><p>
         <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/central/" ID="rdf:#$GvPhC3">@getting_started@</A>
     </DL><p>
     <DT><H3 ID="rdf:#$ZvPhC3">@firefox_heading@</H3>
     <DL><p>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/help/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$22iCK1">@firefox_help@</A>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/customize/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$32iCK1">@firefox_customize@</A>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/contribute/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$42iCK1">@firefox_community@</A>
-        <DT><A HREF="https://www.mozilla.org/@AB_CD@/about/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$52iCK1">@firefox_about@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/help/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$22iCK1">@firefox_help@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/firefox/customize/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$32iCK1">@firefox_customize@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/contribute/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$42iCK1">@firefox_community@</A>
+        <DT><A HREF="https://www.mozilla.org/@AB_CD@/about/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=" ID="rdf:#$52iCK1">@firefox_about@</A>
     </DL><p>
 </DL><p>
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1773,17 +1773,17 @@ richlistitem[type~="action"][actiontype=
 #tabbrowser-tabs {
   /* override the global style to allow the selected tab to be above the nav-bar */
   z-index: auto;
 }
 
 #TabsToolbar {
   min-height: 0;
   padding: 0;
-  margin-bottom: -@tabToolbarNavbarOverlap@;
+  margin-bottom: calc(-1 * var(--tab-toolbar-navbar-overlap));
 }
 
 #TabsToolbar:not(:-moz-lwtheme) {
   -moz-appearance: menubar;
   color: -moz-menubartext;
 }
 
 #toolbar-menubar:not([autohide="true"]):not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
@@ -1848,17 +1848,17 @@ richlistitem[type~="action"][actiontype=
 .tabbrowser-arrowscrollbox > .scrollbutton-down > .toolbarbutton-icon {
   -moz-appearance: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
-  margin: 0 0 @tabToolbarNavbarOverlap@;
+  margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
@@ -1876,17 +1876,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   background-color: Highlight;
   transition: none;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
 }
 
 #alltabs-button {
   list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
 }
 
 #TabsToolbar[brighttext] > #alltabs-button,
 #TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -112,17 +112,17 @@
   height: 22px; /* The native titlebar on OS X is 22px tall. */
 }
 
 /**
  * For tabs in titlebar on OS X, we stretch the titlebar down so that the
  * tabstrip can overlap it.
  */
 #main-window[tabsintitlebar] > #titlebar {
-  min-height: calc(@tabMinHeight@ + var(--space-above-tabbar) - @tabToolbarNavbarOverlap@);
+  min-height: calc(var(--tab-min-height) + var(--space-above-tabbar) - var(--tab-toolbar-navbar-overlap));
 }
 
 /**
  * We also vertically center the window buttons.
  */
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
   margin-top: @windowButtonMarginTop@;
@@ -186,29 +186,29 @@ toolbarseparator {
 
 /* Draw the bottom border of the tabs toolbar when it's not using
    -moz-appearance: toolbar. */
 #main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
   border-top: 1px solid hsla(0,0%,0%,.3);
   background-clip: padding-box;
-  margin-top: -@tabToolbarNavbarOverlap@;
+  margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 /* Always draw a border on Yosemite to ensure the border is well-defined there
  * (the default border is too light). */
 @media (-moz-mac-yosemite-theme) {
   #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
     border-top: 1px solid hsla(0,0%,0%,.2);
     background-clip: padding-box;
-    margin-top: -@tabToolbarNavbarOverlap@;
+    margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
     /* Position the toolbar above the bottom of background tabs */
     position: relative;
     z-index: 1;
   }
 }
 
 #nav-bar-customization-target {
   padding: 4px;
@@ -3179,17 +3179,17 @@ toolbarbutton.chevron > .toolbarbutton-m
   #TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-image-region: rect(0, 13px, 20px, 0);
-  margin: 0 0 @tabToolbarNavbarOverlap@;
+  margin: 0 0 var(--tab-toolbar-navbar-overlap);
   padding: 0 4px;
   border: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up {
   -moz-border-end: 2px solid transparent;
 }
 
@@ -3305,17 +3305,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 #TabsToolbar .toolbarbutton-1:not([type="menu-button"]),
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   padding: 0 1px;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   padding-left: 4px;
   padding-right: 4px;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
@@ -4724,17 +4724,17 @@ window > chatbox {
   }
   #main-window[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
     background-image: url("chrome://browser/skin/privatebrowsing-mask-short@2x.png");
   }
 }
 
 @media (-moz-mac-lion-theme) {
   #TabsToolbar > .private-browsing-indicator {
-    transform: translateY(calc(0px - var(--space-above-tabbar)));
+    transform: translateY(calc(-1 * var(--space-above-tabbar)));
     /* We offset by 38px for mask graphic, plus 4px to account for the
      * margin-left, which sums to 42px.
      */
     margin-right: -42px;
   }
 
   #main-window[privatebrowsingmode=temporary] .titlebar-placeholder[type="fullscreen-button"],
   #main-window[privatebrowsingmode=temporary] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
--- a/browser/themes/shared/browser.inc
+++ b/browser/themes/shared/browser.inc
@@ -6,10 +6,8 @@
 
 %ifdef XP_MACOSX
 % Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen
 % and want it to behave like other toolbar buttons.
 %define primaryToolbarButtons @primaryToolbarButtons@, #restore-button
 %endif
 
 %define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
-%define tabToolbarNavbarOverlap 1px
-%define tabMinHeight 31px
--- a/browser/themes/shared/devtools/images/performance-icons.svg
+++ b/browser/themes/shared/devtools/images/performance-icons.svg
@@ -22,21 +22,21 @@
   <rect x="13px" y="7px" width="2px" height="9px" rx="1" ry="1"/>
 </g>
 <g id="details-waterfall">
   <rect x="0px" y="3px" width="9px" height="2.5px" rx="1" ry="1"/>
   <rect x="5px" y="7px" width="8px" height="2.5px" rx="1" ry="1"/>
   <rect x="7px" y="11px" width="9px" height="2.5px" rx="1" ry="1"/>
 </g>
 <g id="details-call-tree">
-  <rect x="0px" y="3px" width="16px" height="2px"/>
-  <rect x="3px" y="6px" width="7px" height="2px"/>
-  <rect x="6px" y="9px" width="6px" height="2px"/>
-  <rect x="9px" y="12px" width="5px" height="2px"/>
+  <rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
+  <rect x="3px" y="6px" width="7px" height="2px" rx="1" ry="1"/>
+  <rect x="6px" y="9px" width="6px" height="2px" rx="1" ry="1"/>
+  <rect x="9px" y="12px" width="5px" height="2px" rx="1" ry="1"/>
 </g>
 <g id="details-flamegraph">
-  <rect x="0px" y="3px" width="16px" height="2px"/>
-  <rect x="0px" y="6px" width="8px" height="2px"/>
-  <rect x="10px" y="6px" width="6px" height="2px"/>
-  <rect x="2px" y="9px" width="6px" height="2px"/>
-  <rect x="5px" y="12px" width="3px" height="2px"/>
+  <rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
+  <rect x="0px" y="6px" width="8px" height="2px" rx="1" ry="1"/>
+  <rect x="10px" y="6px" width="6px" height="2px" rx="1" ry="1"/>
+  <rect x="2px" y="9px" width="6px" height="2px" rx="1" ry="1"/>
+  <rect x="5px" y="12px" width="3px" height="2px" rx="1" ry="1"/>
 </g>
 </svg>
--- a/browser/themes/shared/incontentprefs/search.css
+++ b/browser/themes/shared/incontentprefs/search.css
@@ -38,8 +38,16 @@
 
 #engineList treechildren::-moz-tree-drop-feedback {
   background-color: Highlight;
   width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
                      we may have, overflow isn't visible. */
   height: 2px;
   -moz-margin-start: 0;
 }
+
+#engineShown {
+  min-width: 26px;
+}
+
+#addEnginesBox {
+  margin-bottom: 1em;
+}
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -1,31 +1,36 @@
 %if 0
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 
+:root {
+  --tab-toolbar-navbar-overlap: 1px;
+  --tab-min-height: 31px;
+}
+
 %define tabCurveWidth 30px
 %define tabCurveHalfWidth 15px
 
 /* image preloading hack */
 #tabbrowser-tabs::before {
   /* Because of bug 853415, we need to ordinal this to the first position: */
   -moz-box-ordinal-group: 0;
   content: '';
   display: block;
   background-image:
     url(chrome://browser/skin/tabbrowser/tab-background-end.png),
     url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
     url(chrome://browser/skin/tabbrowser/tab-background-start.png);
 }
 
 #tabbrowser-tabs {
-  min-height: @tabMinHeight@;
+  min-height: var(--tab-min-height);
 }
 
 .tabbrowser-tab,
 .tabs-newtab-button {
   -moz-appearance: none;
   background-color: transparent;
   border-radius: 0;
   border-width: 0;
@@ -113,17 +118,17 @@
 }
 
 /* Tab Overflow */
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
   background-image: url(chrome://browser/skin/tabbrowser/tab-overflow-indicator.png);
   background-size: 100% 100%;
   width: 14px;
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
   pointer-events: none;
   position: relative;
   z-index: 3; /* the selected tab's z-index + 1 */
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:-moz-locale-dir(ltr) {
   transform: scaleX(-1);
@@ -150,17 +155,17 @@
 }
 
 .tab-background-start[selected=true]::after,
 .tab-background-start[selected=true]::before,
 .tab-background-start,
 .tab-background-end,
 .tab-background-end[selected=true]::after,
 .tab-background-end[selected=true]::before {
-  min-height: @tabMinHeight@;
+  min-height: var(--tab-min-height);
   width: @tabCurveWidth@;
 }
 
 .tabbrowser-tab:not([selected=true]),
 .tabbrowser-tab:-moz-lwtheme {
   color: inherit;
 }
 
@@ -290,30 +295,30 @@
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
   height: 100%;
   position: absolute;
 }
 
 .tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
   background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, transparent 70%);
-  background-position: center bottom @tabToolbarNavbarOverlap@;
+  background-position: center bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 85% 100%;
 }
 
 /* Background tab separators (3px wide).
    Also show separators beside the selected tab when dragging it. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
 .tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
 #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
   -moz-margin-start: -1.5px;
   -moz-margin-end: -1.5px;
   background-image: url(chrome://browser/skin/tabbrowser/tab-separator.png);
-  background-position: left bottom @tabToolbarNavbarOverlap@;
+  background-position: left bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 3px 100%;
   content: "";
   display: -moz-box;
   width: 3px;
 }
 
 /* Handle a case where the last separator in a customized tab bar with a
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -882,17 +882,17 @@ toolbarbutton[sdk-button="true"][cui-are
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   border-style: none;
   padding: 0 3px;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: @tabToolbarNavbarOverlap@;
+  margin-bottom: var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
 #TabsToolbar .toolbarbutton-1[open],
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover {
   background-image: linear-gradient(transparent, rgba(255,255,255,.5)),
@@ -1781,17 +1781,17 @@ toolbarbutton[type="socialmark"] > .tool
   text-shadow: none;
 }
 
 /* Tabstrip */
 
 #TabsToolbar {
   min-height: 0;
   padding: 0;
-  margin-bottom: -@tabToolbarNavbarOverlap@; /* overlap the nav-bar's top border */
+  margin-bottom: calc(-1 * var(--tab-toolbar-navbar-overlap)); /* overlap the nav-bar's top border */
 }
 
 %ifndef WINDOWS_AERO
 @media (-moz-windows-default-theme) {
   #main-window[sizemode=normal] #TabsToolbar {
     padding-left: 2px;
     padding-right: 2px;
   }
@@ -1876,17 +1876,17 @@ toolbarbutton[type="socialmark"] > .tool
   border: none;
 }
 
 /* Tab scrollbox arrow, tabstrip new tab and all-tabs buttons */
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
-  margin: 0 0 @tabToolbarNavbarOverlap@;
+  margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2416,17 +2416,23 @@ nsFrameLoader::EnsureMessageManager()
 {
   NS_ENSURE_STATE(mOwnerContent);
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!mIsTopLevelContent && !OwnerIsBrowserOrAppFrame() && !mRemoteFrame) {
+  if (!mIsTopLevelContent &&
+      !OwnerIsBrowserOrAppFrame() &&
+      !mRemoteFrame &&
+      !(mOwnerContent->IsXUL() &&
+        mOwnerContent->AttrValueIs(kNameSpaceID_None,
+                                   nsGkAtoms::forcemessagemanager,
+                                   nsGkAtoms::_true, eCaseMatters))) {
     return NS_OK;
   }
 
   bool useRemoteProcess = ShouldUseRemoteProcess();
   if (mMessageManager) {
     if (useRemoteProcess && mRemoteBrowserShown) {
       mMessageManager->InitWithCallback(this);
     }
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2112,16 +2112,17 @@ GK_ATOM(DeleteTxnName, "Deleting")
 
 // IPC stuff
 GK_ATOM(Remote, "remote")
 GK_ATOM(RemoteId, "_remote_id")
 GK_ATOM(DisplayPort, "_displayport")
 GK_ATOM(DisplayPortMargins, "_displayportmargins")
 GK_ATOM(DisplayPortBase, "_displayportbase")
 GK_ATOM(AsyncScrollLayerCreationFailed, "_asyncscrolllayercreationfailed")
+GK_ATOM(forcemessagemanager, "forcemessagemanager")
 
 // Names for system metrics
 GK_ATOM(color_picker_available, "color-picker-available")
 GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward")
 GK_ATOM(scrollbar_start_forward, "scrollbar-start-forward")
 GK_ATOM(scrollbar_end_backward, "scrollbar-end-backward")
 GK_ATOM(scrollbar_end_forward, "scrollbar-end-forward")
 GK_ATOM(scrollbar_thumb_proportional, "scrollbar-thumb-proportional")
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -1253,17 +1253,17 @@ Geolocation::Update(nsIDOMGeoPosition *a
   }
 
   if (aSomewhere) {
     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
     aSomewhere->GetCoords(getter_AddRefs(coords));
     if (coords) {
       double accuracy = -1;
       coords->GetAccuracy(&accuracy);
-      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY, accuracy);
+      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY_EXPONENTIAL, accuracy);
     }
   }
 
   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
     mPendingCallbacks[i-1]->Update(aSomewhere);
     RemoveRequest(mPendingCallbacks[i-1]);
   }
 
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -275,22 +275,22 @@ protected:
 };
 
 class AutoLockCompositableHost MOZ_FINAL
 {
 public:
   explicit AutoLockCompositableHost(CompositableHost* aHost)
     : mHost(aHost)
   {
-    mSucceeded = mHost->Lock();
+    mSucceeded = (mHost && mHost->Lock());
   }
 
   ~AutoLockCompositableHost()
   {
-    if (mSucceeded) {
+    if (mSucceeded && mHost) {
       mHost->Unlock();
     }
   }
 
   bool Failed() const { return !mSucceeded; }
 
 private:
   RefPtr<CompositableHost> mHost;
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -245,28 +245,33 @@ ImageHost::GetAsSurface()
 {
   return mFrontBuffer->GetAsSurface();
 }
 
 bool
 ImageHost::Lock()
 {
   MOZ_ASSERT(!mLocked);
+  if (!mFrontBuffer) {
+    return false;
+  }
   if (!mFrontBuffer->Lock()) {
     return false;
   }
   mLocked = true;
   return true;
 }
 
 void
 ImageHost::Unlock()
 {
   MOZ_ASSERT(mLocked);
-  mFrontBuffer->Unlock();
+  if (mFrontBuffer) {
+    mFrontBuffer->Unlock();
+  }
   mLocked = false;
 }
 
 IntSize
 ImageHost::GetImageSize() const
 {
   if (mHasPictureRect) {
     return IntSize(mPictureRect.width, mPictureRect.height);
--- a/toolkit/components/jsdownloads/src/DownloadCore.jsm
+++ b/toolkit/components/jsdownloads/src/DownloadCore.jsm
@@ -225,26 +225,32 @@ this.Download.prototype = {
    */
   progress: 0,
 
   /**
    * When hasProgress is true, indicates the total number of bytes to be
    * transferred before the download finishes, that can be zero for empty files.
    *
    * When hasProgress is false, this property is always zero.
+   *
+   * @note This property may be different than the final file size on disk for
+   *       downloads that are encoded during the network transfer.  You can use
+   *       the "size" property of the DownloadTarget object to get the actual
+   *       size on disk once the download succeeds.
    */
   totalBytes: 0,
 
   /**
    * Number of bytes currently transferred.  This value starts at zero, and may
    * be updated regardless of the value of hasProgress.
    *
    * @note You shouldn't rely on this property being equal to totalBytes to
    *       determine whether the download is completed.  You should use the
-   *       individual state properties instead.
+   *       individual state properties instead.  This property may not be
+   *       updated during the last part of the download.
    */
   currentBytes: 0,
 
   /**
    * Fractional number representing the speed of the download, in bytes per
    * second.  This value is zero when the download is stopped, and may be
    * updated regardless of the value of hasProgress.
    */
@@ -462,24 +468,34 @@ this.Download.prototype = {
           throw undefined;
         }
 
         // Execute the actual download through the saver object.
         this._saverExecuting = true;
         yield this.saver.execute(DS_setProgressBytes.bind(this),
                                  DS_setProperties.bind(this));
 
-        // Check for the last time if the download has been canceled.
+        // Now that the actual saving finished, read the actual file size on
+        // disk, that may be different from the amount of data transferred.
+        yield this.target.refresh();
+
+        // Check for the last time if the download has been canceled. This must
+        // be done right before setting the "stopped" property of the download,
+        // without any asynchronous operations in the middle, so that another
+        // cancellation request cannot start in the meantime and stay unhandled.
         if (this._promiseCanceled) {
           try {
             yield OS.File.remove(this.target.path);
           } catch (ex) {
             Cu.reportError(ex);
           }
 
+          this.target.exists = false;
+          this.target.size = 0;
+
           // Cancellation exceptions will be changed in the catch block below.
           throw new DownloadError();
         }
 
         // Update the status properties for a successful download.
         this.progress = 100;
         this.succeeded = true;
         this.hasPartialData = false;
@@ -605,16 +621,17 @@ this.Download.prototype = {
     if (!this.hasBlockedData) {
       return Promise.reject(new Error(
         "unblock may only be called on Downloads with blocked data."));
     }
 
     this._promiseUnblock = Task.spawn(function* () {
       try {
         yield OS.File.move(this.target.partFilePath, this.target.path);
+        yield this.target.refresh();
       } catch (ex) {
         yield this.refresh();
         this._promiseUnblock = null;
         throw ex;
       }
 
       this.succeeded = true;
       this.hasBlockedData = false;
@@ -891,16 +908,26 @@ this.Download.prototype = {
    */
   refresh: function ()
   {
     return Task.spawn(function () {
       if (!this.stopped || this._finalized) {
         return;
       }
 
+      if (this.succeeded) {
+        let oldExists = this.target.exists;
+        let oldSize = this.target.size;
+        yield this.target.refresh();
+        if (oldExists != this.target.exists || oldSize != this.target.size) {
+          this._notifyChange();
+        }
+        return;
+      }
+
       // Update the current progress from disk if we retained partial data.
       if ((this.hasPartialData || this.hasBlockedData) &&
           this.target.partFilePath) {
 
         try {
           let stat = yield OS.File.stat(this.target.partFilePath);
 
           // Ignore the result if the state has changed meanwhile.
@@ -1320,16 +1347,68 @@ this.DownloadTarget.prototype = {
   /**
    * String containing the path of the ".part" file containing the data
    * downloaded so far, or null to disable the use of a ".part" file to keep
    * partially downloaded data.
    */
   partFilePath: null,
 
   /**
+   * Indicates whether the target file exists.
+   *
+   * This is a dynamic property updated when the download finishes or when the
+   * "refresh" method of the Download object is called. It can be used by the
+   * front-end to reduce I/O compared to checking the target file directly.
+   */
+  exists: false,
+
+  /**
+   * Size in bytes of the target file, or zero if the download has not finished.
+   *
+   * Even if the target file does not exist anymore, this property may still
+   * have a value taken from the download metadata. If the metadata has never
+   * been available in this session and the size cannot be obtained from the
+   * file because it has already been deleted, this property will be zero.
+   *
+   * For single-file downloads, this property will always match the actual file
+   * size on disk, while the totalBytes property of the Download object, when
+   * available, may represent the size of the encoded data instead.
+   *
+   * For downloads involving multiple files, like complete web pages saved to
+   * disk, the meaning of this value is undefined. It currently matches the size
+   * of the main file only rather than the sum of all the written data.
+   *
+   * This is a dynamic property updated when the download finishes or when the
+   * "refresh" method of the Download object is called. It can be used by the
+   * front-end to reduce I/O compared to checking the target file directly.
+   */
+  size: 0,
+
+  /**
+   * Sets the "exists" and "size" properties based on the actual file on disk.
+   *
+   * @return {Promise}
+   * @resolves When the operation has finished successfully.
+   * @rejects JavaScript exception.
+   */
+  refresh: Task.async(function* () {
+    try {
+      this.size = (yield OS.File.stat(this.path)).size;
+      this.exists = true;
+    } catch (ex) {
+      // Report any error not caused by the file not being there. In any case,
+      // the size of the download is not updated and the known value is kept.
+      if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
+        Cu.reportError(ex);
+      }
+      this.exists = false;
+    }
+  }),
+
+  /**
    * Returns a static representation of the current object state.
    *
    * @return A JavaScript object that can be serialized to JSON.
    */
   toSerializable: function ()
   {
     // Simplify the representation if we don't have other details.
     if (!this.partFilePath && !this._unknownProperties) {
--- a/toolkit/components/jsdownloads/test/unit/common_test_Download.js
+++ b/toolkit/components/jsdownloads/test/unit/common_test_Download.js
@@ -102,16 +102,36 @@ function promisePartFileReady(aDownload)
       // The specific error might vary with the platform.
       do_print("Expected exception while checking existence: " + ex.toString());
       // Wait some more time to allow the write to complete.
       yield promiseTimeout(100);
     }
   });
 }
 
+/**
+ * Checks that the actual data written to disk matches the expected data as well
+ * as the properties of the given DownloadTarget object.
+ *
+ * @param downloadTarget
+ *        The DownloadTarget object whose details have to be verified.
+ * @param expectedContents
+ *        String containing the octets that are expected in the file.
+ *
+ * @return {Promise}
+ * @resolves When the properties have been verified.
+ * @rejects JavaScript exception.
+ */
+let promiseVerifyTarget = Task.async(function* (downloadTarget,
+                                                expectedContents) {
+  yield promiseVerifyContents(downloadTarget.path, expectedContents);
+  do_check_true(downloadTarget.exists);
+  do_check_eq(downloadTarget.size, expectedContents.length);
+});
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Tests
 
 /**
  * Executes a download and checks its basic properties after construction.
  * The download is started by constructing the simplest Download object with
  * the "copy" saver, or using the legacy nsITransfer interface.
  */
@@ -143,33 +163,32 @@ add_task(function test_basic()
     do_check_eq(download.target.path, targetFile.path);
 
     yield promiseDownloadStopped(download);
   }
 
   // Check additional properties on the finished download.
   do_check_true(download.source.referrer === null);
 
-  yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
 });
 
 /**
  * Executes a download with the tryToKeepPartialData property set, and ensures
  * that the file is saved correctly.  When testing DownloadLegacySaver, the
  * download is executed using the nsIExternalHelperAppService component.
  */
 add_task(function test_basic_tryToKeepPartialData()
 {
   let download = yield promiseStartDownload_tryToKeepPartialData();
   continueResponses();
   yield promiseDownloadStopped(download);
 
   // The target file should now have been created, and the ".part" file deleted.
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
   do_check_eq(32, download.saver.getSha256Hash().length);
 });
 
 /**
  * Tests the permissions of the final target file once the download finished.
  */
 add_task(function test_unix_permissions()
@@ -282,31 +301,35 @@ add_task(function test_initial_final_sta
     download = yield promiseNewDownload();
 
     do_check_true(download.stopped);
     do_check_false(download.succeeded);
     do_check_false(download.canceled);
     do_check_true(download.error === null);
     do_check_eq(download.progress, 0);
     do_check_true(download.startTime === null);
+    do_check_false(download.target.exists);
+    do_check_eq(download.target.size, 0);
 
     yield download.start();
   } else {
     // When testing DownloadLegacySaver, the download is already started when it
     // is created, thus we cannot check its initial state.
     download = yield promiseStartLegacyDownload();
     yield promiseDownloadStopped(download);
   }
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
   do_check_eq(download.progress, 100);
   do_check_true(isValidDate(download.startTime));
+  do_check_true(download.target.exists);
+  do_check_eq(download.target.size, TEST_DATA_SHORT.length);
 });
 
 /**
  * Checks the notification of the final download state.
  */
 add_task(function test_final_state_notified()
 {
   mustInterruptResponses();
@@ -343,25 +366,28 @@ add_task(function test_intermediate_prog
   let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
 
   yield promiseDownloadMidway(download);
 
   do_check_true(download.hasProgress);
   do_check_eq(download.currentBytes, TEST_DATA_SHORT.length);
   do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
 
+  // The final file size should not be computed for in-progress downloads.
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
+
   // Continue after the first chunk of data is fully received.
   continueResponses();
   yield promiseDownloadStopped(download);
 
   do_check_true(download.stopped);
   do_check_eq(download.progress, 100);
 
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 });
 
 /**
  * Downloads a file with a "Content-Length" of 0 and checks the progress.
  */
 add_task(function test_empty_progress()
 {
   let download = yield promiseStartDownload(httpUrl("empty.txt"));
@@ -372,16 +398,18 @@ add_task(function test_empty_progress()
   do_check_eq(download.progress, 100);
   do_check_eq(download.currentBytes, 0);
   do_check_eq(download.totalBytes, 0);
 
   // We should have received the content type even for an empty file.
   do_check_eq(download.contentType, "text/plain");
 
   do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
+  do_check_true(download.target.exists);
+  do_check_eq(download.target.size, 0);
 });
 
 /**
  * Downloads a file with a "Content-Length" of 0 with the tryToKeepPartialData
  * property set, and ensures that the file is saved correctly.
  */
 add_task(function test_empty_progress_tryToKeepPartialData()
 {
@@ -401,16 +429,19 @@ add_task(function test_empty_progress_tr
     // to keep partially downloaded data by default.
     download = yield promiseStartExternalHelperAppServiceDownload(
                                                          httpUrl("empty.txt"));
   }
   yield promiseDownloadStopped(download);
 
   // The target file should now have been created, and the ".part" file deleted.
   do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
+  do_check_true(download.target.exists);
+  do_check_eq(download.target.size, 0);
+
   do_check_false(yield OS.File.exists(download.target.partFilePath));
   do_check_eq(32, download.saver.getSha256Hash().length);
 });
 
 /**
  * Downloads an empty file with no "Content-Length" and checks the progress.
  */
 add_task(function test_empty_noprogress()
@@ -476,16 +507,18 @@ add_task(function test_empty_noprogress(
   do_check_eq(download.contentType, "text/plain");
 
   // Verify the state of the completed download.
   do_check_true(download.stopped);
   do_check_false(download.hasProgress);
   do_check_eq(download.progress, 100);
   do_check_eq(download.currentBytes, 0);
   do_check_eq(download.totalBytes, 0);
+  do_check_true(download.target.exists);
+  do_check_eq(download.target.size, 0);
 
   do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
 });
 
 /**
  * Calls the "start" method two times before the download is finished.
  */
 add_task(function test_start_twice()
@@ -514,18 +547,17 @@ add_task(function test_start_twice()
   yield promiseAttempt1;
   yield promiseAttempt2;
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
 
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 });
 
 /**
  * Cancels a download and verifies that its state is reported correctly.
  */
 add_task(function test_cancel_midway()
 {
   mustInterruptResponses();
@@ -572,16 +604,18 @@ add_task(function test_cancel_midway()
   if (gUseLegacySaver) {
     // The nsIWebBrowserPersist instance should have been canceled now.
     do_check_eq(options.outPersist.result, Cr.NS_ERROR_ABORT);
   }
 
   do_check_true(download.stopped);
   do_check_true(download.canceled);
   do_check_true(download.error === null);
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 
   do_check_false(yield OS.File.exists(download.target.path));
 
   // Progress properties are not reset by canceling.
   do_check_eq(download.progress, 50);
   do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
   do_check_eq(download.currentBytes, TEST_DATA_SHORT.length);
 
@@ -689,34 +723,35 @@ add_task(function test_cancel_midway_res
 
   yield promiseAttempt;
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
 
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 });
 
 /**
  * Cancels a download and restarts it from where it stopped.
  */
 add_task(function test_cancel_midway_restart_tryToKeepPartialData()
 {
   let download = yield promiseStartDownload_tryToKeepPartialData();
   yield download.cancel();
 
   do_check_true(download.stopped);
   do_check_true(download.hasPartialData);
 
   // The target file should not exist, but we should have kept the partial data.
   do_check_false(yield OS.File.exists(download.target.path));
   yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 
   // Verify that the server sent the response from the start.
   do_check_eq(gMostRecentFirstBytePos, 0);
 
   // The second time, we'll request and obtain the second part of the response,
   // but we still stop when half of the remaining progress is reached.
   let deferMidway = Promise.defer();
   download.onchange = function () {
@@ -740,48 +775,50 @@ add_task(function test_cancel_midway_res
   // Now we allow the download to finish.
   continueResponses();
   yield promiseAttempt;
 
   // Check that the server now sent the second part only.
   do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length);
 
   // The target file should now have been created, and the ".part" file deleted.
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
 });
 
 /**
  * Cancels a download while keeping partially downloaded data, then removes the
  * data and restarts the download from the beginning.
  */
 add_task(function test_cancel_midway_restart_removePartialData()
 {
   let download = yield promiseStartDownload_tryToKeepPartialData();
   yield download.cancel();
 
   do_check_true(download.hasPartialData);
   yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 
   yield download.removePartialData();
 
   do_check_false(download.hasPartialData);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 
   // The second time, we'll request and obtain the entire response again.
   continueResponses();
   yield download.start();
 
   // Verify that the server sent the response from the start.
   do_check_eq(gMostRecentFirstBytePos, 0);
 
   // The target file should now have been created, and the ".part" file deleted.
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
 });
 
 /**
  * Cancels a download while keeping partially downloaded data, then removes the
  * data and restarts the download from the beginning without keeping the partial
  * data anymore.
  */
@@ -826,18 +863,17 @@ add_task(function test_cancel_midway_res
   // The third time, we'll request and obtain the entire response again.
   continueResponses();
   yield download.start();
 
   // Verify that the server sent the response from the start.
   do_check_eq(gMostRecentFirstBytePos, 0);
 
   // The target file should now have been created, and the ".part" file deleted.
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
 });
 
 /**
  * Cancels a download right after starting it, then restarts it immediately.
  */
 add_task(function test_cancel_immediately_restart_immediately()
 {
@@ -880,18 +916,17 @@ add_task(function test_cancel_immediatel
 
   yield promiseRestarted;
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
 
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 });
 
 /**
  * Cancels a download midway, then restarts it immediately.
  */
 add_task(function test_cancel_midway_restart_immediately()
 {
   mustInterruptResponses();
@@ -929,18 +964,17 @@ add_task(function test_cancel_midway_res
 
   yield promiseRestarted;
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
 
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 });
 
 /**
  * Calls the "cancel" method on a successful download.
  */
 add_task(function test_cancel_successful()
 {
   let download = yield promiseStartDownload();
@@ -949,17 +983,17 @@ add_task(function test_cancel_successful
   // The cancel method should succeed with no effect.
   yield download.cancel();
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
 
-  yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
 });
 
 /**
  * Calls the "cancel" method two times in a row.
  */
 add_task(function test_cancel_twice()
 {
   mustInterruptResponses();
@@ -989,16 +1023,41 @@ add_task(function test_cancel_twice()
   do_check_false(download.succeeded);
   do_check_true(download.canceled);
   do_check_true(download.error === null);
 
   do_check_false(yield OS.File.exists(download.target.path));
 });
 
 /**
+ * Checks the "refresh" method for succeeded downloads.
+ */
+add_task(function test_refresh_succeeded()
+{
+  let download = yield promiseStartDownload();
+  yield promiseDownloadStopped(download);
+
+  // The DownloadTarget properties should be the same after calling "refresh".
+  yield download.refresh();
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
+
+  // If the file is removed, only the "exists" property should change, and the
+  // "size" property should keep its previous value.
+  yield OS.File.move(download.target.path, download.target.path + ".old");
+  yield download.refresh();
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, TEST_DATA_SHORT.length);
+
+  // The DownloadTarget properties should be restored when the file is put back.
+  yield OS.File.move(download.target.path + ".old", download.target.path);
+  yield download.refresh();
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
+});
+
+/**
  * Checks that a download cannot be restarted after the "finalize" method.
  */
 add_task(function test_finalize()
 {
   mustInterruptResponses();
 
   let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
 
@@ -1075,18 +1134,17 @@ add_task(function test_whenSucceeded_aft
   // Wait for the download to finish by waiting on the whenSucceeded promise.
   yield promiseSucceeded;
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
 
-  yield promiseVerifyContents(download.target.path,
-                              TEST_DATA_SHORT + TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 });
 
 /**
  * Ensures download error details are reported on network failures.
  */
 add_task(function test_error_source()
 {
   let serverSocket = startFakeServer();
@@ -1119,16 +1177,18 @@ add_task(function test_error_source()
     // Check the properties now that the download stopped.
     do_check_true(download.stopped);
     do_check_false(download.canceled);
     do_check_true(download.error !== null);
     do_check_true(download.error.becauseSourceFailed);
     do_check_false(download.error.becauseTargetFailed);
 
     do_check_false(yield OS.File.exists(download.target.path));
+    do_check_false(download.target.exists);
+    do_check_eq(download.target.size, 0);
   } finally {
     serverSocket.close();
   }
 });
 
 /**
  * Ensures a download error is reported when receiving less bytes than what was
  * specified in the Content-Length header.
@@ -1172,16 +1232,18 @@ add_task(function test_error_source_part
   do_check_true(download.stopped);
   do_check_false(download.canceled);
   do_check_true(download.error !== null);
   do_check_true(download.error.becauseSourceFailed);
   do_check_false(download.error.becauseTargetFailed);
   do_check_eq(download.error.result, Cr.NS_ERROR_NET_PARTIAL_TRANSFER);
 
   do_check_false(yield OS.File.exists(download.target.path));
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 });
 
 /**
  * Ensures download error details are reported on local writing failures.
  */
 add_task(function test_error_target()
 {
   // Create a file without write access permissions before downloading.
@@ -1271,17 +1333,17 @@ add_task(function test_error_restart()
   yield download.start();
 
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.canceled);
   do_check_true(download.error === null);
   do_check_eq(download.progress, 100);
 
-  yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
 });
 
 /**
  * Executes download in both public and private modes.
  */
 add_task(function test_public_and_private()
 {
   let sourcePath = "/test_public_and_private.txt";
@@ -1382,17 +1444,17 @@ add_task(function test_with_content_enco
 
   let download = yield promiseStartDownload(sourceUrl);
   yield promiseDownloadStopped(download);
 
   do_check_eq(download.progress, 100);
   do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
 
   // Ensure the content matches the decoded test data.
-  yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
 
   cleanup();
 });
 
 /**
  * Checks that the file is not decoded if the extension matches the encoding.
  */
 add_task(function test_with_content_encoding_ignore_extension()
@@ -1416,20 +1478,21 @@ add_task(function test_with_content_enco
                        TEST_DATA_SHORT_GZIP_ENCODED.length);
   });
 
   let download = yield promiseStartDownload(sourceUrl);
   yield promiseDownloadStopped(download);
 
   do_check_eq(download.progress, 100);
   do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
+  do_check_eq(download.target.size, TEST_DATA_SHORT_GZIP_ENCODED.length);
 
   // Ensure the content matches the encoded test data.  We convert the data to a
   // string before executing the content check.
-  yield promiseVerifyContents(download.target.path,
+  yield promiseVerifyTarget(download.target,
         String.fromCharCode.apply(String, TEST_DATA_SHORT_GZIP_ENCODED));
 
   cleanup();
 });
 
 /**
  * Cancels and restarts a download sequentially with content-encoding.
  */
@@ -1460,17 +1523,17 @@ add_task(function test_cancel_midway_res
   // The second time, we'll provide the entire interruptible response.
   continueResponses();
   download.onchange = null;
   yield download.start();
 
   do_check_eq(download.progress, 100);
   do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
 
-  yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
 });
 
 /**
  * Download with parental controls enabled.
  */
 add_task(function test_blocked_parental_controls()
 {
   function cleanup() {
@@ -1542,17 +1605,16 @@ add_task(function test_getSha256Hash()
   if (!gUseLegacySaver) {
     let download = yield promiseStartDownload(httpUrl("source.txt"));
     yield promiseDownloadStopped(download);
     do_check_true(download.stopped);
     do_check_eq(32, download.saver.getSha256Hash().length);
   }
 });
 
-
 /**
  * Create a download which will be reputation blocked.
  *
  * @param options
  *        {
  *           keepPartialData: bool,
  *           keepBlockedData: bool,
  *        }
@@ -1608,16 +1670,18 @@ add_task(function test_blocked_applicati
 {
   let download = yield promiseBlockedDownload({
     keepPartialData: false,
     keepBlockedData: false,
   });
 
   // Now that the download is blocked, the target file should not exist.
   do_check_false(yield OS.File.exists(download.target.path));
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 
   // There should also be no blocked data in this case
   do_check_false(download.hasBlockedData);
 });
 
 /**
  * Checks that application reputation blocks the download but maintains the
  * blocked data, which will be deleted when the block is confirmed.
@@ -1636,16 +1700,18 @@ add_task(function test_blocked_applicati
 
   // After confirming the block the download should be in a failed state and
   // have no downloaded data left on disk.
   do_check_true(download.stopped);
   do_check_false(download.succeeded);
   do_check_false(download.hasBlockedData);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
   do_check_false(yield OS.File.exists(download.target.path));
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 });
 
 /**
  * Checks that application reputation blocks the download but maintains the
  * blocked data, which will be used to complete the download when unblocking.
  */
 add_task(function test_blocked_applicationReputation_unblock()
 {
@@ -1660,17 +1726,17 @@ add_task(function test_blocked_applicati
   yield download.unblock();
 
   // After unblocking the download should have succeeded and be
   // present at the final path.
   do_check_true(download.stopped);
   do_check_true(download.succeeded);
   do_check_false(download.hasBlockedData);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
-  do_check_true(yield OS.File.exists(download.target.path));
+  yield promiseVerifyTarget(download.target, TEST_DATA_SHORT + TEST_DATA_SHORT);
 
   // The only indication the download was previously blocked is the
   // existence of the error, so we make sure it's still set.
   do_check_true(download.error instanceof Downloads.Error);
   do_check_true(download.error.becauseBlocked);
   do_check_true(download.error.becauseBlockedByReputationCheck);
 });
 
@@ -1738,16 +1804,18 @@ add_task(function test_blocked_applicati
 
   // After confirming the block the download should be in a failed state and
   // have no downloaded data left on disk.
   do_check_true(download.stopped);
   do_check_false(download.succeeded);
   do_check_false(download.hasBlockedData);
   do_check_false(yield OS.File.exists(download.target.partFilePath));
   do_check_false(yield OS.File.exists(download.target.path));
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 });
 
 /**
  * Checks that unblocking a blocked download fails if the blocked data has been
  * removed.
  */
 add_task(function test_blocked_applicationReputation_unblock()
 {
@@ -1767,16 +1835,18 @@ add_task(function test_blocked_applicati
     do_throw("unblock should have failed.");
   }, () => {});
 
   // Even though unblocking failed the download state should have been updated
   // to reflect the lack of blocked data.
   do_check_false(download.hasBlockedData);
   do_check_true(download.stopped);
   do_check_false(download.succeeded);
+  do_check_false(download.target.exists);
+  do_check_eq(download.target.size, 0);
 });
 
 /**
  * download.showContainingDirectory() action
  */
 add_task(function test_showContainingDirectory() {
   DownloadIntegration._deferTestShowDir = Promise.defer();
 
@@ -2029,17 +2099,17 @@ add_task(function test_platform_integrat
     yield download.whenSucceeded().then(function () {
       do_check_true(DownloadIntegration.downloadDoneCalled);
       do_check_true(downloadWatcherNotified);
     });
 
     // Then, wait for the promise returned by "start" to be resolved.
     yield promiseDownloadStopped(download);
 
-    yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
+    yield promiseVerifyTarget(download.target, TEST_DATA_SHORT);
   }
 });
 
 /**
  * Checks that downloads are added to browsing history when they start.
  */
 add_task(function test_history()
 {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -308,21 +308,21 @@
   },
   "GC_SCC_SWEEP_MAX_PAUSE_MS": {
     "expires_in_version": "never",
     "kind": "linear",
     "high": "500",
     "n_buckets": 50,
     "description": "Time spent sweeping slowest compartment SCC (ms)"
   },
-  "GEOLOCATION_ACCURACY": {
+  "GEOLOCATION_ACCURACY_EXPONENTIAL": {
     "expires_in_version": "default",
-    "kind": "linear",
-    "high": "18000",
-    "n_buckets": 200,
+    "kind": "exponential",
+    "high": "100000",
+    "n_buckets": 50,
     "description": "Location accuracy"
   },
   "GEOLOCATION_ERROR": {
     "expires_in_version": "40",
     "kind": "flag",
     "description": "Has seen location error"
   },
   "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT": {
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -1141,16 +1141,17 @@ var WalkerActor = protocol.ActorClass({
   initialize: function(conn, tabActor, options) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this.rootWin = tabActor.window;
     this.rootDoc = this.rootWin.document;
     this._refMap = new Map();
     this._pendingMutations = [];
     this._activePseudoClassLocks = new Set();
+    this.showAllAnonymousContent = options.showAllAnonymousContent;
 
     this.layoutHelpers = new LayoutHelpers(this.rootWin);
 
     // Nodes which have been removed from the client's known
     // ownership tree are considered "orphaned", and stored in
     // this set.
     this._orphaned = new Set();
 
@@ -1182,16 +1183,22 @@ var WalkerActor = protocol.ActorClass({
       root: this.rootNode.form()
     }
   },
 
   toString: function() {
     return "[WalkerActor " + this.actorID + "]";
   },
 
+  getDocumentWalker: function(node, whatToShow) {
+    // Allow native anon content (like <video> controls) if preffed on
+    let nodeFilter = this.showAllAnonymousContent ? allAnonymousContentTreeWalkerFilter : standardTreeWalkerFilter;
+    return new DocumentWalker(node, this.rootWin, whatToShow, nodeFilter);
+  },
+
   destroy: function() {
     this._destroyed = true;
 
     this.clearPseudoClassLocks();
     this._activePseudoClassLocks = null;
 
     this._hoveredNode = null;
     this.rootDoc = null;
@@ -1371,17 +1378,17 @@ var WalkerActor = protocol.ActorClass({
    * @param NodeActor node
    *    The node whose parents are requested.
    * @param object options
    *    Named options, including:
    *    `sameDocument`: If true, parents will be restricted to the same
    *      document as the node.
    */
   parents: method(function(node, options={}) {
-    let walker = DocumentWalker(node.rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(node.rawNode);
     let parents = [];
     let cur;
     while((cur = walker.parentNode())) {
       if (options.sameDocument && cur.ownerDocument != node.rawNode.ownerDocument) {
         break;
       }
       parents.push(this._ref(cur));
     }
@@ -1392,17 +1399,17 @@ var WalkerActor = protocol.ActorClass({
       sameDocument: Option(1)
     },
     response: {
       nodes: RetVal("array:domnode")
     },
   }),
 
   parentNode: function(node) {
-    let walker = DocumentWalker(node.rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(node.rawNode);
     let parent = walker.parentNode();
     if (parent) {
       return this._ref(parent);
     }
     return null;
   },
 
   /**
@@ -1453,17 +1460,17 @@ var WalkerActor = protocol.ActorClass({
       return;
     }
 
     if (node.retained) {
       // Forcing a retained node to go away.
       this._retainedOrphans.delete(node);
     }
 
-    let walker = DocumentWalker(node.rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(node.rawNode);
 
     let child = walker.firstChild();
     while (child) {
       let childActor = this._refMap.get(child);
       if (childActor) {
         this.releaseNode(childActor, options);
       }
       child = walker.nextSibling();
@@ -1480,17 +1487,17 @@ var WalkerActor = protocol.ActorClass({
   /**
    * Add any nodes between `node` and the walker's root node that have not
    * yet been seen by the client.
    */
   ensurePathToRoot: function(node, newParents=new Set()) {
     if (!node) {
       return newParents;
     }
-    let walker = DocumentWalker(node.rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(node.rawNode);
     let cur;
     while ((cur = walker.parentNode())) {
       let parent = this._refMap.get(cur);
       if (!parent) {
         // This parent didn't exist, so hasn't been seen by the client yet.
         newParents.add(this._ref(cur));
       } else {
         // This parent did exist, so the client knows about it.
@@ -1531,17 +1538,17 @@ var WalkerActor = protocol.ActorClass({
     let maxNodes = options.maxNodes || -1;
     if (maxNodes == -1) {
       maxNodes = Number.MAX_VALUE;
     }
 
     // We're going to create a few document walkers with the same filter,
     // make it easier.
     let getFilteredWalker = (node) => {
-      return new DocumentWalker(node, this.rootWin, options.whatToShow);
+      return this.getDocumentWalker(node, options.whatToShow);
     }
 
     // Need to know the first and last child.
     let rawNode = node.rawNode;
     let firstChild = getFilteredWalker(rawNode).firstChild();
     let lastChild = getFilteredWalker(rawNode).lastChild();
 
     if (!firstChild) {
@@ -1614,17 +1621,17 @@ var WalkerActor = protocol.ActorClass({
    *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
    *
    * @returns an object with three items:
    *    hasFirst: true if the first child of the node is included in the list.
    *    hasLast: true if the last child of the node is included in the list.
    *    nodes: Child nodes returned by the request.
    */
   siblings: method(function(node, options={}) {
-    let parentNode = DocumentWalker(node.rawNode, this.rootWin).parentNode();
+    let parentNode = this.getDocumentWalker(node.rawNode, options.whatToShow).parentNode();
     if (!parentNode) {
       return {
         hasFirst: true,
         hasLast: true,
         nodes: [node]
       };
     }
 
@@ -1640,32 +1647,32 @@ var WalkerActor = protocol.ActorClass({
    * might be inefficient, be careful.
    *
    * @param object options
    *    Named options:
    *    `whatToShow`: A bitmask of node types that should be included.  See
    *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
    */
   nextSibling: method(function(node, options={}) {
-    let walker = DocumentWalker(node.rawNode, this.rootWin, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
+    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
     let sibling = walker.nextSibling();
     return sibling ? this._ref(sibling) : null;
   }, traversalMethod),
 
   /**
    * Get the previous sibling of a given node.  Getting nodes one at a time
    * might be inefficient, be careful.
    *
    * @param object options
    *    Named options:
    *    `whatToShow`: A bitmask of node types that should be included.  See
    *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
    */
   previousSibling: method(function(node, options={}) {
-    let walker = DocumentWalker(node.rawNode, this.rootWin, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
+    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
     let sibling = walker.previousSibling();
     return sibling ? this._ref(sibling) : null;
   }, traversalMethod),
 
   /**
    * Helper function for the `children` method: Read forward in the sibling
    * list into an array with `count` items, including the current node.
    */
@@ -1912,17 +1919,17 @@ var WalkerActor = protocol.ActorClass({
    */
   addPseudoClassLock: method(function(node, pseudo, options={}) {
     this._addPseudoClassLock(node, pseudo);
 
     if (!options.parents) {
       return;
     }
 
-    let walker = DocumentWalker(node.rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(node.rawNode);
     let cur;
     while ((cur = walker.parentNode())) {
       let curNode = this._ref(cur);
       this._addPseudoClassLock(curNode, pseudo);
     }
   }, {
     request: {
       node: Arg(0, "domnode"),
@@ -1993,17 +2000,17 @@ var WalkerActor = protocol.ActorClass({
    */
   removePseudoClassLock: method(function(node, pseudo, options={}) {
     this._removePseudoClassLock(node, pseudo);
 
     if (!options.parents) {
       return;
     }
 
-    let walker = DocumentWalker(node.rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(node.rawNode);
     let cur;
     while ((cur = walker.parentNode())) {
       let curNode = this._ref(cur);
       this._removePseudoClassLock(curNode, pseudo);
     }
   }, {
     request: {
       node: Arg(0, "domnode"),
@@ -2562,17 +2569,17 @@ var WalkerActor = protocol.ActorClass({
       this.rootNode = null;
     }
 
     this.queueMutation({
       type: "documentUnload",
       target: documentActor.actorID
     });
 
-    let walker = DocumentWalker(doc, this.rootWin);
+    let walker = this.getDocumentWalker(doc);
     let parentNode = walker.parentNode();
     if (parentNode) {
       // Send a childList mutation on the frame so that clients know
       // they should reread the children list.
       this.queueMutation({
         type: "childList",
         target: this._refMap.get(parentNode).actorID,
         added: [],
@@ -2587,17 +2594,17 @@ var WalkerActor = protocol.ActorClass({
 
   /**
    * Check if a node is attached to the DOM tree of the current page.
    * @param {nsIDomNode} rawNode
    * @return {Boolean} false if the node is removed from the tree or within a
    * document fragment
    */
   _isInDOMTree: function(rawNode) {
-    let walker = DocumentWalker(rawNode, this.rootWin);
+    let walker = this.getDocumentWalker(rawNode);
     let current = walker.currentNode;
 
     // Reaching the top of tree
     while (walker.parentNode()) {
       current = walker.currentNode;
     }
 
     // The top of the tree is a fragment or is not rootDoc, hence rawNode isn't
@@ -3102,17 +3109,19 @@ var InspectorActor = exports.InspectorAc
     if (window.document.readyState === "loading") {
       window.addEventListener("DOMContentLoaded", domReady, true);
     } else {
       domReady();
     }
 
     return this._walkerPromise;
   }, {
-    request: {},
+    request: {
+      options: Arg(0, "nullable:json")
+    },
     response: {
       walker: RetVal("domwalker")
     }
   }),
 
   getPageStyle: method(function() {
     if (this._pageStylePromise) {
       return this._pageStylePromise;
@@ -3251,18 +3260,18 @@ var InspectorFront = exports.InspectorFr
     this.manage(this);
   },
 
   destroy: function() {
     delete this.walker;
     protocol.Front.prototype.destroy.call(this);
   },
 
-  getWalker: protocol.custom(function() {
-    return this._getWalker().then(walker => {
+  getWalker: protocol.custom(function(options = {}) {
+    return this._getWalker(options).then(walker => {
       this.walker = walker;
       return walker;
     });
   }, {
     impl: "_getWalker"
   }),
 
   getPageStyle: protocol.custom(function() {
@@ -3287,39 +3296,34 @@ exports._documentWalker = DocumentWalker
 function nodeDocument(node) {
   return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
 }
 
 /**
  * Wrapper for inDeepTreeWalker.  Adds filtering to the traversal methods.
  * See inDeepTreeWalker for more information about the methods.
  *
- * @param {DOMNode} aNode
- * @param {Window} aRootWin
- * @param {Int} aShow See Ci.nsIDOMNodeFilter / inIDeepTreeWalker for options.
- * @param {Function} aFilter A custom filter function Taking in a DOMNode
- *        and returning an Int. See nodeFilter for an example.
+ * @param {DOMNode} node
+ * @param {Window} rootWin
+ * @param {Int} whatToShow See Ci.nsIDOMNodeFilter / inIDeepTreeWalker for options.
+ * @param {Function} filter A custom filter function Taking in a DOMNode
+ *        and returning an Int. See WalkerActor.nodeFilter for an example.
  */
-function DocumentWalker(aNode, aRootWin, aShow=Ci.nsIDOMNodeFilter.SHOW_ALL,
-                        aFilter=nodeFilter) {
-  if (!(this instanceof DocumentWalker)) {
-    return new DocumentWalker(aNode, aRootWin, aShow, aFilter);
-  }
-
-  if (!aRootWin.location) {
+function DocumentWalker(node, rootWin, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL, filter=standardTreeWalkerFilter) {
+  if (!rootWin.location) {
     throw new Error("Got an invalid root window in DocumentWalker");
   }
 
   this.walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"].createInstance(Ci.inIDeepTreeWalker);
   this.walker.showAnonymousContent = true;
   this.walker.showSubDocuments = true;
   this.walker.showDocumentsAsNodes = true;
-  this.walker.init(aRootWin.document, aShow);
-  this.walker.currentNode = aNode;
-  this.filter = aFilter;
+  this.walker.init(rootWin.document, whatToShow);
+  this.walker.currentNode = node;
+  this.filter = filter;
 }
 
 DocumentWalker.prototype = {
   get node() this.walker.node,
   get whatToShow() this.walker.whatToShow,
   get currentNode() this.walker.currentNode,
   set currentNode(aVal) this.walker.currentNode = aVal,
 
@@ -3371,19 +3375,21 @@ DocumentWalker.prototype = {
 };
 
 function isXULElement(el) {
   return el &&
          el.namespaceURI === XUL_NS;
 }
 
 /**
- * A tree walker filter for avoiding empty whitespace text nodes.
+ * This DeepTreeWalker filter skips whitespace text nodes and anonymous
+ * content with the exception of ::before and ::after and anonymous content
+ * in XUL document (needed to show all elements in the browser toolbox).
  */
-function nodeFilter(aNode) {
+function standardTreeWalkerFilter(aNode) {
   // Ignore empty whitespace text nodes.
   if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
       !/[^\s]/.exec(aNode.nodeValue)) {
     return Ci.nsIDOMNodeFilter.FILTER_SKIP;
   }
 
   // Ignore all native anonymous content (like internals for form
   // controls).  Except for:
@@ -3399,16 +3405,29 @@ function nodeFilter(aNode) {
       ) {
     return Ci.nsIDOMNodeFilter.FILTER_SKIP;
   }
 
   return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
 }
 
 /**
+ * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
+ * it also includes all anonymous content (like internal form controls).
+ */
+function allAnonymousContentTreeWalkerFilter(aNode) {
+  // Ignore empty whitespace text nodes.
+  if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
+      !/[^\s]/.exec(aNode.nodeValue)) {
+    return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+  }
+  return Ci.nsIDOMNodeFilter.FILTER_ACCEPT
+}
+
+/**
  * Given an image DOMNode, return the image data-uri.
  * @param {DOMNode} node The image node
  * @param {Number} maxDim Optionally pass a maximum size you want the longest
  * side of the image to be resized to before getting the image data.
  * @return {Object} An object containing the data-uri and size-related information
  * {data: "...", size: {naturalWidth: 400, naturalHeight: 300, resized: true}}
  * @throws an error if the node isn't an image or if the image is missing
  */
--- a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
@@ -99,17 +99,17 @@ function sortOwnershipChildren(children)
 
 function serverOwnershipSubtree(walker, node) {
   let actor = walker._refMap.get(node);
   if (!actor) {
     return undefined;
   }
 
   let children = [];
-  let docwalker = _documentWalker(node, window);
+  let docwalker = new _documentWalker(node, window);
   let child = docwalker.firstChild();
   while (child) {
     let item = serverOwnershipSubtree(walker, child);
     if (item) {
       children.push(item);
     }
     child = docwalker.nextSibling();
   }
--- a/toolkit/devtools/server/tests/mochitest/test_css-logic.html
+++ b/toolkit/devtools/server/tests/mochitest/test_css-logic.html
@@ -98,41 +98,41 @@ addTest(function findCssSelector() {
 
 addTest(function getComputedStyle() {
   let node = document.querySelector("#computed-style");
   is (CssLogic.getComputedStyle(node).getPropertyValue("width"),
       "50px", "Computed style on a normal node works (width)");
   is (CssLogic.getComputedStyle(node).getPropertyValue("height"),
       "10px", "Computed style on a normal node works (height)");
 
-  let firstChild = _documentWalker(node, window).firstChild();
+  let firstChild = new _documentWalker(node, window).firstChild();
   is (CssLogic.getComputedStyle(firstChild).getPropertyValue("content"),
       "\"before\"", "Computed style on a ::before node works (content)");
-  let lastChild = _documentWalker(node, window).lastChild();
+  let lastChild = new _documentWalker(node, window).lastChild();
   is (CssLogic.getComputedStyle(lastChild).getPropertyValue("content"),
       "\"after\"", "Computed style on a ::after node works (content)");
 
   runNextTest();
 });
 
 addTest(function getBindingElementAndPseudo() {
   let node = document.querySelector("#computed-style");
   var {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(node);
 
   is (bindingElement, node,
       "Binding element is the node itself for a normal node");
   ok (!pseudo, "Pseudo is null for a normal node");
 
-  let firstChild = _documentWalker(node, window).firstChild();
+  let firstChild = new _documentWalker(node, window).firstChild();
   var {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(firstChild);
   is (bindingElement, node,
       "Binding element is the parent for a pseudo node");
   is (pseudo, ":before", "Pseudo is correct for a ::before node");
 
-  let lastChild = _documentWalker(node, window).lastChild();
+  let lastChild = new _documentWalker(node, window).lastChild();
   var {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(lastChild);
   is (bindingElement, node,
       "Binding element is the parent for a pseudo node");
   is (pseudo, ":after", "Pseudo is correct for a ::after node");
 
   runNextTest();
 });
 
--- a/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html
@@ -60,17 +60,17 @@ addTest(function testRearrange() {
     ok(!gInspectee.querySelector("#a").nextSibling, "a should now be at the end of the list.");
     return gWalker.children(longlist);
   }).then(response => {
     is(nodeA, response.nodes[response.nodes.length - 1], "a should now be the last returned child.");
     // Now move it to the middle of the list.
     nextNode = response.nodes[13];
     return gWalker.insertBefore(nodeA, longlist, nextNode);
   }).then(response => {
-    let sibling = inspector._documentWalker(gInspectee.querySelector("#a"), window).nextSibling();
+    let sibling = new inspector._documentWalker(gInspectee.querySelector("#a"), window).nextSibling();
     is(sibling, nextNode.rawNode(), "Node should match the expected next node.");
     return gWalker.children(longlist);
   }).then(response => {
     is(nodeA, response.nodes[13], "a should be where we expect it.");
     is(nextNode, response.nodes[14], "next node should be where we expect it.");
   }).then(runNextTest));
 });
 
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -309,19 +309,17 @@ PopupNotifications.prototype = {
       if (isActiveWindow) {
         // show panel now
         this._update(notifications, notification.anchorElement, true);
       } else {
         // indicate attention and update the icon if necessary
         if (!notification.dismissed) {
           this.window.getAttention();
         }
-        if (notification.anchorElement.parentNode != this.iconBox) {
-          this._updateAnchorIcon(notifications, notification.anchorElement);
-        }
+        this._updateAnchorIcon(notifications, notification.anchorElement);
         this._notify("backgroundShow");
       }
 
     } else {
       // Notify observers that we're not showing the popup (useful for testing)
       this._notify("backgroundShow");
     }