Merge f-t to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 16 Mar 2014 21:48:47 -0700
changeset 191043 25cfa01ba05440e5abe1b06f4b73cb104e224a82
parent 191036 5870a4beef4d29fae596c5abb71cedafea1753b7 (current diff)
parent 191042 6ce4ff67f3f94922907b88a5d01af6ef82f45b98 (diff)
child 191051 0accce5c96a567094af21d8b3597e12af2e3ad06
child 191060 309dd572373ba60e9878f9f89a60da5aa34784d3
child 191070 8e798988fe2c98bb559dd1c8e07f1de11cf8ff7e
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
25cfa01ba054 / 30.0a1 / 20140317030202 / files
nightly linux64
25cfa01ba054 / 30.0a1 / 20140317030202 / files
nightly mac
25cfa01ba054 / 30.0a1 / 20140317030202 / files
nightly win32
25cfa01ba054 / 30.0a1 / 20140317030202 / files
nightly win64
25cfa01ba054 / 30.0a1 / 20140317030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge f-t to m-c
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -113,34 +113,35 @@ tabbrowser {
 }
 
 .tabbrowser-tab:not([pinned]):not([fadein]) {
   max-width: 0.1px;
   min-width: 0.1px;
   visibility: hidden;
 }
 
+.tab-close-button,
 .tab-background {
   /* Explicitly set the visibility to override the value (collapsed)
    * we inherit from #TabsToolbar[collapsed] upon opening a browser window. */
   visibility: visible;
   /* The transition is only delayed for opening tabs. */
   transition: visibility 0ms 25ms;
 }
 
+.tab-close-button:not([fadein]):not([pinned]),
 .tab-background:not([fadein]):not([pinned]) {
   visibility: hidden;
   /* Closing tabs are hidden without a delay. */
   transition-delay: 0ms;
 }
 
 .tab-throbber:not([fadein]):not([pinned]),
 .tab-label:not([fadein]):not([pinned]),
-.tab-icon-image:not([fadein]):not([pinned]),
-.tab-close-button:not([fadein]):not([pinned]) {
+.tab-icon-image:not([fadein]):not([pinned]) {
   display: none;
 }
 
 .tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
   position: fixed !important;
   display: block; /* position:fixed already does this (bug 579776), but let's be explicit */
 }
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1158,24 +1158,25 @@
                          tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
 #endif
    </toolbar>
   </vbox>
 
   <svg:svg height="0">
 #include tab-shape.inc.svg
 
-#ifndef XP_MACOSX
-    <svg:clipPath id="keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
+#ifndef XP_UNIX
+    <svg:clipPath id="windows-keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
       <svg:path d="m 0,0 c .3,.25 .3,.75, 0,1 l 1,0 0,-1 z"/>
     </svg:clipPath>
-    <svg:clipPath id="urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
+    <svg:clipPath id="windows-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
       <svg:path d="m 0,-5 l 0,7.8 c 2.5,3.2 4,6.2 4,10.2 c 0,4 -1.5,7 -4,10 l 0,22l10000,0 l 0,-50 l -10000,0 z"/>
     </svg:clipPath>
-#else
+#endif
+#ifdef XP_MACOSX
     <svg:clipPath id="osx-keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
       <svg:path d="M 0,0 C 0.15,0.12 0.25,0.3 0.25,0.5 0.25,0.7 0.15,0.88 0,1 L 1,1 1,0 0,0 z"/>
     </svg:clipPath>
     <svg:clipPath id="osx-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
       <svg:path d="m -3,-10 l -0.1,7.7 c 6.6,1.8 8.8,7.6 8.8,12.5 c 0,5 -1.9,11.5 -8.25,13.25 l 0.05,25.75 l 10000,0 l 0,-55 l -10000,-4.2 z"/>
     </svg:clipPath>
 #endif
   </svg:svg>
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -326,16 +326,17 @@ const PanelUI = {
       tempPanel.setAttribute("class", "cui-widget-panel");
       tempPanel.setAttribute("context", "");
       document.getElementById(CustomizableUI.AREA_NAVBAR).appendChild(tempPanel);
       // If the view has a footer, set a convenience class on the panel.
       tempPanel.classList.toggle("cui-widget-panelWithFooter",
                                  viewNode.querySelector(".panel-subview-footer"));
 
       let multiView = document.createElement("panelmultiview");
+      multiView.setAttribute("nosubviews", "true");
       tempPanel.appendChild(multiView);
       multiView.setAttribute("mainViewIsSubView", "true");
       multiView.setMainView(viewNode);
       viewNode.classList.add("cui-widget-panelview");
       CustomizableUI.addPanelCloseListeners(tempPanel);
 
       let panelRemover = function() {
         tempPanel.removeEventListener("popuphidden", panelRemover);
--- a/browser/themes/linux/browser-lightweightTheme.css
+++ b/browser/themes/linux/browser-lightweightTheme.css
@@ -12,18 +12,20 @@
 
 /* Lightweight theme on tabs */
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
+  background-repeat: repeat-x, no-repeat;
 }
 
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
+  background-repeat: repeat-x, repeat-x, no-repeat;
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -11,17 +11,17 @@
 @namespace svg url("http://www.w3.org/2000/svg");
 
 %include ../shared/browser.inc
 %include linuxShared.inc
 %filter substitution
 
 %define forwardTransitionLength 150ms
 %define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-container
-%define conditionalForwardWithUrlbarWidth 27
+%define conditionalForwardWithUrlbarWidth 40
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
 }
@@ -648,108 +648,31 @@ toolbarbutton[sdk-button="true"][cui-are
   background-size: 1px 18px;
   box-shadow: 0 0 0 1px hsla(0,0%,100%,.2);
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   -moz-margin-start: -4px;
 }
 
-#back-button {
-  padding-top: 3px;
-  padding-bottom: 3px;
-  -moz-padding-start: 5px;
-  -moz-padding-end: 0;
-  position: relative;
-  z-index: 1;
-  border-radius: 0 10000px 10000px 0;
-}
-
-#back-button:-moz-locale-dir(rtl) {
-  border-radius: 10000px 0 0 10000px;
-}
-
-#back-button > menupopup {
-  margin-top: -1px;
+#forward-button[disabled] {
+  transform: scale(0);
+  opacity: 0;
+  pointer-events: none;
 }
 
-#back-button > .toolbarbutton-icon {
-  border-radius: 10000px;
-  padding: 5px;
-  margin-top: -5px;
-  margin-bottom: -5px;
-  border: none;
-  box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(210,54%,20%,.25),
-              0 1px 0 hsla(210,54%,20%,.35);
-  background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
-  transition-property: background-color, box-shadow;
-  transition-duration: 250ms;
-}
-
-#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
-  background-color: hsla(210,48%,96%,.75);
-  box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(210,54%,20%,.3),
-              0 1px 0 hsla(210,54%,20%,.4),
-              0 0 4px hsla(210,54%,20%,.2);
-}
-
-#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
-#back-button[open="true"] > .toolbarbutton-icon {
-  background-color: hsla(210,54%,20%,.15);
-  box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
-              0 0 1px hsla(210,54%,20%,.2) inset,
-              0 0 0 1px hsla(210,54%,20%,.4),
-              0 1px 0 hsla(210,54%,20%,.2);
-  transition: none;
-}
-
-#main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon {
-  box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
-              0 1px 0 hsla(210,54%,20%,.65) !important;
-  transition: none;
+@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
+  transition: @forwardTransitionLength@ ease-out;
 }
 
 #back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
-#forward-button:-moz-locale-dir(rtl) {
+#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
-@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
-  opacity: 0;
-}
-
-@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-  transition: opacity @forwardTransitionLength@ ease-out;
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[occluded-by-urlbar] {
-  visibility: hidden;
-}
-
-#forward-button {
-  padding: 0;
-}
-
-#forward-button > .toolbarbutton-icon {
-  background-clip: padding-box;
-  clip-path: url("chrome://browser/content/browser.xul#keyhole-forward-clip-path");
-  margin-left: -7px;
-  border-left-style: none;
-  border-radius: 0;
-  padding-left: 7px;
-  padding-right: 3px;
-  padding-top: 2px;
-  padding-bottom: 2px;
-  border: 1px solid #9a9a9a;
-}
-
 /* tabview menu item */
 
 #menu_tabview {
   list-style-image: url(chrome://browser/skin/tabview/tabview.png);
   -moz-image-region: rect(0, 80px, 16px, 64px);
 }
 
 #menu_tabview[groups="0"] {
@@ -848,31 +771,19 @@ toolbarbutton[sdk-button="true"][cui-are
 #restore-button {
   list-style-image: url("chrome://global/skin/icons/Restore.gif");
 }
 #close-button {
   list-style-image: url("chrome://global/skin/icons/Close.gif");
 }
 
 /* Location bar */
-#urlbar,
-.searchbar-textbox {
-  -moz-appearance: none;
-  padding: 1px;
-  border: 1px solid ThreeDShadow;
-  border-radius: 2px;
-}
-
-#urlbar[focused],
-.searchbar-textbox[focused] {
-  border-color: Highlight;
-}
-
 #urlbar {
-  background-color: -moz-field;
+  -moz-appearance: textfield;
+  padding: 0;
 }
 
 .urlbar-textbox-container {
   -moz-appearance: none;
   -moz-box-align: stretch;
 }
 
 .urlbar-input-box {
@@ -884,66 +795,36 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 #urlbar-container {
   -moz-box-orient: horizontal;
   -moz-box-align: stretch;
 }
 
 @conditionalForwardWithUrlbar@ > #urlbar-wrapper {
-  padding-left: @conditionalForwardWithUrlbarWidth@px;
+  -moz-padding-start: @conditionalForwardWithUrlbarWidth@px;
   -moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
   position: relative;
   pointer-events: none;
 }
 
 @conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar {
-  -moz-border-start: none;
-  margin-left: 0;
   pointer-events: all;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  transition: margin-left @forwardTransitionLength@ ease-out;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper {
-  clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path");
+  transition: margin-left @forwardTransitionLength@ ease-out,
+              margin-right @forwardTransitionLength@ ease-out;
 }
 
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar {
+@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) {
   margin-left: -@conditionalForwardWithUrlbarWidth@px;
 }
-
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
-  /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  transition-delay: 100s;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar,
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar {
-  /* when switching tabs, or when not hovered anymore, trigger a new transition
-   * to hide the forward button immediately */
-  margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
-  /* let windows-urlbar-back-button-mask clip the urlbar's right side for RTL */
-  transform: scaleX(-1);
+@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
+  margin-right: -@conditionalForwardWithUrlbarWidth@px;
 }
 
 #urlbar-icons {
   -moz-box-align: center;
 }
 
 .urlbar-icon {
   cursor: pointer;
@@ -973,109 +854,67 @@ toolbarbutton[sdk-button="true"][cui-are
   -moz-margin-start: 0;
   color: GrayText;
 }
 
 #search-container {
   min-width: calc(54px + 11ch);
 }
 
-/* identity box */
+%include ../shared/identity-block.inc.css
+
+#page-proxy-favicon {
+  margin-top: 2px;
+  margin-bottom: 2px;
+  -moz-margin-start: 4px;
+  -moz-margin-end: 3px;
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
 
+#identity-box:hover > #page-proxy-favicon {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+#identity-box:hover:active > #page-proxy-favicon,
+#identity-box[open=true] > #page-proxy-favicon {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
+/* Identity indicator */
 #identity-box {
   padding: 1px;
+  margin: -1px;
+  -moz-margin-end: 0;
   font-size: .9em;
 }
 
 #identity-box:-moz-locale-dir(ltr) {
-  border-top-left-radius: 1.5px;
-  border-bottom-left-radius: 1.5px;
+  border-top-left-radius: 2.5px;
+  border-bottom-left-radius: 2.5px;
 }
 
 #identity-box:-moz-locale-dir(rtl) {
-  border-top-right-radius: 1.5px;
-  border-bottom-right-radius: 1.5px;
-}
-
-#notification-popup-box:not([hidden]) + #identity-box {
-  -moz-padding-start: 10px;
-  border-radius: 0;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box {
-  border-radius: 0;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-  padding-left: 5px;
-  transition: padding-left;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  padding-right: 5px;
-  transition: padding-right;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box {
-  /* forward button hiding is delayed when hovered */
-  transition-delay: 100s;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-  /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
-  padding-left: 5.01px;
-}
-
-@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl),
-@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
-  padding-right: 5.01px;
-}
-
-#urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
-#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
-  -moz-margin-end: 4px;
-}
-
-#identity-box.verifiedIdentity:not(:-moz-lwtheme) {
-  background-color: #fff;
+  border-top-right-radius: 2.5px;
+  border-bottom-right-radius: 2.5px;
 }
 
 #identity-box:-moz-focusring {
   outline: 1px dotted #000;
   outline-offset: -3px;
 }
 
 #identity-icon-labels {
   -moz-padding-start: 2px;
   -moz-padding-end: 5px;
 }
 
-%include ../shared/identity-block.inc.css
-
-#page-proxy-favicon {
-  margin-top: 1px;
-  margin-bottom: 1px;
-  -moz-margin-start: 3px;
-  -moz-margin-end: 2px;
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box > #page-proxy-favicon {
-  -moz-margin-end: 1px;
-}
-
-#identity-box:hover > #page-proxy-favicon {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-#identity-box:hover:active > #page-proxy-favicon,
-#identity-box[open=true] > #page-proxy-favicon {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
+#urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
+  background-color: #fff;
+  -moz-margin-end: 4px;
 }
 
 /* Identity popup icons */
 #identity-popup-icon {
   height: 64px;
   width: 64px;
   padding: 0;
   list-style-image: url("chrome://browser/skin/identity.png");
--- a/browser/themes/osx/browser-lightweightTheme.css
+++ b/browser/themes/osx/browser-lightweightTheme.css
@@ -11,25 +11,27 @@
 
 /* Lightweight theme on tabs */
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
+  background-repeat: repeat-x, no-repeat;
 }
 
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
+  background-repeat: repeat-x, repeat-x, no-repeat;
 }
 
 @media (min-resolution: 2dppx) {
   #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTextureLWT@;/*,
                       lwtHeader;*/
   }
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %filter substitution
 
 %define menuPanelWidth 22.35em
+%define standaloneSubviewWidth 30em
 % XXXgijs This is the ugliest bit of code I think I've ever written for Mozilla.
 % Basically, the 0.1px is there to avoid CSS rounding errors causing buttons to wrap.
 % For gory details, refer to https://bugzilla.mozilla.org/show_bug.cgi?id=963365#c11
 % There's no calc() here (and therefore lots of calc() where this is used) because
 % we don't support nested calc(): https://bugzilla.mozilla.org/show_bug.cgi?id=968761
 %define menuPanelButtonWidth (@menuPanelWidth@ / 3 - 0.1px)
 %define exitSubviewGutterWidth 38px
 %define buttonStateHover :not(:-moz-any([disabled],[open],:active)):hover
@@ -84,16 +85,20 @@
 .panel-viewstack[viewtype="main"] > .panel-subviews {
   transform: translateX(@menuPanelWidth@);
 }
 
 .panel-viewstack[viewtype="main"] > .panel-subviews:-moz-locale-dir(rtl) {
   transform: translateX(-@menuPanelWidth@);
 }
 
+panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
+  display: none;
+}
+
 .panel-viewstack:not([viewtype="main"]) > .panel-mainview > #PanelUI-mainView {
   -moz-box-flex: 1;
 }
 
 .panel-subview-body {
   overflow-y: auto;
   overflow-x: hidden;
   -moz-box-flex: 1;
@@ -181,20 +186,23 @@
   margin: -1px 0 0;
 }
 
 #wrapper-edit-controls:-moz-any([place="palette"],[place="panel"]) > #edit-controls,
 #wrapper-zoom-controls:-moz-any([place="palette"],[place="panel"]) > #zoom-controls {
   -moz-margin-start: 0;
 }
 
-#PanelUI-contents,
+#PanelUI-contents {
+  max-width: @menuPanelWidth@;
+}
+
 #BMB_bookmarksPopup,
 .panel-mainview:not([panelid="PanelUI-popup"]) {
-  max-width: @menuPanelWidth@;
+  max-width: @standaloneSubviewWidth@;
 }
 
 panelview:not([mainview]) .toolbarbutton-text,
 .cui-widget-panel toolbarbutton > .toolbarbutton-text {
   text-align: start;
   display: -moz-box;
 }
 
--- a/browser/themes/windows/browser-lightweightTheme.css
+++ b/browser/themes/windows/browser-lightweightTheme.css
@@ -12,18 +12,20 @@
 
 /* Lightweight theme on tabs */
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
+  background-repeat: repeat-x, no-repeat;
 }
 
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
+  background-repeat: repeat-x, repeat-x, no-repeat;
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -759,17 +759,17 @@ toolbarbutton[sdk-button="true"][cui-are
   #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
   #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
   #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container {
     background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
     background-color: hsla(210,54%,20%,.15);
     border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4);
     box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
                 0 0 1px hsla(210,54%,20%,.2) inset,
-                /* allows keyhole-forward-clip-path to be used for non-hover as well as hover: */
+                /* allows windows-keyhole-forward-clip-path to be used for non-hover as well as hover: */
                 0 1px 0 hsla(210,54%,20%,0),
                 0 0 2px hsla(210,54%,20%,0);
     text-shadow: none;
     transition: none;
   }
 
   #nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
     -moz-border-start-color: hsla(210,54%,20%,.35);
@@ -813,17 +813,17 @@ toolbarbutton[sdk-button="true"][cui-are
 
 #forward-button > menupopup {
   margin-top: 1px !important;
 }
 
 #forward-button > .toolbarbutton-icon {
   background-clip: padding-box !important;
   /*mask: url(keyhole-forward-mask.svg#mask); XXX: this regresses twinopen */
-  clip-path: url(chrome://browser/content/browser.xul#keyhole-forward-clip-path) !important;
+  clip-path: url(chrome://browser/content/browser.xul#windows-keyhole-forward-clip-path) !important;
   margin-left: -7px !important;
   border-left-style: none !important;
   border-radius: 0 !important;
   padding-left: 7px !important;
   padding-right: 3px !important;
 }
 
 %ifdef WINDOWS_AERO
@@ -1118,17 +1118,17 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 @conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper {
-  clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path");
+  clip-path: url("chrome://browser/content/browser.xul#windows-urlbar-back-button-clip-path");
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar {
   margin-left: -@conditionalForwardWithUrlbarWidth@px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
--- a/services/sync/Makefile.in
+++ b/services/sync/Makefile.in
@@ -54,16 +54,17 @@ sync_stage_modules := \
   declined.js \
   enginesync.js \
   $(NULL)
 
 sync_testing_modules := \
   fakeservices.js \
   rotaryengine.js \
   utils.js \
+  fxa_utils.js \
   $(NULL)
 
 PREF_JS_EXPORTS := $(srcdir)/services-sync.js
 
 # Install JS module files.
 SYNC_MAIN_FILES := $(addprefix modules/,$(sync_modules))
 SYNC_MAIN_DEST = $(FINAL_TARGET)/modules/services-sync
 INSTALL_TARGETS += SYNC_MAIN
new file mode 100644
--- /dev/null
+++ b/services/sync/modules-testing/fxa_utils.js
@@ -0,0 +1,67 @@
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+  "Assert_rejects",
+  "initializeIdentityWithTokenServerResponse",
+];
+
+const {utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-sync/main.js");
+Cu.import("resource://services-sync/browserid_identity.js");
+Cu.import("resource://services-common/tokenserverclient.js");
+Cu.import("resource://testing-common/services-common/logging.js");
+Cu.import("resource://testing-common/services/sync/utils.js");
+
+// This shouldn't be here - it should be part of the xpcshell harness.
+// Maybe as Assert.rejects - so we name it like that.
+function Assert_rejects(promise, message) {
+  let deferred = Promise.defer();
+  promise.then(
+    () => deferred.reject(message || "Expected the promise to be rejected"),
+    deferred.resolve
+  );
+  return deferred.promise;
+}
+
+// Create a new browserid_identity object and initialize it with a
+// mocked TokenServerClient which always receives the specified response.
+this.initializeIdentityWithTokenServerResponse = function(response) {
+  // First create a mock "request" object that well' hack into the token server.
+  // A log for it
+  let requestLog = Log.repository.getLogger("testing.mock-rest");
+  if (!requestLog.appenders.length) { // might as well see what it says :)
+    requestLog.addAppender(new Log.DumpAppender());
+    requestLog.level = Log.Level.Trace;
+  }
+
+  // A mock request object.
+  function MockRESTRequest(url) {};
+  MockRESTRequest.prototype = {
+    _log: requestLog,
+    setHeader: function() {},
+    get: function(callback) {
+      this.response = response;
+      callback.call(this);
+    }
+  }
+  // The mocked TokenServer client which will get the response.
+  function MockTSC() { }
+  MockTSC.prototype = new TokenServerClient();
+  MockTSC.prototype.constructor = MockTSC;
+  MockTSC.prototype.newRESTRequest = function(url) {
+    return new MockRESTRequest(url);
+  }
+  // tie it all together.
+  Weave.Status.__authManager = Weave.Service.identity = new BrowserIDManager();
+  Weave.Service._clusterManager = Weave.Service.identity.createClusterManager(Weave.Service);
+  let browseridManager = Weave.Service.identity;
+  // a sanity check
+  if (!(browseridManager instanceof BrowserIDManager)) {
+    throw new Error("sync isn't configured for browserid_identity");
+  }
+  let mockTSC = new MockTSC()
+  configureFxAccountIdentity(browseridManager);
+  browseridManager._tokenServerClient = mockTSC;
+}
--- a/services/sync/modules/browserid_identity.js
+++ b/services/sync/modules/browserid_identity.js
@@ -629,17 +629,38 @@ BrowserIDClusterManager.prototype = {
       ).then(
         () => endPointFromIdentityToken()
       );
     }.bind(this);
 
     let cb = Async.makeSpinningCallback();
     promiseClusterURL().then(function (clusterURL) {
       cb(null, clusterURL);
-    }).then(null, cb);
+    }).then(
+      null, err => {
+      // service.js's verifyLogin() method will attempt to fetch a cluster
+      // URL when it sees a 401.  If it gets null, it treats it as a "real"
+      // auth error and sets Status.login to LOGIN_FAILED_LOGIN_REJECTED, which
+      // in turn causes a notification bar to appear informing the user they
+      // need to re-authenticate.
+      // On the other hand, if fetching the cluster URL fails with an exception,
+      // verifyLogin() assumes it is a transient error, and thus doesn't show
+      // the notification bar under the assumption the issue will resolve
+      // itself.
+      // Thus:
+      // * On a real 401, we must return null.
+      // * On any other problem we must let an exception bubble up.
+      if (err instanceof AuthenticationError) {
+        // callback with no error and a null result - cb.wait() returns null.
+        cb(null, null);
+      } else {
+        // callback with an error - cb.wait() completes by raising an exception.
+        cb(err);
+      }
+    });
     return cb.wait();
   },
 
   getUserBaseURL: function() {
     // Legacy Sync and FxA Sync construct the userBaseURL differently. Legacy
     // Sync appends path components onto an empty path, and in FxA Sync the
     // token server constructs this for us in an opaque manner. Since the
     // cluster manager already sets the clusterURL on Service and also has
--- a/services/sync/tests/unit/test_browserid_identity.js
+++ b/services/sync/tests/unit/test_browserid_identity.js
@@ -3,40 +3,29 @@
 
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 Cu.import("resource://services-sync/browserid_identity.js");
 Cu.import("resource://services-sync/rest.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
+Cu.import("resource://testing-common/services/sync/fxa_utils.js");
 Cu.import("resource://services-common/hawkclient.js");
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 Cu.import("resource://gre/modules/FxAccountsClient.jsm");
 Cu.import("resource://gre/modules/FxAccountsCommon.js");
-Cu.import("resource://services-common/tokenserverclient.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/constants.js");
 
 const SECOND_MS = 1000;
 const MINUTE_MS = SECOND_MS * 60;
 const HOUR_MS = MINUTE_MS * 60;
 
-// This shouldn't be here - it should be part of the xpcshell harness.
-// Maybe as Assert.rejects - so we name it like that.
-function Assert_rejects(promise, message) {
-  let deferred = Promise.defer();
-  promise.then(
-    () => deferred.reject(message || "Expected the promise to be rejected"),
-    deferred.resolve
-  );
-  return deferred.promise;
-}
-
 let identityConfig = makeIdentityConfig();
 let browseridManager = new BrowserIDManager();
 configureFxAccountIdentity(browseridManager, identityConfig);
 
 /**
  * Mock client clock and skew vs server in FxAccounts signed-in user module and
  * API client.  browserid_identity.js queries these values to construct HAWK
  * headers.  We will use this to test clock skew compensation in these headers
@@ -343,62 +332,83 @@ add_test(function test_computeXClientSta
   do_check_eq(header, "6ae94683571c7a7c54dab4700aa3995f");
   run_next_test();
 });
 
 add_task(function test_getTokenErrors() {
   _("BrowserIDManager correctly handles various failures to get a token.");
 
   _("Arrange for a 401 - Sync should reflect an auth error.");
-  yield initializeIdentityWithTokenServerFailure({
+  initializeIdentityWithTokenServerResponse({
     status: 401,
     headers: {"content-type": "application/json"},
     body: JSON.stringify({}),
   });
+  let browseridManager = Service.identity;
+
+  yield browseridManager.initializeWithCurrentIdentity();
+  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
+                       "should reject due to 401");
   Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED, "login was rejected");
 
   // XXX - other interesting responses to return?
 
   // And for good measure, some totally "unexpected" errors - we generally
   // assume these problems are going to magically go away at some point.
   _("Arrange for an empty body with a 200 response - should reflect a network error.");
-  yield initializeIdentityWithTokenServerFailure({
+  initializeIdentityWithTokenServerResponse({
     status: 200,
     headers: [],
     body: "",
   });
+  browseridManager = Service.identity;
+  yield browseridManager.initializeWithCurrentIdentity();
+  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
+                       "should reject due to non-JSON response");
   Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
 });
 
 add_task(function test_getTokenErrorWithRetry() {
   _("tokenserver sends an observer notification on various backoff headers.");
 
   // Set Sync's backoffInterval to zero - after we simulated the backoff header
   // it should reflect the value we sent.
   Status.backoffInterval = 0;
   _("Arrange for a 503 with a Retry-After header.");
-  yield initializeIdentityWithTokenServerFailure({
+  initializeIdentityWithTokenServerResponse({
     status: 503,
     headers: {"content-type": "application/json",
               "retry-after": "100"},
     body: JSON.stringify({}),
   });
+  let browseridManager = Service.identity;
+
+  yield browseridManager.initializeWithCurrentIdentity();
+  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
+                       "should reject due to 503");
+
   // The observer should have fired - check it got the value in the response.
   Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login was rejected");
   // Sync will have the value in ms with some slop - so check it is at least that.
   Assert.ok(Status.backoffInterval >= 100000);
 
   _("Arrange for a 200 with an X-Backoff header.");
   Status.backoffInterval = 0;
-  yield initializeIdentityWithTokenServerFailure({
+  initializeIdentityWithTokenServerResponse({
     status: 503,
     headers: {"content-type": "application/json",
               "x-backoff": "200"},
     body: JSON.stringify({}),
   });
+  browseridManager = Service.identity;
+
+  yield browseridManager.initializeWithCurrentIdentity();
+  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
+                       "should reject due to no token in response");
+
   // The observer should have fired - check it got the value in the response.
   Assert.ok(Status.backoffInterval >= 200000);
 });
 
 add_task(function test_getHAWKErrors() {
   _("BrowserIDManager correctly handles various HAWK failures.");
 
   _("Arrange for a 401 - Sync should reflect an auth error.");
@@ -467,55 +477,16 @@ add_task(function test_getKeysError() {
 
   Assert.ok(ex.message.indexOf("missing kA or kB") >= 0);
 });
 
 // End of tests
 // Utility functions follow
 
 // Create a new browserid_identity object and initialize it with a
-// mocked TokenServerClient which always gets the specified response.
-function* initializeIdentityWithTokenServerFailure(response) {
-  // First create a mock "request" object that well' hack into the token server.
-  // A log for it
-  let requestLog = Log.repository.getLogger("testing.mock-rest");
-  if (!requestLog.appenders.length) { // might as well see what it says :)
-    requestLog.addAppender(new Log.DumpAppender());
-    requestLog.level = Log.Level.Trace;
-  }
-
-  // A mock request object.
-  function MockRESTRequest(url) {};
-  MockRESTRequest.prototype = {
-    _log: requestLog,
-    setHeader: function() {},
-    get: function(callback) {
-      this.response = response;
-      callback.call(this);
-    }
-  }
-  // The mocked TokenServer client which will get the response.
-  function MockTSC() { }
-  MockTSC.prototype = new TokenServerClient();
-  MockTSC.prototype.constructor = MockTSC;
-  MockTSC.prototype.newRESTRequest = function(url) {
-    return new MockRESTRequest(url);
-  }
-  // tie it all together.
-  let mockTSC = new MockTSC()
-  configureFxAccountIdentity(browseridManager);
-  browseridManager._tokenServerClient = mockTSC;
-
-  yield browseridManager.initializeWithCurrentIdentity();
-  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
-                       "expecting rejection due to tokenserver error");
-}
-
-
-// Create a new browserid_identity object and initialize it with a
 // hawk mock that simulates a failure.
 // A token server mock will be used that doesn't hit a server, so we move
 // directly to a hawk request.
 function* initializeIdentityWithHAWKFailure(response) {
   // A mock request object.
   function MockRESTRequest() {};
   MockRESTRequest.prototype = {
     setHeader: function() {},
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_fxa_service_cluster.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/util.js");
+Cu.import("resource://testing-common/services/sync/fxa_utils.js");
+Cu.import("resource://testing-common/services/sync/utils.js");
+
+add_task(function test_findCluster() {
+  _("Test FxA _findCluster()");
+
+  _("_findCluster() throws on 500 errors.");
+  initializeIdentityWithTokenServerResponse({
+    status: 500,
+    headers: [],
+    body: "",
+  });
+
+  yield Service.identity.initializeWithCurrentIdentity();
+  yield Assert_rejects(Service.identity.whenReadyToAuthenticate.promise,
+                       "should reject due to 500");
+
+  Assert.throws(function() {
+    Service._clusterManager._findCluster();
+  });
+
+  _("_findCluster() returns null on authentication errors.");
+  initializeIdentityWithTokenServerResponse({
+    status: 401,
+    headers: {"content-type": "application/json"},
+    body: "{}",
+  });
+
+  yield Service.identity.initializeWithCurrentIdentity();
+  yield Assert_rejects(Service.identity.whenReadyToAuthenticate.promise,
+                       "should reject due to 401");
+
+  cluster = Service._clusterManager._findCluster();
+  Assert.strictEqual(cluster, null);
+
+  _("_findCluster() works with correct tokenserver response.");
+  let endpoint = "http://example.com/something";
+  initializeIdentityWithTokenServerResponse({
+    status: 200,
+    headers: {"content-type": "application/json"},
+    body:
+      JSON.stringify({
+        api_endpoint: endpoint,
+        duration: 300,
+        id: "id",
+        key: "key",
+        uid: "uid",
+      })
+  });
+
+  yield Service.identity.initializeWithCurrentIdentity();
+  yield Service.identity.whenReadyToAuthenticate.promise;
+  cluster = Service._clusterManager._findCluster();
+  // The cluster manager ensures a trailing "/"
+  Assert.strictEqual(cluster, endpoint + "/");
+
+  Svc.Prefs.resetBranch("");
+});
+
+function run_test() {
+  initTestLogging();
+  run_next_test();
+}
--- a/services/sync/tests/unit/test_load_modules.js
+++ b/services/sync/tests/unit/test_load_modules.js
@@ -32,16 +32,17 @@ const modules = [
   "userapi.js",
   "util.js",
 ];
 
 const testingModules = [
   "fakeservices.js",
   "rotaryengine.js",
   "utils.js",
+  "fxa_utils.js",
 ];
 
 function run_test() {
   for (let m of modules) {
     let res = "resource://services-sync/" + m;
     _("Attempting to load " + res);
     Cu.import(res, {});
   }
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -119,16 +119,17 @@ skip-if = os == "android"
 [test_sendcredentials_controller.js]
 [test_status.js]
 [test_status_checkSetup.js]
 [test_syncscheduler.js]
 [test_upgrade_old_sync_key.js]
 
 # Firefox Accounts specific tests
 [test_fxa_startOver.js]
+[test_fxa_service_cluster.js]
 
 # Finally, we test each engine.
 [test_addons_engine.js]
 run-sequentially = Hardcoded port in static files.
 [test_addons_reconciler.js]
 [test_addons_store.js]
 run-sequentially = Hardcoded port in static files.
 [test_addons_tracker.js]