Merge m-c to inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 25 Jan 2016 14:06:08 -0800
changeset 306983 2ecefc09d9af7ad20ed0deef0544eceab4d1cd2c
parent 306977 7e32d3531b96def705f7e5a84045365d29716ce1 (current diff)
parent 306982 aa90f482e16db77cdb7dea84564ea1cbd8f7f6b3 (diff)
child 306986 d19d71fdf85999caf68cd02c918e137d99ac8e2d
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone47.0a1
Merge m-c to inbound a=merge
CLOBBER
browser/themes/osx/tabbrowser/tab-active-middle-yosemite-inactive.png
browser/themes/osx/tabbrowser/tab-active-middle-yosemite-inactive@2x.png
browser/themes/osx/tabbrowser/tab-selected-end-yosemite-inactive.svg
browser/themes/osx/tabbrowser/tab-selected-start-yosemite-inactive.svg
browser/themes/osx/tabbrowser/tab-stroke-end-yosemite-inactive.png
browser/themes/osx/tabbrowser/tab-stroke-end-yosemite-inactive@2x.png
browser/themes/osx/tabbrowser/tab-stroke-start-yosemite-inactive.png
browser/themes/osx/tabbrowser/tab-stroke-start-yosemite-inactive@2x.png
devtools/client/themes/floating-scrollbars-light.css
devtools/client/themes/floating-scrollbars.css
mobile/android/base/java/org/mozilla/gecko/util/ScreenshotObserver.java
--- a/.hgtags
+++ b/.hgtags
@@ -118,8 +118,9 @@ 2c951493eef5b50b8085ef78ffe0d7902ff3d593
 98086da94ccdc88f6de86774ce3d1fa258dc7c44 FIREFOX_AURORA_38_BASE
 1b6bf6612c0f4d4fee81d18bf18016e692f874e1 FIREFOX_AURORA_39_BASE
 66a95a483d2c77dfc387019336d18093acd6aac2 FIREFOX_AURORA_40_BASE
 312c68b16549de9cea1557f461d5d234bd5e0a7d FIREFOX_AURORA_41_BASE
 7a19194812eb767bee7cdf8fc36ba9a383c1bead FIREFOX_AURORA_42_BASE
 fcef8ded82219c89298b4e376cfbdfba79a1d35a FIREFOX_AURORA_43_BASE
 67a788db9f07822cfef52351bbbe3745dff8bd7f FIREFOX_AURORA_44_BASE
 99137d6d4061f408ae0869122649d8bdf489cc30 FIREFOX_AURORA_45_BASE
+67c66c2878aed17ae3096d7db483ddbb2293c503 FIREFOX_AURORA_46_BASE
--- a/browser/base/content/browser-trackingprotection.js
+++ b/browser/base/content/browser-trackingprotection.js
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var TrackingProtection = {
+  // If the user ignores the doorhanger, we stop showing it after some time.
   MAX_INTROS: 0,
   PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
   PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
   enabledGlobally: false,
   enabledInPrivateWindows: false,
   container: null,
   content: null,
   icon: null,
@@ -101,21 +102,23 @@ var TrackingProtection = {
     let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
 
     if (isBlocking) {
       this.icon.setAttribute("tooltiptext", this.activeTooltipText);
       this.icon.setAttribute("state", "blocked-tracking-content");
       this.content.setAttribute("state", "blocked-tracking-content");
 
       // Open the tracking protection introduction panel, if applicable.
-      let introCount = gPrefService.getIntPref("privacy.trackingprotection.introCount");
-      if (introCount < TrackingProtection.MAX_INTROS) {
-        gPrefService.setIntPref("privacy.trackingprotection.introCount", ++introCount);
-        gPrefService.savePrefFile(null);
-        this.showIntroPanel();
+      if (this.enabledGlobally) {
+        let introCount = gPrefService.getIntPref("privacy.trackingprotection.introCount");
+        if (introCount < TrackingProtection.MAX_INTROS) {
+          gPrefService.setIntPref("privacy.trackingprotection.introCount", ++introCount);
+          gPrefService.savePrefFile(null);
+          this.showIntroPanel();
+        }
       }
 
       this.shieldHistogramAdd(2);
     } else if (isAllowing) {
       this.icon.setAttribute("tooltiptext", this.disabledTooltipText);
       this.icon.setAttribute("state", "loaded-tracking-content");
       this.content.setAttribute("state", "loaded-tracking-content");
 
@@ -178,29 +181,37 @@ var TrackingProtection = {
     this.eventsHistogramAdd(2);
 
     // Hide the control center.
     document.getElementById("identity-popup").hidePopup();
 
     BrowserReload();
   },
 
+  dontShowIntroPanelAgain() {
+    // This function may be called in private windows, but it does not change
+    // any preference unless Tracking Protection is enabled globally.
+    if (this.enabledGlobally) {
+      gPrefService.setIntPref("privacy.trackingprotection.introCount",
+                              this.MAX_INTROS);
+      gPrefService.savePrefFile(null);
+    }
+  },
+
   showIntroPanel: Task.async(function*() {
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
 
     let openStep2 = () => {
       // When the user proceeds in the tour, adjust the counter to indicate that
       // the user doesn't need to see the intro anymore.
-      gPrefService.setIntPref("privacy.trackingprotection.introCount",
-                              this.MAX_INTROS);
-      gPrefService.savePrefFile(null);
+      this.dontShowIntroPanelAgain();
 
       let nextURL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
-                    "#step2";
+                    "?step=2&newtab=true";
       switchToTabHavingURI(nextURL, true, {
         // Ignore the fragment in case the intro is shown on the tour page
         // (e.g. if the user manually visited the tour or clicked the link from
         // about:privatebrowsing) so we can avoid a reload.
         ignoreFragment: true,
       });
     };
 
@@ -215,13 +226,14 @@ var TrackingProtection = {
         style: "primary",
       },
     ];
 
     let panelTarget = yield UITour.getTarget(window, "trackingProtection");
     UITour.initForBrowser(gBrowser.selectedBrowser, window);
     UITour.showInfo(window, panelTarget,
                     gNavigatorBundle.getString("trackingProtection.intro.title"),
-                    gNavigatorBundle.getFormattedString("trackingProtection.intro.description",
+                    gNavigatorBundle.getFormattedString("trackingProtection.intro.description2",
                                                         [brandShortName]),
-                    undefined, buttons);
+                    undefined, buttons,
+                    { closeButtonCallback: () => this.dontShowIntroPanelAgain() });
   }),
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7877,16 +7877,21 @@ var AboutPrivateBrowsingListener = {
         OpenBrowserWindow({private: true});
     });
     window.messageManager.addMessageListener(
       "AboutPrivateBrowsing:ToggleTrackingProtection",
       msg => {
         const PREF = "privacy.trackingprotection.pbmode.enabled";
         Services.prefs.setBoolPref(PREF, !Services.prefs.getBoolPref(PREF));
     });
+    window.messageManager.addMessageListener(
+      "AboutPrivateBrowsing:DontShowIntroPanelAgain",
+      msg => {
+        TrackingProtection.dontShowIntroPanelAgain();
+    });
   }
 };
 
 function TabModalPromptBox(browser) {
   this._weakBrowserRef = Cu.getWeakReference(browser);
 }
 
 TabModalPromptBox.prototype = {
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -224,16 +224,18 @@ var AboutHomeListener = {
 AboutHomeListener.init(this);
 
 var AboutPrivateBrowsingListener = {
   init(chromeGlobal) {
     chromeGlobal.addEventListener("AboutPrivateBrowsingOpenWindow", this,
                                   false, true);
     chromeGlobal.addEventListener("AboutPrivateBrowsingToggleTrackingProtection", this,
                                   false, true);
+    chromeGlobal.addEventListener("AboutPrivateBrowsingDontShowIntroPanelAgain", this,
+                                  false, true);
   },
 
   get isAboutPrivateBrowsing() {
     return content.document.documentURI.toLowerCase() == "about:privatebrowsing";
   },
 
   handleEvent(aEvent) {
     if (!this.isAboutPrivateBrowsing) {
@@ -241,16 +243,19 @@ var AboutPrivateBrowsingListener = {
     }
     switch (aEvent.type) {
       case "AboutPrivateBrowsingOpenWindow":
         sendAsyncMessage("AboutPrivateBrowsing:OpenPrivateWindow");
         break;
       case "AboutPrivateBrowsingToggleTrackingProtection":
         sendAsyncMessage("AboutPrivateBrowsing:ToggleTrackingProtection");
         break;
+      case "AboutPrivateBrowsingDontShowIntroPanelAgain":
+        sendAsyncMessage("AboutPrivateBrowsing:DontShowIntroPanelAgain");
+        break;
     }
   },
 };
 AboutPrivateBrowsingListener.init(this);
 
 var AboutReaderListener = {
 
   _articlePromise: null,
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -52,16 +52,18 @@ document.addEventListener("DOMContentLoa
 
   document.title = stringBundle.GetStringFromName("title");
   document.getElementById("favicon")
           .setAttribute("href", FAVICON_PRIVACY);
   document.getElementById("enableTrackingProtection")
           .addEventListener("click", toggleTrackingProtection);
   document.getElementById("disableTrackingProtection")
           .addEventListener("click", toggleTrackingProtection);
+  document.getElementById("startTour")
+          .addEventListener("click", dontShowIntroPanelAgain);
 
   let formatURLPref = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
                         .getService(Ci.nsIURLFormatter).formatURLPref;
   document.getElementById("startTour").setAttribute("href",
                      formatURLPref("privacy.trackingprotection.introURL"));
   document.getElementById("learnMore").setAttribute("href",
                      formatURLPref("app.support.baseURL") + "private-browsing");
 
@@ -76,8 +78,15 @@ function openPrivateWindow() {
 }
 
 function toggleTrackingProtection() {
   // Ask chrome to enable tracking protection
   document.dispatchEvent(
     new CustomEvent("AboutPrivateBrowsingToggleTrackingProtection",
                     {bubbles:true}));
 }
+
+function dontShowIntroPanelAgain() {
+  // Ask chrome to disable the doorhanger
+  document.dispatchEvent(
+    new CustomEvent("AboutPrivateBrowsingDontShowIntroPanelAgain",
+                    {bubbles:true}));
+}
--- a/browser/components/uitour/test/browser_trackingProtection.js
+++ b/browser/components/uitour/test/browser_trackingProtection.js
@@ -47,17 +47,17 @@ add_task(function* test_trackingPages() 
     yield new Promise((resolve, reject) => {
       waitForPopupAtAnchor(TOOLTIP_PANEL, TOOLTIP_ANCHOR, resolve,
                            "Intro panel should appear");
     });
 
     is(Services.prefs.getIntPref(PREF_INTRO_COUNT), TrackingProtection.MAX_INTROS, "Check intro count increased");
 
     let step2URL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
-                    "#step2";
+                   "?step=2&newtab=true";
     let buttons = document.getElementById("UITourTooltipButtons");
 
     info("Click the step text and nothing should happen");
     let tabCount = gBrowser.tabs.length;
     yield EventUtils.synthesizeMouseAtCenter(buttons.children[0], {});
     is(gBrowser.tabs.length, tabCount, "Same number of tabs should be open");
 
     info("Resetting count to test that viewing the tour prevents future panels");
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-46.0a1
+47.0a1
--- a/browser/config/version_display.txt
+++ b/browser/config/version_display.txt
@@ -1,1 +1,1 @@
-46.0a1
+47.0a1
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -327,18 +327,20 @@ offlineApps.manageUsageAccessKey=S
 
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site.
 identity.identified.state_and_country=%S, %S
 
 identity.unknown.tooltip=This website does not supply identity information.
 
 trackingProtection.intro.title=How Tracking Protection works
-# LOCALIZATION NOTE (trackingProtection.intro.description): %S is brandShortName
-trackingProtection.intro.description=When the shield is visible, that means Firefox is actively blocking content that tracks you.
+# LOCALIZATION NOTE (trackingProtection.intro.description2):
+# %S is brandShortName. This string should match the one from Step 1 of the tour
+# when it starts from the button shown when a new private window is opened.
+trackingProtection.intro.description2=When you see the shield, %S is blocking some parts of the page that could track your browsing activity.
 # LOCALIZATION NOTE (trackingProtection.intro.step1of3): Indicates that the intro panel is step one of three in a tour.
 trackingProtection.intro.step1of3=1 of 3
 trackingProtection.intro.nextButton.label=Next
 
 trackingProtection.icon.activeTooltip=Tracking attempts blocked
 trackingProtection.icon.disabledTooltip=Tracking content detected
 
 # Edit Bookmark UI
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -59,22 +59,16 @@
   content: "";
   display: -moz-box;
   height: 2px;
   margin-top: -2px;
   position: relative;
   z-index: 2; /* navbar is at 1 */
 }
 
-@media (-moz-mac-yosemite-theme) {
-  #navigator-toolbox::after {
-    background-image: linear-gradient(to top, hsla(0,0%,0%,.1), hsla(0,0%,0%,.1) 1px, hsla(0,0%,100%,0) 1px, hsla(0,0%,100%,0) 2px, transparent 3px);
-  }
-}
-
 #navigator-toolbox toolbarbutton:-moz-lwtheme {
   color: inherit;
   text-shadow: inherit;
 }
 
 #main-window {
   -moz-appearance: none;
   background-color: #eeeeee;
@@ -151,20 +145,16 @@ toolbarseparator {
   background: url(chrome://browser/skin/Toolbar-background-noise.png) hsl(0,0%,83%);
 }
 
 /* remove noise texture on Yosemite */
 @media (-moz-mac-yosemite-theme) {
   #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
     background-image: none;
   }
-
-  #navigator-toolbox > toolbar:-moz-window-inactive:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
-    background-color: hsl(0,0%,95%);
-  }
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar) {
   overflow: -moz-hidden-unscrollable;
   max-height: 4em;
   transition: min-height 170ms ease-out, max-height 170ms ease-out;
 }
 
@@ -197,20 +187,16 @@ toolbarseparator {
   }
 }
 
 /* remove noise texture on Yosemite */
 @media (-moz-mac-yosemite-theme) {
   #nav-bar {
     background: linear-gradient(hsl(0,0%,93%), hsl(0,0%,83%));
   }
-
-  #nav-bar:-moz-window-inactive {
-    background: linear-gradient(hsl(0,0%,97%), hsl(0,0%,95%));
-  }
 }
 
 /* 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);
@@ -227,20 +213,16 @@ toolbarseparator {
   #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: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
     /* Position the toolbar above the bottom of background tabs */
     position: relative;
     z-index: 1;
   }
-
-  #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-window-inactive:not(:-moz-lwtheme) {
-    border-top-color: hsla(0,0%,0%,.05);
-  }
 }
 
 #nav-bar-customization-target {
   padding: 4px;
 }
 
 #PersonalToolbar {
   padding: 0 4px 4px;
@@ -2485,64 +2467,16 @@ toolbarbutton.chevron > .toolbarbutton-m
     list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
   }
 
   .tab-throbber[progress] {
     list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
   }
 }
 
-@media (-moz-mac-yosemite-theme) {
-  .tab-background-middle[visuallyselected=true]:-moz-window-inactive {
-    background-image: url(chrome://browser/skin/yosemite/tab-active-middle-inactive.png),
-                      @fgTabTextureYosemiteInactive@,
-                      none;
-  }
-
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
-    background-image: url(chrome://browser/skin/yosemite/tab-stroke-start-inactive.png);
-  }
-
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
-    background-image: url(chrome://browser/skin/yosemite/tab-stroke-end-inactive.png);
-  }
-
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
-    background-image: url(chrome://browser/skin/yosemite/tab-selected-start-inactive.svg);
-    background-size: 100% 100%;
-  }
-
-  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
-    background-image: url(chrome://browser/skin/yosemite/tab-selected-end-inactive.svg);
-    background-size: 100% 100%;
-  }
-
-  @media (min-resolution: 2dppx) {
-    .tab-background-middle[visuallyselected=true]:-moz-window-inactive {
-      background-image: url(chrome://browser/skin/yosemite/tab-active-middle-inactive@2x.png),
-                        @fgTabTextureYosemiteInactive@,
-                        none;
-    }
-
-    .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-    .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
-      background-image: url(chrome://browser/skin/yosemite/tab-stroke-start-inactive@2x.png);
-    }
-
-    .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
-    .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
-      background-image: url(chrome://browser/skin/yosemite/tab-stroke-end-inactive@2x.png);
-    }
-  }
-}
-
 .tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([visuallyselected="true"]) {
   opacity: .9;
 }
 
 /*
  * Force the overlay to create a new stacking context so it always appears on
  * top of the icon.
  */
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -233,24 +233,16 @@ browser.jar:
   skin/classic/browser/yosemite/menuPanel-help.png                     (menuPanel-help-yosemite.png)
   skin/classic/browser/yosemite/menuPanel-help@2x.png                  (menuPanel-help-yosemite@2x.png)
   skin/classic/browser/yosemite/menuPanel-small.png                    (menuPanel-small-yosemite.png)
   skin/classic/browser/yosemite/menuPanel-small@2x.png                 (menuPanel-small-yosemite@2x.png)
   skin/classic/browser/yosemite/reload-stop-go.png                     (reload-stop-go-yosemite.png)
   skin/classic/browser/yosemite/reload-stop-go@2x.png                  (reload-stop-go-yosemite@2x.png)
   skin/classic/browser/yosemite/sync-horizontalbar.png                 (sync-horizontalbar-yosemite.png)
   skin/classic/browser/yosemite/sync-horizontalbar@2x.png              (sync-horizontalbar-yosemite@2x.png)
-  skin/classic/browser/yosemite/tab-selected-end-inactive.svg          (tabbrowser/tab-selected-end-yosemite-inactive.svg)
-  skin/classic/browser/yosemite/tab-selected-start-inactive.svg        (tabbrowser/tab-selected-start-yosemite-inactive.svg)
-  skin/classic/browser/yosemite/tab-active-middle-inactive.png         (tabbrowser/tab-active-middle-yosemite-inactive.png)
-  skin/classic/browser/yosemite/tab-active-middle-inactive@2x.png      (tabbrowser/tab-active-middle-yosemite-inactive@2x.png)
-  skin/classic/browser/yosemite/tab-stroke-end-inactive.png            (tabbrowser/tab-stroke-end-yosemite-inactive.png)
-  skin/classic/browser/yosemite/tab-stroke-end-inactive@2x.png         (tabbrowser/tab-stroke-end-yosemite-inactive@2x.png)
-  skin/classic/browser/yosemite/tab-stroke-start-inactive.png          (tabbrowser/tab-stroke-start-yosemite-inactive.png)
-  skin/classic/browser/yosemite/tab-stroke-start-inactive@2x.png       (tabbrowser/tab-stroke-start-yosemite-inactive@2x.png)
 #ifdef E10S_TESTING_ONLY
   skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
 #endif
 
 [extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
 % override chrome://browser/skin/feeds/audioFeedIcon.png                   chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/audioFeedIcon16.png                 chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/videoFeedIcon.png                   chrome://browser/skin/feeds/feedIcon.png
@@ -269,10 +261,10 @@ browser.jar:
 % override chrome://browser/skin/menuPanel-help.png                        chrome://browser/skin/yosemite/menuPanel-help.png                       os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-help@2x.png                     chrome://browser/skin/yosemite/menuPanel-help@2x.png                    os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-small.png                       chrome://browser/skin/yosemite/menuPanel-small.png                      os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-small@2x.png                    chrome://browser/skin/yosemite/menuPanel-small@2x.png                   os=Darwin osversion>=10.10
 % override chrome://browser/skin/preferences/checkbox.png                  chrome://browser/skin/yosemite/preferences/checkbox.png                 os=Darwin osversion>=10.10
 % override chrome://browser/skin/preferences/checkbox@2x.png               chrome://browser/skin/yosemite/preferences/checkbox@2x.png              os=Darwin osversion>=10.10
 % override chrome://browser/skin/reload-stop-go.png                        chrome://browser/skin/yosemite/reload-stop-go.png                       os=Darwin osversion>=10.10
 % override chrome://browser/skin/reload-stop-go@2x.png                     chrome://browser/skin/yosemite/reload-stop-go@2x.png                    os=Darwin osversion>=10.10
-% override chrome://browser/skin/sync-horizontalbar.png                    chrome://browser/skin/yosemite/sync-horizontalbar.png               os=Darwin osversion>=10.10
-% override chrome://browser/skin/sync-horizontalbar@2x.png                 chrome://browser/skin/yosemite/sync-horizontalbar@2x.png            os=Darwin osversion>=10.10
+% override chrome://browser/skin/sync-horizontalbar.png                    chrome://browser/skin/yosemite/sync-horizontalbar.png                   os=Darwin osversion>=10.10
+% override chrome://browser/skin/sync-horizontalbar@2x.png                 chrome://browser/skin/yosemite/sync-horizontalbar@2x.png                os=Darwin osversion>=10.10
--- a/browser/themes/osx/shared.inc
+++ b/browser/themes/osx/shared.inc
@@ -1,13 +1,12 @@
 %include ../../../toolkit/themes/osx/global/shared.inc
 %include ../shared/browser.inc
 
 %filter substitution
 
 %define fgTabTexture linear-gradient(transparent 2px, hsla(0,0%,100%,.6) 2px, hsla(0,0%,100%,.6) 3px, hsl(0,0%,99%) 3px, hsl(0,0%,93%))
-%define fgTabTextureYosemiteInactive linear-gradient(transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,97%))
 %define toolbarColorLWT rgba(253,253,253,0.45)
 %define fgTabTextureLWT linear-gradient(transparent 2px, rgba(254,254,254,.72) 2px, @toolbarColorLWT@)
 %define fgTabBackgroundColor transparent
 %define hudButton -moz-appearance: none; color: #434343; border-radius: 4px; border: 1px solid #b5b5b5; background: linear-gradient(#fff, #f2f2f2); box-shadow: inset 0 1px rgba(255,255,255,.8), inset 0 0 1px rgba(255,255, 255,.25), 0 1px rgba(255,255,255,.3); background-clip: padding-box; background-origin: padding-box; padding: 2px 6px;
 %define hudButtonPressed box-shadow: inset 0 1px 4px -3px #000, 0 1px rgba(255,255,255,.3);
 %define hudButtonFocused box-shadow: 0 0 1px -moz-mac-focusring inset, 0 0 4px 1px -moz-mac-focusring, 0 0 2px 1px -moz-mac-focusring;
deleted file mode 100644
index 9f74c4e7654d18ca9b811c778ebf8586ba525dcd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 50961db7ec08f990d349ab94ebe4296cddd72368..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/osx/tabbrowser/tab-selected-end-yosemite-inactive.svg
+++ /dev/null
@@ -1,28 +0,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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="30px" height="31px" preserveAspectRatio="none">
-  <defs>
-    <style>
-      #tab-background-fill {
-        background-color: transparent;
-        background-image: linear-gradient(transparent, transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,97%));
-        background-repeat: no-repeat;
-        height: 100%;
-        width: 100%;
-      }
-    </style>
-
-    <clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
-      <path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
-    </clipPath>
-
-    <clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
-      <path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
-    </clipPath>
-  </defs>
-
-  <foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-end)">
-    <div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
-  </foreignObject>
-</svg>
deleted file mode 100644
--- a/browser/themes/osx/tabbrowser/tab-selected-start-yosemite-inactive.svg
+++ /dev/null
@@ -1,28 +0,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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="30px" height="31px" preserveAspectRatio="none">
-  <defs>
-    <style>
-      #tab-background-fill {
-        background-color: transparent;
-        background-image: linear-gradient(transparent, transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,97%));
-        background-repeat: no-repeat;
-        height: 100%;
-        width: 100%;
-      }
-    </style>
-
-    <clipPath id="tab-curve-clip-path-start" clipPathUnits="objectBoundingBox">
-      <path d="m 1,0.0625 0.05,0 0,0.938 -1,0 0,-0.028 C 0.32082458,0.95840561 0.4353096,0.81970962 0.48499998,0.5625 0.51819998,0.3905 0.535,0.0659 1,0.0625 z"/>
-    </clipPath>
-
-    <clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
-      <path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
-    </clipPath>
-  </defs>
-
-  <foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-start)">
-    <div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
-  </foreignObject>
-</svg>
deleted file mode 100644
index 4206f0c7dca0188d886a6a10f4a9ba0a6c7767dc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 21ac892703cffc3bb16e4c63e7735984b598f23c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2acbac438c6aea4c4f3c4b0a2efb859a5f8945d7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index a12fc39a7b10829cffefb0f736eebd8a545ef19f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.py.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-46.0a1
+47.0a1
--- a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
@@ -44,21 +44,34 @@ add_task(function *() {
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(TAB_URL);
 
   yield waitForWorkersUpdate(document);
 
   assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
 
-  // XXX: race, the WorkerDebugger is ready whereas ServiceWorkerInfo
-  // doesn't has the worker registered yet on its side
+  // Ensure that the registration resolved before trying to connect to the sw
+  let frameScript = function () {
+    // Retrieve the `sw` promise created in the html page
+    let { sw } = content.wrappedJSObject;
+    sw.then(function (registration) {
+      sendAsyncMessage("sw-registered");
+    });
+  };
+  let mm = swTab.linkedBrowser.messageManager;
+  mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
+
   yield new Promise(done => {
-    require("sdk/timers").setTimeout(done, 250);
+    mm.addMessageListener("sw-registered", function listener() {
+      mm.removeMessageListener("sw-registered", listener);
+      done();
+    });
   });
+  ok(true, "Service worker registration resolved");
 
   // Retrieve the DEBUG button for the worker
   let names = [...document.querySelectorAll("#service-workers .target-name")];
   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
   ok(name, "Found the service worker in the list");
   let debugBtn = name.parentNode.parentNode.querySelector("button");
   ok(debugBtn, "Found its debug button");
 
@@ -88,29 +101,29 @@ add_task(function *() {
   // after we destroy the toolbox.
   // The list should update once it get destroyed.
   yield waitForWorkersUpdate(document);
 
   assertHasWorker(false, document, "service-workers", SERVICE_WORKER);
 
   // Finally, unregister the service worker itself
   // Use message manager to work with e10s
-  let frameScript = function () {
+  frameScript = function () {
     // Retrieve the `sw` promise created in the html page
     let { sw } = content.wrappedJSObject;
     sw.then(function (registration) {
       registration.unregister().then(function (success) {
         sendAsyncMessage("sw-unregistered");
       },
       function (e) {
         dump("SW not unregistered; " + e + "\n");
       });
     });
   };
-  let mm = swTab.linkedBrowser.messageManager;
+  mm = swTab.linkedBrowser.messageManager;
   mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
 
   yield new Promise(done => {
     mm.addMessageListener("sw-unregistered", function listener() {
       mm.removeMessageListener("sw-unregistered", listener);
       done();
     });
   });
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -103,19 +103,20 @@ OptionsPanel.prototype = {
     } else {
       targetPromise = promise.resolve(this.target);
     }
 
     return targetPromise.then(() => {
       this.setupToolsList();
       this.setupToolbarButtonsList();
       this.setupThemeList();
-      this.populatePreferences();
       this.updateDefaultTheme();
     }).then(() => {
+      return this.populatePreferences();
+    }).then(() => {
       this.isReady = true;
       this.emit("ready");
       return this;
     }).then(null, function onError(aReason) {
       Cu.reportError("OptionsPanel open failed. " +
                      aReason.error + ": " + aReason.message);
     });
   },
@@ -310,17 +311,17 @@ OptionsPanel.prototype = {
         }
       }
       menulist.addEventListener("command", function() {
         setPrefAndEmit(this.getAttribute("data-pref"), this.value);
       }.bind(menulist));
     }
 
     if (this.target.activeTab) {
-      this.target.client.attachTab(this.target.activeTab._actor, (response) => {
+      return this.target.client.attachTab(this.target.activeTab._actor).then(([response,client]) => {
         this._origJavascriptEnabled = !response.javascriptEnabled;
         this.disableJSNode.checked = this._origJavascriptEnabled;
         this.disableJSNode.addEventListener("click", this._disableJSClicked, false);
       });
     } else {
       this.disableJSNode.hidden = true;
     }
   },
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-appears-on-swatch-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-appears-on-swatch-click.js
@@ -45,10 +45,10 @@ function* testColorPickerAppearsOnColorS
   let onShown = cPicker.tooltip.once("shown");
   swatch.click();
   yield onShown;
 
   ok(true, "The color picker was shown on click of the color swatch");
   ok(!inplaceEditor(swatch.parentNode),
     "The inplace editor wasn't shown as a result of the color swatch click");
 
-  yield hideTooltipAndWaitForRuleviewChanged(cPicker, view);
+  yield hideTooltipAndWaitForRuleViewChanged(cPicker, view);
 }
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-edit-gradient.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-edit-gradient.js
@@ -67,10 +67,10 @@ function* testPickingNewColor(view) {
   is(swatchEl.style.backgroundColor, "rgb(1, 1, 1)",
     "The color swatch's background was updated");
   is(colorEl.textContent, "#010101", "The color text was updated");
   is(content.getComputedStyle(content.document.body).backgroundImage,
     "linear-gradient(to left, rgb(1, 1, 1) 25%, rgb(51, 51, 51) 95%, " +
       "rgb(0, 0, 0) 100%)",
     "The gradient has been updated correctly");
 
-  yield hideTooltipAndWaitForRuleviewChanged(cPicker, view);
+  yield hideTooltipAndWaitForRuleViewChanged(cPicker, view);
 }
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-multiple-changes.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-multiple-changes.js
@@ -85,17 +85,17 @@ function* testComplexMultipleColorChange
     yield simulateColorPickerChange(ruleView, picker, rgba, {
       element: content.document.body,
       name: "backgroundColor",
       value: computed
     });
   }
 
   info("Closing the color picker");
-  yield hideTooltipAndWaitForRuleviewChanged(picker.tooltip, ruleView);
+  yield hideTooltipAndWaitForRuleViewChanged(picker.tooltip, ruleView);
 }
 
 function* testOverriddenMultipleColorChanges(inspector, ruleView) {
   yield selectNode("p", inspector);
 
   info("Getting the <body> tag's color property");
   let swatch = getRuleViewProperty(ruleView, "body", "color").valueSpan
     .querySelector(".ruleview-colorswatch");
--- a/devtools/client/inspector/rules/test/browser_rules_cubicbezier-appears-on-swatch-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_cubicbezier-appears-on-swatch-click.js
@@ -61,10 +61,10 @@ function* testAppears(view, swatch) {
 
   let onShown = bezier.tooltip.once("shown");
   swatch.click();
   yield onShown;
 
   ok(true, "The cubic-bezier tooltip was shown on click of the cibuc swatch");
   ok(!inplaceEditor(swatch.parentNode),
     "The inplace editor wasn't shown as a result of the cibuc swatch click");
-  yield hideTooltipAndWaitForRuleviewChanged(bezier, view);
+  yield hideTooltipAndWaitForRuleViewChanged(bezier, view);
 }
--- a/devtools/client/inspector/rules/test/browser_rules_filtereditor-appears-on-swatch-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_filtereditor-appears-on-swatch-click.js
@@ -24,10 +24,10 @@ add_task(function*() {
   swatch.click();
   yield onRuleViewChanged;
 
   ok(true, "The shown event was emitted after clicking on swatch");
   ok(!inplaceEditor(swatch.parentNode),
   "The inplace editor wasn't shown as a result of the filter swatch click");
 
   yield filterTooltip.widget;
-  yield hideTooltipAndWaitForRuleviewChanged(filterTooltip, view);
+  yield hideTooltipAndWaitForRuleViewChanged(filterTooltip, view);
 });
--- a/devtools/client/inspector/rules/test/browser_rules_multiple-properties-duplicates.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple-properties-duplicates.js
@@ -8,26 +8,28 @@
 // unfinished properties/values in inplace-editors
 
 const TEST_URI = "<div>Test Element</div>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("div", inspector);
-  yield testCreateNewMultiDuplicates(inspector, view);
-});
 
-function* testCreateNewMultiDuplicates(inspector, view) {
   let ruleEditor = getRuleViewRuleEditor(view, 0);
+  // Note that we wait for a markup mutation here because this new rule will end
+  // up creating a style attribute on the node shown in the markup-view.
+  // (we also wait for the rule-view to refresh).
   let onMutation = inspector.once("markupmutation");
+  let onRuleViewChanged = view.once("ruleview-changed");
   yield createNewRuleViewProperty(ruleEditor,
     "color:red;color:orange;color:yellow;color:green;color:blue;color:indigo;" +
     "color:violet;");
   yield onMutation;
+  yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 7,
     "Should have created new text properties.");
   is(ruleEditor.propertyList.children.length, 8,
     "Should have created new property editors.");
 
   is(ruleEditor.rule.textProps[0].name, "color",
     "Should have correct property name");
@@ -58,9 +60,9 @@ function* testCreateNewMultiDuplicates(i
     "Should have correct property name");
   is(ruleEditor.rule.textProps[5].value, "indigo",
     "Should have correct property value");
 
   is(ruleEditor.rule.textProps[6].name, "color",
     "Should have correct property name");
   is(ruleEditor.rule.textProps[6].value, "violet",
     "Should have correct property value");
-}
+});
--- a/devtools/client/inspector/rules/test/browser_rules_multiple-properties-priority.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple-properties-priority.js
@@ -8,25 +8,27 @@
 // unfinished properties/values in inplace-editors.
 
 const TEST_URI = "<div>Test Element</div>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("div", inspector);
-  yield testCreateNewMultiPriority(inspector, view);
-});
 
-function* testCreateNewMultiPriority(inspector, view) {
   let ruleEditor = getRuleViewRuleEditor(view, 0);
+  // Note that we wait for a markup mutation here because this new rule will end
+  // up creating a style attribute on the node shown in the markup-view.
+  // (we also wait for the rule-view to refresh).
   let onMutation = inspector.once("markupmutation");
+  let onRuleViewChanged = view.once("ruleview-changed");
   yield createNewRuleViewProperty(ruleEditor,
     "color:red;width:100px;height: 100px;");
   yield onMutation;
+  yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 3,
     "Should have created new text properties.");
   is(ruleEditor.propertyList.children.length, 4,
     "Should have created new property editors.");
 
   is(ruleEditor.rule.textProps[0].name, "color",
     "Should have correct property name");
@@ -37,9 +39,9 @@ function* testCreateNewMultiPriority(ins
     "Should have correct property name");
   is(ruleEditor.rule.textProps[1].value, "100px",
     "Should have correct property value");
 
   is(ruleEditor.rule.textProps[2].name, "height",
     "Should have correct property name");
   is(ruleEditor.rule.textProps[2].value, "100px",
     "Should have correct property value");
-}
+});
--- a/devtools/client/inspector/rules/test/browser_rules_multiple-properties-unfinished_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple-properties-unfinished_02.js
@@ -8,36 +8,40 @@
 // unfinished properties/values in inplace-editors
 
 const TEST_URI = "<div>Test Element</div>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("div", inspector);
-  yield testCreateNewMultiPartialUnfinished(inspector, view);
-});
 
-function* testCreateNewMultiPartialUnfinished(inspector, view) {
   let ruleEditor = getRuleViewRuleEditor(view, 0);
+  // Note that we wait for a markup mutation here because this new rule will end
+  // up creating a style attribute on the node shown in the markup-view.
+  // (we also wait for the rule-view to refresh).
   let onMutation = inspector.once("markupmutation");
+  let onRuleViewChanged = view.once("ruleview-changed");
   yield createNewRuleViewProperty(ruleEditor, "width: 100px; heig");
   yield onMutation;
+  yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 2,
     "Should have created a new text property.");
   is(ruleEditor.propertyList.children.length, 2,
     "Should have created a property editor.");
 
   // Value is focused, lets add multiple rules here and make sure they get added
   onMutation = inspector.once("markupmutation");
+  onRuleViewChanged = view.once("ruleview-changed");
   let valueEditor = ruleEditor.propertyList.children[1].querySelector("input");
   valueEditor.value = "10px;background:orangered;color: black;";
   EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
   yield onMutation;
+  yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 4,
     "Should have added the changed value.");
   is(ruleEditor.propertyList.children.length, 5,
     "Should have added the changed value editor.");
 
   is(ruleEditor.rule.textProps[0].name, "width",
     "Should have correct property name");
@@ -53,9 +57,9 @@ function* testCreateNewMultiPartialUnfin
     "Should have correct property name");
   is(ruleEditor.rule.textProps[2].value, "orangered",
     "Should have correct property value");
 
   is(ruleEditor.rule.textProps[3].name, "color",
     "Should have correct property name");
   is(ruleEditor.rule.textProps[3].value, "black",
     "Should have correct property value");
-}
+});
--- a/devtools/client/inspector/rules/test/browser_rules_multiple_properties_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple_properties_01.js
@@ -8,26 +8,28 @@
 // unfinished properties/values in inplace-editors.
 
 const TEST_URI = "<div>Test Element</div>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("div", inspector);
-  yield testCreateNewMulti(inspector, view);
-});
 
-function* testCreateNewMulti(inspector, view) {
   let ruleEditor = getRuleViewRuleEditor(view, 0);
+  // Note that we wait for a markup mutation here because this new rule will end
+  // up creating a style attribute on the node shown in the markup-view.
+  // (we also wait for the rule-view to refresh).
   let onMutation = inspector.once("markupmutation");
+  let onRuleViewChanged = view.once("ruleview-changed");
   yield createNewRuleViewProperty(ruleEditor,
     "color:blue;background : orange   ; text-align:center; " +
     "border-color: green;");
   yield onMutation;
+  yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 4,
     "Should have created a new text property.");
   is(ruleEditor.propertyList.children.length, 5,
     "Should have created a new property editor.");
 
   is(ruleEditor.rule.textProps[0].name, "color",
     "Should have correct property name");
@@ -43,9 +45,9 @@ function* testCreateNewMulti(inspector, 
     "Should have correct property name");
   is(ruleEditor.rule.textProps[2].value, "center",
     "Should have correct property value");
 
   is(ruleEditor.rule.textProps[3].name, "border-color",
     "Should have correct property name");
   is(ruleEditor.rule.textProps[3].value, "green",
     "Should have correct property value");
-}
+});
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_07.js
+++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_07.js
@@ -18,38 +18,45 @@ const TEST_URI = `
   </style>
   <h1 id='testid'>Styled Node</h1>
 `;
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("#testid", inspector);
-  yield testModifyPropertyNameFilter(inspector, view);
-});
 
-function* testModifyPropertyNameFilter(inspector, view) {
+  info("Enter the test value in the search filter");
   yield setSearchFilter(view, SEARCH);
 
+  info("Focus the width property name");
   let ruleEditor = getRuleViewRuleEditor(view, 1);
   let rule = ruleEditor.rule;
   let propEditor = rule.textProps[0].editor;
-  let editor = yield focusEditableField(view, propEditor.nameSpan);
+  yield focusEditableField(view, propEditor.nameSpan);
 
   info("Check that the correct rules are visible");
   is(view.element.children.length, 2, "Should have 2 rules.");
   is(rule.selectorText, "#testid", "Second rule is #testid.");
   ok(!propEditor.container.classList.contains("ruleview-highlight"),
     "width text property is not highlighted.");
   ok(rule.textProps[1].editor.container.classList
     .contains("ruleview-highlight"),
     "height text property is correctly highlighted.");
 
-  let onBlur = once(editor.input, "blur");
-  let onModification = rule._applyingModifications;
+  info("Change the width property to margin-left");
   EventUtils.sendString("margin-left", view.styleWindow);
+
+  info("Submit the change");
+  let onRuleViewChanged = view.once("ruleview-changed");
   EventUtils.synthesizeKey("VK_RETURN", {});
-  yield onBlur;
-  yield onModification;
+  yield onRuleViewChanged;
 
   ok(propEditor.container.classList.contains("ruleview-highlight"),
     "margin-left text property is correctly highlighted.");
-}
+
+  // After pressing return on the property name, the value has been focused
+  // automatically. Blur it now and wait for the rule-view to refresh to avoid
+  // pending requests.
+  onRuleViewChanged = view.once("ruleview-changed");
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+  yield onRuleViewChanged;
+});
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_08.js
+++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_08.js
@@ -18,38 +18,36 @@ const TEST_URI = `
   </style>
   <h1 id='testid'>Styled Node</h1>
 `;
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("#testid", inspector);
-  yield testModifyPropertyValueFilter(inspector, view);
-});
 
-function* testModifyPropertyValueFilter(inspector, view) {
+  info("Enter the test value in the search filter");
   yield setSearchFilter(view, SEARCH);
 
+  info("Focus the height property value");
   let ruleEditor = getRuleViewRuleEditor(view, 1);
   let rule = ruleEditor.rule;
   let propEditor = rule.textProps[1].editor;
   let editor = yield focusEditableField(view, propEditor.valueSpan);
 
   info("Check that the correct rules are visible");
   is(view.element.children.length, 2, "Should have 2 rules.");
   is(rule.selectorText, "#testid", "Second rule is #testid.");
   ok(rule.textProps[0].editor.container.classList
     .contains("ruleview-highlight"),
     "width text property is correctly highlighted.");
   ok(!propEditor.container.classList.contains("ruleview-highlight"),
     "height text property is not highlighted.");
 
-  let onBlur = once(editor.input, "blur");
-  let onModification = rule._applyingModifications;
+  info("Change the height property value to 100%");
+  let onRuleViewChanged = view.once("ruleview-changed");
   EventUtils.sendString("100%", view.styleWindow);
   EventUtils.synthesizeKey("VK_RETURN", {});
-  yield onBlur;
-  yield onModification;
+  yield onRuleViewChanged;
 
   ok(propEditor.container.classList.contains("ruleview-highlight"),
     "height text property is correctly highlighted.");
-}
+});
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_09.js
+++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_09.js
@@ -18,22 +18,21 @@ const TEST_URI = `
   </style>
   <h1 id='testid'>Styled Node</h1>
 `;
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("#testid", inspector);
-  yield testNewPropertyFilter(inspector, view);
-});
 
-function* testNewPropertyFilter(inspector, view) {
+  info("Enter the test value in the search filter");
   yield setSearchFilter(view, SEARCH);
 
+  info("Start entering a new property in the rule");
   let ruleEditor = getRuleViewRuleEditor(view, 1);
   let rule = ruleEditor.rule;
   let editor = yield focusEditableField(view, ruleEditor.closeBrace);
 
   info("Check that the correct rules are visible");
   is(view.element.children.length, 2, "Should have 2 rules.");
   is(rule.selectorText, "#testid", "Second rule is #testid.");
   ok(rule.textProps[0].editor.container.classList
@@ -41,32 +40,33 @@ function* testNewPropertyFilter(inspecto
     "width text property is correctly highlighted.");
   ok(!rule.textProps[1].editor.container.classList
     .contains("ruleview-highlight"),
     "height text property is not highlighted.");
 
   info("Test creating a new property");
 
   info("Entering margin-left in the property name editor");
+  // Changing the value doesn't cause a rule-view refresh, no need to wait for
+  // ruleview-changed here.
   editor.input.value = "margin-left";
 
   info("Pressing return to commit and focus the new value field");
-  let onValueFocus = once(ruleEditor.element, "focus", true);
-  let onModifications = ruleEditor.rule._applyingModifications;
+  let onRuleViewChanged = view.once("ruleview-changed");
   EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
-  yield onValueFocus;
-  yield onModifications;
+  yield onRuleViewChanged;
 
   // Getting the new value editor after focus
   editor = inplaceEditor(view.styleDocument.activeElement);
   let propEditor = ruleEditor.rule.textProps[2].editor;
 
   info("Entering a value and bluring the field to expect a rule change");
+  onRuleViewChanged = view.once("ruleview-changed");
   editor.input.value = "100%";
-  let onBlur = once(editor.input, "blur");
-  onModifications = ruleEditor.rule._applyingModifications;
+  yield onRuleViewChanged;
+
+  onRuleViewChanged = view.once("ruleview-changed");
   editor.input.blur();
-  yield onBlur;
-  yield onModifications;
+  yield onRuleViewChanged;
 
   ok(propEditor.container.classList.contains("ruleview-highlight"),
     "margin-left text property is correctly highlighted.");
-}
+});
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -284,17 +284,17 @@ function assertHoverTooltipOn(tooltip, e
  * When a tooltip is closed, this ends up "commiting" the value changed within
  * the tooltip (e.g. the color in case of a colorpicker) which, in turn, ends up
  * setting the value of the corresponding css property in the rule-view.
  * Use this function to close the tooltip and make sure the test waits for the
  * ruleview-changed event.
  * @param {Tooltip} tooltip
  * @param {CSSRuleView} view
  */
-function* hideTooltipAndWaitForRuleviewChanged(tooltip, view) {
+function* hideTooltipAndWaitForRuleViewChanged(tooltip, view) {
   let onModified = view.once("ruleview-changed");
   tooltip.hide();
   yield onModified;
 }
 
 /**
  * Listen for a new tab to open and return a promise that resolves when one
  * does and completes the load event.
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -274,18 +274,18 @@ devtools.jar:
     skin/images/responsivemode/responsiveui-screenshot@2x.png (themes/images/responsivemode/responsiveui-screenshot@2x.png)
     skin/images/responsivemode/responsiveui-home.png (themes/images/responsivemode/responsiveui-home.png)
     skin/images/toggle-tools.png (themes/images/toggle-tools.png)
     skin/images/toggle-tools@2x.png (themes/images/toggle-tools@2x.png)
     skin/images/dock-bottom@2x.png (themes/images/dock-bottom@2x.png)
     skin/images/dock-bottom-minimize@2x.png (themes/images/dock-bottom-minimize@2x.png)
     skin/images/dock-bottom-maximize@2x.png (themes/images/dock-bottom-maximize@2x.png)
     skin/images/dock-side@2x.png (themes/images/dock-side@2x.png)
-*   skin/floating-scrollbars.css (themes/floating-scrollbars.css)
-    skin/floating-scrollbars-light.css (themes/floating-scrollbars-light.css)
+    skin/floating-scrollbars-dark-theme.css (themes/floating-scrollbars-dark-theme.css)
+    skin/floating-scrollbars-responsive-design.css (themes/floating-scrollbars-responsive-design.css)
     skin/inspector.css (themes/inspector.css)
     skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
     skin/images/emojis/emoji-command-pick.svg (themes/images/emojis/emoji-command-pick.svg)
     skin/images/emojis/emoji-tool-webconsole.svg (themes/images/emojis/emoji-tool-webconsole.svg)
     skin/images/emojis/emoji-tool-canvas.svg (themes/images/emojis/emoji-tool-canvas.svg)
     skin/images/emojis/emoji-tool-debugger.svg (themes/images/emojis/emoji-tool-debugger.svg)
     skin/images/emojis/emoji-tool-inspector.svg (themes/images/emojis/emoji-tool-inspector.svg)
     skin/images/emojis/emoji-tool-shadereditor.svg (themes/images/emojis/emoji-tool-shadereditor.svg)
--- a/devtools/client/responsivedesign/responsivedesign-child.js
+++ b/devtools/client/responsivedesign/responsivedesign-child.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var Ci = Components.interfaces;
 const gDeviceSizeWasPageSize = docShell.deviceSizeIsPageSize;
-const gFloatingScrollbarsStylesheet = Services.io.newURI("chrome://devtools/skin/floating-scrollbars.css", null, null);
+const gFloatingScrollbarsStylesheet = Services.io.newURI("chrome://devtools/skin/floating-scrollbars-responsive-design.css", null, null);
 var gRequiresFloatingScrollbars;
 
 var active = false;
 
 addMessageListener("ResponsiveMode:Start", startResponsiveMode);
 addMessageListener("ResponsiveMode:Stop", stopResponsiveMode);
 
 function startResponsiveMode({data:data}) {
--- a/devtools/client/shared/theme-switching.js
+++ b/devtools/client/shared/theme-switching.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 (function() {
-  const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-light.css";
+  const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-dark-theme.css";
   let documentElement = document.documentElement;
 
   let os;
   let platform = navigator.platform;
   if (platform.startsWith("Win")) {
     os = "win";
   } else if (platform.startsWith("Mac")) {
     os = "mac";
rename from devtools/client/themes/floating-scrollbars.css
rename to devtools/client/themes/floating-scrollbars-dark-theme.css
--- a/devtools/client/themes/floating-scrollbars.css
+++ b/devtools/client/themes/floating-scrollbars-dark-theme.css
@@ -1,49 +1,55 @@
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
-scrollbar {
+xul|scrollbar {
   -moz-appearance: none !important;
   position: relative;
   background-color: transparent;
   background-image: none;
   z-index: 2147483647;
   padding: 2px;
-%ifdef XP_MACOSX
-  border: 0px solid transparent;
-%endif
+}
+
+:root[platform="mac"] xul|scrollbar {
+  border: none;
 }
 
 /* Scrollbar code will reset the margin to the correct side depending on
    where layout actually puts the scrollbar */
-scrollbar[orient="vertical"] {
+xul|scrollbar[orient="vertical"] {
   margin-left: -10px;
   min-width: 10px;
   max-width: 10px;
 }
 
-scrollbar[orient="horizontal"] {
+xul|scrollbar[orient="horizontal"] {
   margin-top: -10px;
   min-height: 10px;
   max-height: 10px;
 }
 
-%ifdef XP_MACOSX
-slider {
+:root[platform="mac"] xul|slider {
   -moz-appearance: none !important;
 }
 
-thumb {
+:root[platform="mac"] xul|thumb {
   -moz-appearance: none !important;
-  background-color: rgba(0,0,0,0.2);
   border-radius: 3px;
 }
-%else
-scrollbar thumb {
+
+xul|scrollbar xul|thumb {
+  background-color: rgba(170,170,170,0.2) !important;
+}
+
+:root[platform="win"] xul|thumb,
+:root[platform="linux"] xul|thumb {
   -moz-appearance: none !important;
   border-width: 0px !important;
-  background-color: rgba(170,170,170,0.2) !important;
   border-radius: 3px !important;
 }
-scrollbar scrollbarbutton, scrollbar gripper {
+
+:root[platform="win"] xul|scrollbar xul|scrollbarbutton,
+:root[platform="linux"] xul|scrollbar xul|scrollbarbutton,
+:root[platform="win"] xul|scrollbar xul|gripper,
+:root[platform="linux"] xul|scrollbar xul|gripper {
   display: none;
 }
-%endif
deleted file mode 100644
--- a/devtools/client/themes/floating-scrollbars-light.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/* vim:set ts=2 sw=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/. */
-
-@import url("floating-scrollbars.css");
-
-scrollbar thumb {
-  background-color: rgba(170,170,170,0.2) !important;
-}
copy from devtools/client/themes/floating-scrollbars.css
copy to devtools/client/themes/floating-scrollbars-responsive-design.css
--- a/devtools/client/themes/floating-scrollbars.css
+++ b/devtools/client/themes/floating-scrollbars-responsive-design.css
@@ -2,48 +2,41 @@
 
 scrollbar {
   -moz-appearance: none !important;
   position: relative;
   background-color: transparent;
   background-image: none;
   z-index: 2147483647;
   padding: 2px;
-%ifdef XP_MACOSX
-  border: 0px solid transparent;
-%endif
+  border: none;
 }
 
 /* Scrollbar code will reset the margin to the correct side depending on
    where layout actually puts the scrollbar */
 scrollbar[orient="vertical"] {
   margin-left: -10px;
   min-width: 10px;
   max-width: 10px;
 }
 
 scrollbar[orient="horizontal"] {
   margin-top: -10px;
   min-height: 10px;
   max-height: 10px;
 }
 
-%ifdef XP_MACOSX
 slider {
   -moz-appearance: none !important;
 }
 
 thumb {
   -moz-appearance: none !important;
   background-color: rgba(0,0,0,0.2);
   border-radius: 3px;
-}
-%else
-scrollbar thumb {
-  -moz-appearance: none !important;
   border-width: 0px !important;
-  background-color: rgba(170,170,170,0.2) !important;
   border-radius: 3px !important;
 }
-scrollbar scrollbarbutton, scrollbar gripper {
+
+scrollbar scrollbarbutton,
+scrollbar gripper {
   display: none;
 }
-%endif
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -435,17 +435,17 @@ DebuggerClient.prototype = {
     if (this._clients.has(aTabActor)) {
       let cachedTab = this._clients.get(aTabActor);
       let cachedResponse = {
         cacheDisabled: cachedTab.cacheDisabled,
         javascriptEnabled: cachedTab.javascriptEnabled,
         traits: cachedTab.traits,
       };
       DevToolsUtils.executeSoon(() => aOnResponse(cachedResponse, cachedTab));
-      return;
+      return promise.resolve([cachedResponse, cachedTab]);
     }
 
     let packet = {
       to: aTabActor,
       type: "attach"
     };
     return this.request(packet).then(aResponse => {
       let tabClient;
@@ -456,22 +456,23 @@ DebuggerClient.prototype = {
       aOnResponse(aResponse, tabClient);
       return [aResponse, tabClient];
     });
   },
 
   attachWorker: function DC_attachWorker(aWorkerActor, aOnResponse = noop) {
     let workerClient = this._clients.get(aWorkerActor);
     if (workerClient !== undefined) {
-      DevToolsUtils.executeSoon(() => aOnResponse({
+      let response = {
         from: workerClient.actor,
         type: "attached",
         url: workerClient.url
-      }, workerClient));
-      return;
+      };
+      DevToolsUtils.executeSoon(() => aOnResponse(response, workerClient));
+      return promise.resolve([response, workerClient]);
     }
 
     return this.request({ to: aWorkerActor, type: "attach" }).then(aResponse => {
       if (aResponse.error) {
         aOnResponse(aResponse, null);
         return [aResponse, null];
       }
 
@@ -551,18 +552,19 @@ DebuggerClient.prototype = {
    *        Called with the response packet and a ThreadClient
    *        (which will be undefined on error).
    * @param object aOptions
    *        Configuration options.
    *        - useSourceMaps: whether to use source maps or not.
    */
   attachThread: function (aThreadActor, aOnResponse = noop, aOptions={}) {
     if (this._clients.has(aThreadActor)) {
-      DevToolsUtils.executeSoon(() => aOnResponse({}, this._clients.get(aThreadActor)));
-      return;
+      let client = this._clients.get(aThreadActor);
+      DevToolsUtils.executeSoon(() => aOnResponse({}, client));
+      return promise.resolve([{}, client]);
     }
 
     let packet = {
       to: aThreadActor,
       type: "attach",
       options: aOptions
     };
     return this.request(packet).then(aResponse => {
@@ -581,18 +583,19 @@ DebuggerClient.prototype = {
    * @param string aTraceActor
    *        The actor ID for the tracer to attach.
    * @param function aOnResponse
    *        Called with the response packet and a TraceClient
    *        (which will be undefined on error).
    */
   attachTracer: function (aTraceActor, aOnResponse = noop) {
     if (this._clients.has(aTraceActor)) {
-      DevToolsUtils.executeSoon(() => aOnResponse({}, this._clients.get(aTraceActor)));
-      return;
+      let client = this._clients.get(aTraceActor);
+      DevToolsUtils.executeSoon(() => aOnResponse({}, client));
+      return promise.resolve([{}, client]);
     }
 
     let packet = {
       to: aTraceActor,
       type: "attach"
     };
     return this.request(packet).then(aResponse => {
       if (!aResponse.error) {
@@ -1256,17 +1259,17 @@ TabClient.prototype = {
    *        - useSourceMaps: whether to use source maps or not.
    * @param function aOnResponse
    *        Called with the response packet and a ThreadClient
    *        (which will be undefined on error).
    */
   attachThread: function(aOptions={}, aOnResponse = noop) {
     if (this.thread) {
       DevToolsUtils.executeSoon(() => aOnResponse({}, this.thread));
-      return;
+      return promise.resolve([{}, this.thread]);
     }
 
     let packet = {
       to: this._threadActor,
       type: "attach",
       options: aOptions
     };
     return this.request(packet).then(aResponse => {
@@ -1822,21 +1825,20 @@ ThreadClient.prototype = {
                                aIgnoreCaughtExceptions,
                                aOnResponse = noop) {
     this._pauseOnExceptions = aPauseOnExceptions;
     this._ignoreCaughtExceptions = aIgnoreCaughtExceptions;
 
     // If the debuggee is paused, we have to send the flag via a reconfigure
     // request.
     if (this.paused) {
-      this.reconfigure({
+      return this.reconfigure({
         pauseOnExceptions: aPauseOnExceptions,
         ignoreCaughtExceptions: aIgnoreCaughtExceptions
       }, aOnResponse);
-      return;
     }
     // Otherwise send the flag using a standard resume request.
     return this.interrupt(aResponse => {
       if (aResponse.error) {
         // Can't continue if pausing failed.
         aOnResponse(aResponse);
         return aResponse;
       }
--- a/mobile/android/app/base/build.gradle
+++ b/mobile/android/app/base/build.gradle
@@ -117,16 +117,20 @@ dependencies {
     }
 
     if (mozconfig.substs.MOZ_ANDROID_GCM) {
         compile 'com.google.android.gms:play-services-basement:8.1.0'
         compile 'com.google.android.gms:play-services-base:8.1.0'
         compile 'com.google.android.gms:play-services-gcm:8.1.0'
     }
 
+    // Gradle based builds include LeakCanary. Mach based builds only include the no-op version of
+    // this library.
+    compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
+
     compile project(':thirdparty')
 
     testCompile 'junit:junit:4.12'
     testCompile 'org.robolectric:robolectric:3.0'
     testCompile 'org.simpleframework:simple-http:6.0.1'
     testCompile 'org.mockito:mockito-core:1.10.19'
 }
 
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -63,25 +63,25 @@ import org.mozilla.gecko.tabs.TabsPanel;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.toolbar.BrowserToolbar;
 import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
 import org.mozilla.gecko.toolbar.ToolbarProgressView;
 import org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.Experiments;
 import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.MenuUtils;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.PrefUtils;
-import org.mozilla.gecko.util.ScreenshotObserver;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UIAsyncTask;
 import org.mozilla.gecko.widget.AnchoredPopup;
 import org.mozilla.gecko.widget.ButtonToast;
 import org.mozilla.gecko.widget.GeckoActionProvider;
 
 import android.app.Activity;
@@ -3045,16 +3045,18 @@ public class BrowserApp extends GeckoApp
 
         Tab tab = Tabs.getInstance().getSelectedTab();
         final MenuItem bookmark = aMenu.findItem(R.id.bookmark);
         final MenuItem reader = aMenu.findItem(R.id.reading_list);
         final MenuItem back = aMenu.findItem(R.id.back);
         final MenuItem forward = aMenu.findItem(R.id.forward);
         final MenuItem share = aMenu.findItem(R.id.share);
         final MenuItem quickShare = aMenu.findItem(R.id.quickshare);
+        final MenuItem bookmarksList = aMenu.findItem(R.id.bookmarks_list);
+        final MenuItem historyList = aMenu.findItem(R.id.history_list);
         final MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
         final MenuItem print = aMenu.findItem(R.id.print);
         final MenuItem charEncoding = aMenu.findItem(R.id.char_encoding);
         final MenuItem findInPage = aMenu.findItem(R.id.find_in_page);
         final MenuItem desktopMode = aMenu.findItem(R.id.desktop_mode);
         final MenuItem enterGuestMode = aMenu.findItem(R.id.new_guest_session);
         final MenuItem exitGuestMode = aMenu.findItem(R.id.exit_guest_session);
 
@@ -3254,16 +3256,21 @@ public class BrowserApp extends GeckoApp
         if (!Restrictions.isAllowed(this, Restrictable.GUEST_BROWSING)) {
             MenuUtils.safeSetVisible(aMenu, R.id.new_guest_session, false);
         }
 
         if (!Restrictions.isAllowed(this, Restrictable.INSTALL_EXTENSION)) {
             MenuUtils.safeSetVisible(aMenu, R.id.addons, false);
         }
 
+        if (!SwitchBoard.isInExperiment(this, Experiments.BOOKMARKS_HISTORY_MENU)) {
+            bookmarksList.setVisible(false);
+            historyList.setVisible(false);
+        }
+
         return true;
     }
 
     private int resolveBookmarkIconID(final boolean isBookmark) {
         if (isBookmark) {
             return R.drawable.ic_menu_bookmark_remove;
         } else {
             return R.drawable.ic_menu_bookmark_add;
@@ -3382,16 +3389,30 @@ public class BrowserApp extends GeckoApp
 
         if (itemId == R.id.forward) {
             tab = Tabs.getInstance().getSelectedTab();
             if (tab != null)
                 tab.doForward();
             return true;
         }
 
+        if (itemId == R.id.bookmarks_list) {
+            final String url = AboutPages.getURLForBuiltinPanelType(PanelType.BOOKMARKS);
+            Tabs.getInstance().loadUrl(url);
+            Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, Experiments.BOOKMARKS_HISTORY_MENU);
+            return true;
+        }
+
+        if (itemId == R.id.history_list) {
+            final String url = AboutPages.getURLForBuiltinPanelType(PanelType.HISTORY);
+            Tabs.getInstance().loadUrl(url);
+            Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, Experiments.BOOKMARKS_HISTORY_MENU);
+            return true;
+        }
+
         if (itemId == R.id.save_as_pdf) {
             Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "pdf");
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SaveAs:PDF", null));
             return true;
         }
 
         if (itemId == R.id.print) {
             Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "print");
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -19,16 +19,18 @@ import org.mozilla.gecko.util.ThreadUtil
 
 import android.app.Application;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.os.SystemClock;
 import android.util.Log;
 
+import com.squareup.leakcanary.LeakCanary;
+
 import java.io.File;
 
 public class GeckoApplication extends Application 
     implements ContextGetter {
     private static final String LOG_TAG = "GeckoApplication";
 
     private static volatile GeckoApplication instance;
 
@@ -122,16 +124,18 @@ public class GeckoApplication extends Ap
 
         mInBackground = false;
     }
 
     @Override
     public void onCreate() {
         Log.i(LOG_TAG, "zerdatime " + SystemClock.uptimeMillis() + " - Fennec application start");
 
+        LeakCanary.install(this);
+
         final Context context = getApplicationContext();
         HardwareUtils.init(context);
         Clipboard.init(context);
         FilePicker.init(context);
         DownloadsIntegration.init();
         HomePanelsManager.getInstance().init(context);
 
         // This getInstance call will force initialization of the NotificationHelper, but does nothing with the result
rename from mobile/android/base/java/org/mozilla/gecko/util/ScreenshotObserver.java
rename to mobile/android/base/java/org/mozilla/gecko/ScreenshotObserver.java
--- a/mobile/android/base/java/org/mozilla/gecko/util/ScreenshotObserver.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ScreenshotObserver.java
@@ -1,18 +1,20 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
-package org.mozilla.gecko.util;
+package org.mozilla.gecko;
 
 import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.util.ThreadUtils;
 
+import android.Manifest;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.MediaStore;
 import android.util.Log;
 
@@ -44,36 +46,56 @@ public class ScreenshotObserver {
     private String[] mediaProjections = new String[] {
                     MediaStore.Images.ImageColumns.DATA,
                     MediaStore.Images.ImageColumns.DISPLAY_NAME,
                     MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
                     MediaStore.Images.ImageColumns.DATE_TAKEN,
                     MediaStore.Images.ImageColumns.TITLE
     };
 
+    /**
+     * Start ScreenshotObserver if this device is supported and all required runtime permissions
+     * have been granted by the user. Calling this method will not prompt for permissions.
+     */
     public void start() {
         if (!Versions.feature14Plus) {
             return;
         }
 
-        try {
-            if (mediaObserver == null) {
-                mediaObserver = new MediaObserver();
-                context.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, mediaObserver);
+        Permissions.from(context)
+                   .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                   .doNotPrompt()
+                   .run(startObserverRunnable());
+    }
+
+    private Runnable startObserverRunnable() {
+        return new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    if (mediaObserver == null) {
+                        mediaObserver = new MediaObserver();
+                        context.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, mediaObserver);
+                    }
+                } catch (Exception e) {
+                    Log.e(LOGTAG, "Failure to start watching media: ", e);
+                }
             }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Failure to start watching media: ", e);
-        }
+        };
     }
 
     public void stop() {
         if (!Versions.feature14Plus) {
             return;
         }
 
+        if (mediaObserver == null) {
+            return;
+        }
+
         try {
             context.getContentResolver().unregisterContentObserver(mediaObserver);
             mediaObserver = null;
         } catch (Exception e) {
             Log.e(LOGTAG, "Failure to stop watching media: ", e);
         }
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomePager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomePager.java
@@ -12,16 +12,17 @@ import java.util.List;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.home.HomeAdapter.OnAddPanelListener;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
+import org.mozilla.gecko.util.Experiments;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
@@ -282,16 +283,19 @@ public class HomePager extends ViewPager
     }
 
     /**
      * Shows a home panel. If the given panelId is null,
      * the default panel will be shown. No action will be taken if:
      *  * HomePager has not loaded yet
      *  * Panel with the given panelId cannot be found
      *
+     * If you're trying to open a built-in panel, consider loading the panel url directly with
+     * {@link org.mozilla.gecko.AboutPages#getURLForBuiltinPanelType(HomeConfig.PanelType)}.
+     *
      * @param panelId of the home panel to be shown.
      */
     public void showPanel(String panelId) {
         if (!mVisible) {
             return;
         }
 
         switch (mLoadState) {
@@ -520,11 +524,12 @@ public class HomePager extends ViewPager
     /**
      * Stop the current panel telemetry session if one exists.
      */
     private void stopCurrentPanelTelemetrySession() {
         if (mCurrentPanelSession != null) {
             Telemetry.stopUISession(mCurrentPanelSession, mCurrentPanelSessionSuffix);
             mCurrentPanelSession = null;
             mCurrentPanelSessionSuffix = null;
+            Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, Experiments.BOOKMARKS_HISTORY_MENU);
         }
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/util/Experiments.java
@@ -0,0 +1,13 @@
+/* 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/. */
+
+package org.mozilla.gecko.util;
+
+/**
+ * This class should reflect the experiment names found in the Switchboard experiments config here:
+ * https://github.com/mozilla-services/switchboard-experiments
+ */
+public class Experiments {
+    public static final String BOOKMARKS_HISTORY_MENU = "bookmark-history-menu";
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -92,16 +92,17 @@ gujar.sources += ['java/org/mozilla/geck
     'util/ActivityResultHandler.java',
     'util/ActivityResultHandlerMap.java',
     'util/ActivityUtils.java',
     'util/BundleEventListener.java',
     'util/Clipboard.java',
     'util/ColorUtils.java',
     'util/DrawableUtil.java',
     'util/EventCallback.java',
+    'util/Experiments.java',
     'util/FileUtils.java',
     'util/FloatUtils.java',
     'util/GamepadUtils.java',
     'util/GeckoBackgroundThread.java',
     'util/GeckoEventListener.java',
     'util/GeckoJarReader.java',
     'util/GeckoRequest.java',
     'util/HardwareCodecCapabilityUtils.java',
@@ -114,17 +115,16 @@ gujar.sources += ['java/org/mozilla/geck
     'util/MenuUtils.java',
     'util/NativeEventListener.java',
     'util/NativeJSContainer.java',
     'util/NativeJSObject.java',
     'util/NonEvictingLruCache.java',
     'util/PrefUtils.java',
     'util/ProxySelector.java',
     'util/RawResource.java',
-    'util/ScreenshotObserver.java',
     'util/StringUtils.java',
     'util/ThreadUtils.java',
     'util/UIAsyncTask.java',
     'util/WeakReferenceHandler.java',
     'util/WebActivityMapper.java',
     'util/WindowUtils.java',
 ]]
 gujar.extra_jars = [
@@ -501,16 +501,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'Restarter.java',
     'Restrictions.java',
     'restrictions/DefaultConfiguration.java',
     'restrictions/GuestProfileConfiguration.java',
     'restrictions/Restrictable.java',
     'restrictions/RestrictedProfileConfiguration.java',
     'restrictions/RestrictionConfiguration.java',
     'restrictions/RestrictionProvider.java',
+    'ScreenshotObserver.java',
     'ServiceNotificationClient.java',
     'SessionParser.java',
     'SharedPreferencesHelper.java',
     'SiteIdentity.java',
     'SmsManager.java',
     'SnackbarHelper.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
@@ -773,16 +774,18 @@ gtjar.sources += [ thirdparty_source_dir
     'com/nineoldandroids/util/Property.java',
     'com/nineoldandroids/util/ReflectiveProperty.java',
     'com/nineoldandroids/view/animation/AnimatorProxy.java',
     'com/nineoldandroids/view/ViewHelper.java',
     'com/nineoldandroids/view/ViewPropertyAnimator.java',
     'com/nineoldandroids/view/ViewPropertyAnimatorHC.java',
     'com/nineoldandroids/view/ViewPropertyAnimatorICS.java',
     'com/nineoldandroids/view/ViewPropertyAnimatorPreHC.java',
+    'com/squareup/leakcanary/LeakCanary.java',
+    'com/squareup/leakcanary/RefWatcher.java',
     'com/squareup/picasso/Action.java',
     'com/squareup/picasso/AssetBitmapHunter.java',
     'com/squareup/picasso/BitmapHunter.java',
     'com/squareup/picasso/Cache.java',
     'com/squareup/picasso/Callback.java',
     'com/squareup/picasso/ContactsPhotoBitmapHunter.java',
     'com/squareup/picasso/ContentStreamBitmapHunter.java',
     'com/squareup/picasso/DeferredRequestCreator.java',
--- a/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml
@@ -49,16 +49,22 @@
           android:showAsAction="collapseActionView"/>
 
     <item android:id="@+id/new_tab"
           android:title="@string/new_tab"/>
 
     <item android:id="@+id/new_private_tab"
           android:title="@string/new_private_tab"/>
 
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
     <item android:id="@+id/find_in_page"
           android:title="@string/find_in_page" />
 
     <item android:id="@+id/desktop_mode"
           android:title="@string/desktop_mode"
           android:checkable="true" />
 
     <item android:id="@+id/page"
--- a/mobile/android/base/resources/menu-v11/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu-v11/browser_app_menu.xml
@@ -49,16 +49,22 @@
           android:showAsAction="collapseActionView"/>
 
     <item android:id="@+id/new_tab"
           android:title="@string/new_tab"/>
 
     <item android:id="@+id/new_private_tab"
           android:title="@string/new_private_tab"/>
 
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
     <item android:id="@+id/find_in_page"
           android:title="@string/find_in_page" />
 
     <item android:id="@+id/desktop_mode"
           android:title="@string/desktop_mode"
           android:checkable="true" />
 
     <item android:id="@+id/page"
--- a/mobile/android/base/resources/menu-xlarge-v11/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu-xlarge-v11/browser_app_menu.xml
@@ -49,16 +49,22 @@
           android:showAsAction="collapseActionView"/>
 
     <item android:id="@+id/new_tab"
           android:title="@string/new_tab"/>
 
     <item android:id="@+id/new_private_tab"
           android:title="@string/new_private_tab"/>
 
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
     <item android:id="@+id/find_in_page"
           android:title="@string/find_in_page" />
 
     <item android:id="@+id/desktop_mode"
           android:title="@string/desktop_mode"
           android:checkable="true" />
 
 
--- a/mobile/android/base/resources/menu/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu/browser_app_menu.xml
@@ -34,16 +34,22 @@
 
     <item android:id="@+id/find_in_page"
           android:title="@string/find_in_page" />
 
     <item android:id="@+id/desktop_mode"
           android:title="@string/desktop_mode"
           android:checkable="true" />
 
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
     <item android:id="@+id/page"
           android:title="@string/page">
 
         <menu>
             <item android:id="@+id/subscribe"
                   android:title="@string/contextmenu_subscribe"/>
 
             <item android:id="@+id/save_as_pdf"
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/proguard/leakcanary-keeps.cfg
@@ -0,0 +1,7 @@
+# LeakCanary
+-keep class org.eclipse.mat.** { *; }
+-keep class com.squareup.leakcanary.** { *; }
+-keep class com.squareup.haha.** { *; }
+
+# With LeakCanary 1.4-beta1 this creates a pile of warnings
+-dontwarn com.squareup.haha.**
--- a/mobile/android/config/proguard/proguard.cfg
+++ b/mobile/android/config/proguard/proguard.cfg
@@ -242,8 +242,11 @@
 
 -include "play-services-keeps.cfg"
 
 # Don't print spurious warnings from the support library.
 # See: http://stackoverflow.com/questions/22441366/note-android-support-v4-text-icucompatics-cant-find-dynamically-referenced-cl
 -dontnote android.support.**
 
 -include "adjust-keeps.cfg"
+
+-include "leakcanary-keeps.cfg"
+
--- a/mobile/android/thirdparty/build.gradle
+++ b/mobile/android/thirdparty/build.gradle
@@ -23,16 +23,20 @@ android {
     sourceSets {
         main {
             manifest.srcFile 'AndroidManifest.xml'
             java {
                 srcDir '.'
                 if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
                     exclude 'com/adjust/**'
                 }
+
+                // Exclude LeakCanary: It will be added again via a gradle dependency. This version
+                // here is only the no-op library for mach-based builds.
+                exclude 'com/squareup/leakcanary/**'
             }
         }
     }
 }
 
 dependencies {
     compile 'com.android.support:support-v4:23.0.1'
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/thirdparty/com/squareup/leakcanary/LeakCanary.java
@@ -0,0 +1,21 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package com.squareup.leakcanary;
+
+import android.app.Application;
+
+/**
+ * A no-op version of {@link LeakCanary} that can be used in release builds.
+ */
+public final class LeakCanary {
+    public static RefWatcher install(Application application) {
+        return RefWatcher.DISABLED;
+    }
+
+    private LeakCanary() {
+        throw new AssertionError();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/thirdparty/com/squareup/leakcanary/RefWatcher.java
@@ -0,0 +1,20 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package com.squareup.leakcanary;
+
+/**
+ * No-op implementation of {@link RefWatcher} for release builds. Please use {@link
+ * RefWatcher#DISABLED}.
+ */
+public final class RefWatcher {
+    public static final RefWatcher DISABLED = new RefWatcher();
+
+    private RefWatcher() {}
+
+    public void watch(Object watchedReference) {}
+
+    public void watch(Object watchedReference, String referenceName) {}
+}
--- a/services/sync/moz.build
+++ b/services/sync/moz.build
@@ -37,17 +37,17 @@ EXTRA_JS_MODULES['services-sync'] += [
     'modules/util.js',
 ]
 
 EXTRA_PP_JS_MODULES['services-sync'] += [
     'modules/constants.js',
 ]
 
 # Definitions used by constants.js
-DEFINES['weave_version'] = '1.48.0'
+DEFINES['weave_version'] = '1.49.0'
 DEFINES['weave_id'] = '{340c2bbc-ce74-4362-90b5-7c26312808ef}'
 
 EXTRA_JS_MODULES['services-sync'].engines += [
     'modules/engines/addons.js',
     'modules/engines/bookmarks.js',
     'modules/engines/clients.js',
     'modules/engines/forms.js',
     'modules/engines/history.js',
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8289,72 +8289,72 @@
   },
   "LOOP_CLIENT_CALL_URL_SHARED": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "Stores 1 every time the URL is copied or shared."
   },
   "LOOP_TWO_WAY_MEDIA_CONN_LENGTH_1": {
     "alert_emails": ["firefox-dev@mozilla.org", "dmose@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "50",
     "kind": "enumerated",
     "n_values": 8,
     "releaseChannelCollection": "opt-out",
     "description": "Connection length for bi-directionally connected media (0=SHORTER_THAN_10S, 1=BETWEEN_10S_AND_30S, 2=BETWEEN_30S_AND_5M, 3=MORE_THAN_5M)"
   },
   "LOOP_SHARING_STATE_CHANGE_1": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "48",
     "kind": "enumerated",
     "n_values": 8,
     "releaseChannelCollection": "opt-out",
     "description": "Number of times the sharing feature has been enabled and disabled (0=WINDOW_ENABLED, 1=WINDOW_DISABLED, 2=BROWSER_ENABLED, 3=BROWSER_DISABLED)"
   },
   "LOOP_SHARING_ROOM_URL": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "50",
     "kind": "enumerated",
     "n_values": 8,
     "releaseChannelCollection": "opt-out",
     "description": "Number of times a room URL is shared (0=COPY_FROM_PANEL, 1=COPY_FROM_CONVERSATION, 2=EMAIL_FROM_CALLFAILED, 3=EMAIL_FROM_CONVERSATION, 4=FACEBOOK_FROM_CONVERSATION)"
   },
   "LOOP_ROOM_CREATE": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "50",
     "kind": "enumerated",
     "n_values": 4,
     "releaseChannelCollection": "opt-out",
     "description": "Number of times a room create action is performed (0=CREATE_SUCCESS, 1=CREATE_FAIL)"
   },
   "LOOP_ROOM_DELETE": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "50",
     "kind": "enumerated",
     "n_values": 4,
     "releaseChannelCollection": "opt-out",
     "description": "Number of times a room delete action is performed (0=DELETE_SUCCESS, 2=DELETE_FAIL)"
   },
   "LOOP_ROOM_CONTEXT_ADD": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "48",
     "kind": "enumerated",
     "n_values": 8,
     "releaseChannelCollection": "opt-out",
     "description": "Number of times a room context action is performed (0=ADD_FROM_PANEL, 1=ADD_FROM_CONVERSATION)"
   },
   "LOOP_ROOM_CONTEXT_CLICK": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "48",
     "kind": "count",
     "releaseChannelCollection": "opt-out",
     "description": "Number times room context is clicked to visit the attached URL"
   },
   "LOOP_ROOM_SESSION_WITHCHAT": {
     "alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
-    "expires_in_version": "47",
+    "expires_in_version": "50",
     "kind": "count",
     "releaseChannelCollection": "opt-out",
     "description": "Number of sessions where at least one chat message was exchanged"
   },
   "E10S_STATUS": {
     "alert_emails": ["firefox-dev@mozilla.org"],
     "expires_in_version": "never",
     "kind": "enumerated",
--- a/toolkit/mozapps/extensions/test/browser/browser-common.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser-common.ini
@@ -5,16 +5,17 @@ support-files =
 [browser_about.js]
 skip-if = os == 'linux' || os == 'win' # bug 632290
 [browser_bug523784.js]
 [browser_bug557943.js]
 [browser_bug562797.js]
 skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
 [browser_bug562854.js]
 [browser_bug562890.js]
+skip-if = os == 'win' && !debug # Disabled on Windows opt/PGO builds due to intermittent failures (bug 1135866)
 [browser_bug562899.js]
 skip-if = buildapp == 'mulet'
 [browser_bug562992.js]
 [browser_bug567127.js]
 [browser_bug567137.js]
 [browser_bug570760.js]
 [browser_bug572561.js]
 [browser_bug577990.js]
@@ -53,16 +54,17 @@ skip-if = e10s # TypeError: Promise.defe
 [browser_manualupdates.js]
 [browser_globalwarnings.js]
 [browser_globalinformations.js]
 [browser_eula.js]
 skip-if = buildapp == 'mulet'
 [browser_updateid.js]
 [browser_purchase.js]
 [browser_openDialog.js]
+skip-if = os == 'win' && !debug # Disabled on Windows opt/PGO builds due to intermittent failures (bug 1135866)
 [browser_types.js]
 [browser_inlinesettings.js]
 [browser_inlinesettings_custom.js]
 [browser_inlinesettings_info.js]
 [browser_tabsettings.js]
 [browser_pluginprefs.js]
 skip-if = buildapp == 'mulet'
 [browser_CTP_plugins.js]
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -17,17 +17,17 @@ namespace mozilla {
 /**
  * A module implements one or more XPCOM components. This structure is used
  * for both binary and script modules, but the registration members
  * (cids/contractids/categoryentries) are unused for modules which are loaded
  * via a module loader.
  */
 struct Module
 {
-  static const unsigned int kVersion = 46;
+  static const unsigned int kVersion = 47;
 
   struct CIDEntry;
 
   typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
     const Module& module, const CIDEntry& entry);
 
   typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
                                          const nsIID& aIID,