Merge m-c to inbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 18 Apr 2016 15:14:45 -0700
changeset 317449 08763e0e411fce69e74a49422af5ba9345bb1ad8
parent 317448 67cd0f4372e924d0d9e7bc8c56fc41015191b09e (current diff)
parent 317409 67ac40fb8f680ea5e03805552187ba1b5e8392a1 (diff)
child 317450 cb9908e330d178d06ba9af56dc7c9c963688489b
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
Merge m-c to inbound, a=merge MozReview-Commit-ID: KlkboTscjR5
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -24,18 +24,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
   "resource://gre/modules/ResetProfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
   "resource://gre/modules/UITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
   "resource:///modules/BrowserUITelemetry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
-  "resource://gre/modules/Metrics.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
   "resource://gre/modules/ReaderMode.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
   "resource:///modules/ReaderParent.jsm");
 
 // See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -28,18 +28,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
                                   "resource://gre/modules/TelemetryLog.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryUtils",
                                   "resource://gre/modules/TelemetryUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
-XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
-                                  "resource://gre/modules/Metrics.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                    "@mozilla.org/xre/app-info;1",
                                    "nsICrashReporter");
 
 const FILE_CACHE                = "experiments.json";
 const EXPERIMENTS_CHANGED_TOPIC = "experiments-changed";
 const MANIFEST_VERSION          = 1;
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -57,16 +57,20 @@ UNINSTALLER_PACKAGE_HOOK = $(RM) -r $(ST
 STUB_HOOK = $(NSINSTALL) -D '$(ABS_DIST)/$(PKG_INST_PATH)'; \
     $(RM) '$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe'; \
     cp ../installer/windows/l10ngen/stub.exe '$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe'; \
     chmod 0755 '$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_STUB_BASENAME).exe'; \
     $(NULL)
 endif
 
 SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt)) ddg
+ifeq (,$(filter-out en-US ru be kk tr uk zh-CN zh-TW,$(AB_CD)))
+    SEARCHPLUGINS_NAMES := $(subst google,google:hidden,$(SEARCHPLUGINS_NAMES))
+    SEARCHPLUGINS_NAMES += google-nocodes
+endif
 SEARCHPLUGINS_FILENAMES = $(subst :hidden,,$(SEARCHPLUGINS_NAMES))
 SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
 SEARCHPLUGINS_TARGET := libs searchplugins
 SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(warning Missing searchplugin: $(plugin))))
 # Some locale-specific search plugins may have preprocessor directives, but the
 # default en-US ones do not.
 SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings
 PP_TARGETS += SEARCHPLUGINS
copy from browser/locales/en-US/searchplugins/google.xml
copy to browser/locales/en-US/searchplugins/google-nocodes.xml
--- a/browser/locales/en-US/searchplugins/google.xml
+++ b/browser/locales/en-US/searchplugins/google.xml
@@ -7,10 +7,12 @@
 <Description>Google Search</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zD9/f2W/f392P39/fn9/f35/f391/39/ZT+/v4uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Cf39/Zn///////////////////////////////////////////39/ZX///8IAAAAAAAAAAAAAAAA/v7+Cf39/cH/////+v35/7TZp/92ul3/WKs6/1iqOv9yuFn/rNWd//j79v///////f39v////wgAAAAAAAAAAP39/Zn/////7PXp/3G3WP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP+Or1j//vDo///////9/f2VAAAAAP///zD/////+vz5/3G3V/9TqDT/WKo6/6LQkf/U6cz/1urO/6rUm/+Zo0r/8IZB//adZ////v7///////7+/i79/f2Y/////4nWzf9Lqkj/Vqo4/9Xqzv///////////////////////ebY//SHRv/0hUL//NjD///////9/f2U/f392v////8sxPH/Ebzt/43RsP/////////////////////////////////4roL/9IVC//i1jf///////f391/39/fr/////Cr37/wW8+/+16/7/////////////////9IVC//SFQv/0hUL/9IVC//SFQv/3pnX///////39/fn9/f36/////wu++/8FvPv/tuz+//////////////////SFQv/0hUL/9IVC//SFQv/0hUL/96p7///////9/f35/f392/////81yfz/CrL5/2uk9v///////////////////////////////////////////////////////f392P39/Zn/////ks/7/zdS7P84Rur/0NT6///////////////////////9/f////////////////////////39/Zb+/v4y//////n5/v9WYu3/NUPq/ztJ6/+VnPT/z9L6/9HU+v+WnfT/Ul7t/+Hj/P////////////////////8wAAAAAP39/Z3/////6Or9/1hj7v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9sdvD////////////9/f2YAAAAAAAAAAD///8K/f39w//////5+f7/paz2/11p7v88Suv/Okfq/1pm7v+iqfX/+fn+///////9/f3B/v7+CQAAAAAAAAAAAAAAAP///wr9/f2d///////////////////////////////////////////9/f2Z/v7+CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f2Z/f392/39/fr9/f36/f392v39/Zj///8wAAAAAAAAAAAAAAAAAAAAAPAPAADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/g3+/v5X/f39mf39/cj9/f3q/f39+f39/fn9/f3q/f39yP39/Zn+/v5W////DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f2c/f399f/////////////////////////////////////////////////////9/f31/f39mv7+/iMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/gn9/f2K/f39+////////////////////////////////////////////////////////////////////////////f39+v39/Yf///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4k/f390v////////////////////////////////////////////////////////////////////////////////////////////////39/dD///8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////MP39/er//////////////////////////+r05v+v16H/gsBs/2WxSf9Wqjj/Vqk3/2OwRv99vWX/pdKV/97u2P////////////////////////////39/ej+/v4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f3q/////////////////////+v15/+Pxnv/VKk2/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/36+Z//d7tf///////////////////////39/ej///8iAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K/f390//////////////////////E4bn/XKw+/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1apN/+x0pv///////////////////////39/dD///8IAAAAAAAAAAAAAAAAAAAAAP39/Yv/////////////////////sdij/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/YKU1/8qOPv/5wZ////////////////////////39/YcAAAAAAAAAAAAAAAD+/v4l/f39+////////////////8Lgt/9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9utlT/n86N/7faqv+426v/pdKV/3u8ZP9UqDX/U6g0/3egN//jiUH/9IVC//SFQv/82MP//////////////////f39+v7+/iMAAAAAAAAAAP39/Z3////////////////q9Ob/W6w+/1OoNP9TqDT/U6g0/1OoNP9nskz/zOXC/////////////////////////////////+Dv2v+osWP/8YVC//SFQv/0hUL/9IVC//WQVP/++fb//////////////////f39mgAAAAD+/v4O/f399v///////////////4LHj/9TqDT/U6g0/1OoNP9TqDT/dblc//L58P/////////////////////////////////////////////8+v/3p3f/9IVC//SFQv/0hUL/9IVC//rIqf/////////////////9/f31////DP7+/ln////////////////f9v7/Cbz2/zOwhv9TqDT/U6g0/2KwRv/v9+z///////////////////////////////////////////////////////738//1kFT/9IVC//SFQv/0hUL/9plg///////////////////////+/v5W/f39nP///////////////4jf/f8FvPv/Bbz7/yG1s/9QqDz/vN2w//////////////////////////////////////////////////////////////////rHqP/0hUL/9IVC//SFQv/0hUL//vDn//////////////////39/Zn9/f3L////////////////R878/wW8+/8FvPv/Bbz7/y7C5P/7/fr//////////////////////////////////////////////////////////////////ere//SFQv/0hUL/9IVC//SFQv/718H//////////////////f39yP39/ez///////////////8cwvv/Bbz7/wW8+/8FvPv/WNL8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//rIqv/////////////////9/f3q/f39+v///////////////we9+/8FvPv/Bbz7/wW8+/993P3///////////////////////////////////////SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/+cGf//////////////////39/fn9/f36////////////////B737/wW8+/8FvPv/Bbz7/33c/f//////////////////////////////////////9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/6xaX//////////////////f39+f39/e3///////////////8cwvv/Bbz7/wW8+/8FvPv/WdP8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//vVv//////////////////9/f3q/f39y////////////////0bN/P8FvPv/Bbz7/wW8+/8hrvn/+/v///////////////////////////////////////////////////////////////////////////////////////////////////////////////////39/cj9/f2c////////////////ht/9/wW8+/8FvPv/FZP1/zRJ6/+zuPf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////f39mf7+/lr////////////////d9v7/B7n7/yB38f81Q+r/NUPq/0hV7P/u8P3////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v5X////D/39/ff///////////////9tkPT/NUPq/zVD6v81Q+r/NUPq/2Fs7//y8v7////////////////////////////////////////////09f7//////////////////////////////////////////////////f399f7+/g0AAAAA/f39n////////////////+Tm/P89Suv/NUPq/zVD6v81Q+r/NUPq/1Bc7f/IzPn/////////////////////////////////x8v5/0xY7P+MlPP////////////////////////////////////////////9/f2cAAAAAAAAAAD+/v4n/f39/P///////////////7W69/81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9ZZe7/k5v0/6609/+vtff/lJv0/1pm7v81Q+r/NUPq/zVD6v+GjvL//v7//////////////////////////////f39+/7+/iQAAAAAAAAAAAAAAAD9/f2N/////////////////////6Cn9f81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v+BivL////////////////////////////9/f2KAAAAAAAAAAAAAAAAAAAAAP7+/gv9/f3V/////////////////////7W69/8+S+v/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/P0zr/7q/+P///////////////////////f390v7+/gkAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3r/////////////////////+Xn/P94gfH/NkTq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NkTq/3Z/8f/l5/z///////////////////////39/er+/v4kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f3r///////////////////////////k5vz/nqX1/2p08P9IVez/OEbq/zdF6v9GU+z/aHLv/5qh9f/i5Pz////////////////////////////9/f3q////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3V/////////////////////////////////////////////////////////////////////////////////////////////////f390v7+/iQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr9/f2N/f39/P///////////////////////////////////////////////////////////////////////////f39+/39/Yv+/v4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/v4n/f39n/39/ff//////////////////////////////////////////////////////f399v39/Z3+/v4lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Dv7+/lr9/f2c/f39y/39/e39/f36/f39+v39/ez9/f3L/f39nP7+/ln+/v4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AAAAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4AAAf/AAAP/8AAP//wAP/</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?client=firefox&amp;q={searchTerms}"/>
 <Url type="text/html" method="GET" template="https://www.google.com/search" rel="searchform">
   <Param name="q" value="{searchTerms}"/>
   <Param name="ie" value="utf-8"/>
   <Param name="oe" value="utf-8"/>
+  <Param name="client" value="firefox-b"/>
+  <MozParam name="client" condition="purpose" purpose="keyword" value="firefox-b-ab"/>
 </Url>
 </SearchPlugin>
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1954,17 +1954,22 @@ notification.pluginVulnerable > .notific
 }
 
 #UITourTooltipClose {
   -moz-margin-end: -4px;
   height: 16px;
   width: 16px;
 }
 
+/**
+ * Override the --panel-arrowcontent-padding so the background extends
+ * to the sides and bottom of the panel.
+ */
 #UITourTooltipButtons {
+  margin-left: -10px;
   margin-bottom: -10px;
 }
 
 %include ../shared/contextmenu.inc.css
 
 #context-navigation > .menuitem-iconic > .menu-iconic-left {
   visibility: visible;
   /* override toolkit/themes/linux/global/menu.css */
--- a/browser/themes/shared/UITour.inc.css
+++ b/browser/themes/shared/UITour.inc.css
@@ -22,72 +22,70 @@
      on Linux without an X compositor where opacity is either 0 or 1. */
   box-shadow: 0 0 3px 0 rgba(0,0,0,0.49);
   min-height: 32px;
   min-width: 32px;
 }
 
 #UITourTooltipBody {
   -moz-margin-end: 14px;
-}
-
-#UITourTooltipBody > vbox {
-  padding-top: 4px;
-}
-
-#UITourTooltipIconContainer {
-  -moz-margin-start: -16px;
+  -moz-margin-start: 14px;
+  margin-top: -16px;
+  margin-bottom: 8px;
 }
 
 #UITourTooltipIcon {
   width: 48px;
   height: 48px;
-  -moz-margin-start: 28px;
-  -moz-margin-end: 28px;
+  -moz-margin-start: 14px;
+  -moz-margin-end: 14px;
 }
 
 #UITourTooltipTitle,
 #UITourTooltipDescription {
   max-width: 20rem;
 }
 
 #UITourTooltipTitle {
   font-size: 1.45rem;
   font-weight: bold;
   -moz-margin-start: 0;
-  -moz-margin-end: 0;
-  margin: 0 0 9px 0;
+  /* Avoid the title overlapping the close button */
+  -moz-margin-end: 14px;
+  margin-top: 0;
+  margin-bottom: 9px;
 }
 
 #UITourTooltipDescription {
   -moz-margin-start: 0;
   -moz-margin-end: 0;
   font-size: 1.15rem;
   line-height: 1.8rem;
   margin-bottom: 0; /* Override global.css */
 }
 
 #UITourTooltipClose {
+  position: relative;
   -moz-appearance: none;
   border: none;
   background-color: transparent;
   min-width: 0;
   -moz-margin-start: 4px;
   margin-top: -2px;
 }
 
 #UITourTooltipClose > .toolbarbutton-text {
   display: none;
 }
 
 #UITourTooltipButtons {
   -moz-box-pack: end;
   background-color: hsla(210,4%,10%,.07);
   border-top: 1px solid hsla(210,4%,10%,.14);
-  margin: 24px -16px -16px;
+  margin: 10px -16px -16px;
   padding: 2em 15px;
 }
 
 #UITourTooltipButtons > label,
 #UITourTooltipButtons > button {
   margin: 0 15px;
 }
 
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -260,26 +260,40 @@ var openToolboxForTab = Task.async(funct
  * toolbox has been opened. Resolves to the toolbox.
  */
 var openNewTabAndToolbox = Task.async(function*(url, toolId, hostType) {
   let tab = yield addTab(url);
   return openToolboxForTab(tab, toolId, hostType)
 });
 
 /**
+ * Close a tab and if necessary, the toolbox that belongs to it
+ * @param {Tab} tab The tab to close.
+ * @return {Promise} Resolves when the toolbox and tab have been destroyed and
+ * closed.
+ */
+var closeTabAndToolbox = Task.async(function*(tab = gBrowser.selectedTab) {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  if (target) {
+    yield gDevTools.closeToolbox(target);
+  }
+
+  yield removeTab(gBrowser.selectedTab);
+});
+
+/**
  * Close a toolbox and the current tab.
  * @param {Toolbox} toolbox The toolbox to close.
  * @return {Promise} Resolves when the toolbox and tab have been destroyed and
  * closed.
  */
-function closeToolboxAndTab(toolbox) {
-  return toolbox.destroy().then(function() {
-    gBrowser.removeCurrentTab();
-  });
-}
+var closeToolboxAndTab = Task.async(function*(toolbox) {
+  yield toolbox.destroy();
+  yield removeTab(gBrowser.selectedTab);
+});
 
 /**
  * Waits until a predicate returns true.
  *
  * @param function predicate
  *        Invoked once in a while until it returns true.
  * @param number interval [optional]
  *        How often the predicate is invoked, in milliseconds.
--- a/devtools/client/inspector/rules/test/browser_rules_edit-selector-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-selector-click.js
@@ -31,25 +31,25 @@ function* testClickOnSelectorEditorInput
   info("Focusing an existing selector name in the rule-view");
   let editor = yield focusEditableField(view, ruleEditor.selectorText);
   let editorInput = editor.input;
   is(inplaceEditor(ruleEditor.selectorText), editor,
     "The selector editor got focused");
 
   info("Click inside the editor input");
   let onClick = once(editorInput, "click");
-  EventUtils.synthesizeMouse(editor.input, 1, 1, {}, view.styleWindow);
+  EventUtils.synthesizeMouse(editor.input, 2, 1, {}, view.styleWindow);
   yield onClick;
   is(editor.input, view.styleDocument.activeElement,
     "The editor input should still be focused");
   ok(!ruleEditor.newPropSpan, "No newProperty editor was created");
 
   info("Doubleclick inside the editor input");
   let onDoubleClick = once(editorInput, "dblclick");
-  EventUtils.synthesizeMouse(editor.input, 1, 1, { clickCount: 2 },
+  EventUtils.synthesizeMouse(editor.input, 2, 1, { clickCount: 2 },
     view.styleWindow);
   yield onDoubleClick;
   is(editor.input, view.styleDocument.activeElement,
     "The editor input should still be focused");
   ok(!ruleEditor.newPropSpan, "No newProperty editor was created");
 
   info("Click outside the editor input");
   let onBlur = once(editorInput, "blur");
--- a/devtools/client/shared/components/h-split-box.js
+++ b/devtools/client/shared/components/h-split-box.js
@@ -125,17 +125,17 @@ module.exports = createClass({
         {
           className: "h-split-box-pane",
           style: { flex: startWidth, minWidth: minStartWidth },
         },
         start
       ),
 
       dom.div({
-        className: "h-split-box-splitter",
+        className: "devtools-side-splitter",
         onMouseDown: this._onMouseDown,
       }),
 
       dom.div(
         {
           className: "h-split-box-pane",
           style: { flex: 1 - startWidth, minWidth: minEndWidth },
         },
--- a/devtools/client/shared/components/test/mochitest/test_HSplitBox_01.html
+++ b/devtools/client/shared/components/test/mochitest/test_HSplitBox_01.html
@@ -4,16 +4,17 @@
 Basic tests for the HSplitBox component.
 -->
 <head>
   <meta charset="utf-8">
   <title>Tree component test</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript "src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <link rel="stylesheet" href="chrome://devtools/skin/splitters.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/components-h-split-box.css" type="text/css"/>
   <style>
     html {
       --theme-splitter-color: black;
     }
   </style>
 </head>
 <body>
@@ -72,17 +73,17 @@ window.onload = Task.async(function* () 
     const threeQuarters = left + 3 * width / 4;
 
     synthesizeMouse(container, middle, top, { type: "mousemove" }, window);
     is(newSizes.length, 0, "Mouse moves without dragging the splitter should have no effect");
 
     // Send a mouse down on the splitter, and then move the mouse a couple
     // times. Now we should get resizes.
 
-    const splitter = document.querySelector(".h-split-box-splitter");
+    const splitter = document.querySelector(".devtools-side-splitter");
     ok(splitter, "Should get our splitter");
 
     synthesizeMouseAtCenter(splitter, { button: 0, type: "mousedown" }, window);
 
     function mouseMove(clientX) {
       const event = new MouseEvent("mousemove", { clientX });
       document.defaultView.top.dispatchEvent(event);
     }
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -1675,17 +1675,17 @@ EditableFieldsEngine.prototype = {
    * @param  {Node} target
    *         Dom node to be edited.
    */
   edit: function(target) {
     if (!target) {
       return;
     }
 
-    target.scrollIntoView();
+    target.scrollIntoView(false);
     target.focus();
 
     if (!target.matches(this.selectors.join(","))) {
       return;
     }
 
     // If we are actively editing something complete the edit first.
     if (this.isEditing) {
--- a/devtools/client/storage/test/head.js
+++ b/devtools/client/storage/test/head.js
@@ -838,17 +838,17 @@ function waitForContextMenu(popup, butto
     deferred.resolve(popup);
   }
 
   popup.addEventListener("popupshown", onPopupShown);
 
   info("wait for the context menu to open");
   button.scrollIntoView();
   let eventDetails = {type: "contextmenu", button: 2};
-  EventUtils.synthesizeMouse(button, 2, 2, eventDetails,
+  EventUtils.synthesizeMouse(button, 5, 2, eventDetails,
                              button.ownerDocument.defaultView);
   return deferred.promise;
 }
 
 /**
  * Verify the storage inspector state: check that given type/host exists
  * in the tree, and that the table contains rows with specified names.
  *
--- a/devtools/client/themes/components-h-split-box.css
+++ b/devtools/client/themes/components-h-split-box.css
@@ -17,15 +17,8 @@
   height: 100%;
 }
 
 .h-split-box {
   display: flex;
   flex-direction: row;
   flex: 1;
 }
-
-.h-split-box-splitter {
-  -moz-border-end: 1px solid var(--theme-splitter-color);
-  cursor: ew-resize;
-  width: 3px;
-  -moz-margin-start: -3px;
-}
--- a/devtools/client/themes/splitters.css
+++ b/devtools/client/themes/splitters.css
@@ -1,43 +1,79 @@
 /* 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/. */
 
+/* This file is loaded by both browser.xul and toolbox.xul. Therefore, rules
+   defined here can not rely on toolbox.xul variables. */
+
 /* Splitters */
+
+:root {
+  /* Define the widths of the draggable areas on each side of a splitter. top
+     and bottom widths are used for horizontal splitters, inline-start and
+     inline-end for side splitters.*/
+
+  --devtools-splitter-top-width: 2px;
+  --devtools-splitter-bottom-width: 2px;
+
+  /* Small draggable area on inline-start to avoid overlaps on scrollbars.*/
+  --devtools-splitter-inline-start-width: 1px;
+  --devtools-splitter-inline-end-width: 4px;
+}
+
 :root[devtoolstheme="light"] {
   /* These variables are used in browser.xul but inside the toolbox they are overridden by --theme-splitter-color */
   --devtools-splitter-color: #dde1e4;
 }
 
 :root[devtoolstheme="dark"] {
   --devtools-splitter-color: #42484f;
 }
 
-.devtools-horizontal-splitter {
+.devtools-horizontal-splitter,
+.devtools-side-splitter {
   -moz-appearance: none;
   background-image: none;
-  background-color: transparent;
   border: 0;
-  border-bottom: 1px solid var(--devtools-splitter-color);
-  min-height: 3px;
-  height: 3px;
-  margin-top: -3px;
+  border-style: solid;
+  border-color: transparent;
+  background-color: var(--devtools-splitter-color);
+  background-clip: content-box;
   position: relative;
+
+  box-sizing: border-box;
+
+  /* Positive z-index positions the splitter on top of its siblings and makes
+     it clickable on both sides. */
+  z-index: 1;
+}
+
+.devtools-horizontal-splitter {
+  min-height: calc(var(--devtools-splitter-top-width) +
+    var(--devtools-splitter-bottom-width) + 1px);
+
+  border-top-width: var(--devtools-splitter-top-width);
+  border-bottom-width: var(--devtools-splitter-bottom-width);
+
+  margin-top: calc(-1 * var(--devtools-splitter-top-width) - 1px);
+  margin-bottom: calc(-1 * var(--devtools-splitter-bottom-width));
+
+  cursor: n-resize;
 }
 
 .devtools-side-splitter {
-  -moz-appearance: none;
-  background-image: none;
-  background-color: transparent;
-  border: 0;
-  -moz-border-end: 1px solid var(--devtools-splitter-color);
-  min-width: 3px;
-  width: 3px;
-  -moz-margin-start: -3px;
-  position: relative;
+  min-width: calc(var(--devtools-splitter-inline-start-width) +
+    var(--devtools-splitter-inline-end-width) + 1px);
+
+  border-inline-start-width: var(--devtools-splitter-inline-start-width);
+  border-inline-end-width: var(--devtools-splitter-inline-end-width);
+
+  margin-inline-start: calc(-1 * var(--devtools-splitter-inline-start-width) - 1px);
+  margin-inline-end: calc(-1 * var(--devtools-splitter-inline-end-width));
+
   cursor: e-resize;
 }
 
 .devtools-horizontal-splitter.disabled,
 .devtools-side-splitter.disabled {
   pointer-events: none;
 }
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -975,26 +975,24 @@
 
 .hidden-labels-box:not(.visible) > label,
 .hidden-labels-box.visible ~ .hidden-labels-box > label:last-child {
   display: none;
 }
 
 .devtools-invisible-splitter {
   border-color: transparent;
+  background-color: transparent;
 }
 
-.devtools-horizontal-splitter {
-  border-bottom: 1px solid var(--theme-splitter-color);
+.devtools-horizontal-splitter,
+.devtools-side-splitter {
+  background-color: var(--theme-splitter-color);
 }
 
-.devtools-side-splitter {
-  -moz-border-end: 1px solid var(--theme-splitter-color);
-  border-color: var(--theme-splitter-color); /* Needed for responsive container at low width. */
-}
 
 /* Throbbers */
 .devtools-throbber::before {
   content: "";
   display: inline-block;
   vertical-align: bottom;
   -moz-margin-end: 0.5em;
   width: 1em;
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -59,27 +59,29 @@
 @media (max-width: 700px) {
   .devtools-responsive-container {
     -moz-box-orient: vertical;
   }
 
   .devtools-responsive-container > .devtools-side-splitter {
     /* This is a normally vertical splitter, but we have turned it horizontal
        due to the smaller resolution */
-    min-height: 3px;
-    height: 3px;
-    margin-top: -3px;
+    min-height: calc(var(--devtools-splitter-top-width) +
+    var(--devtools-splitter-bottom-width) + 1px);
+    border-top-width: var(--devtools-splitter-top-width);
+    border-bottom-width: var(--devtools-splitter-bottom-width);
+    margin-top: calc(-1 * var(--devtools-splitter-top-width) - 1px);
+    margin-bottom: calc(-1 * var(--devtools-splitter-bottom-width));
 
     /* Reset the vertical splitter styles */
-    border-width: 0;
-    border-bottom-width: 1px;
-    border-bottom-style: solid;
-    -moz-margin-start: 0;
-    width: auto;
     min-width: 0;
+    border-inline-end-width: 0;
+    border-inline-start-width: 0;
+    margin-inline-end: 0;
+    margin-inline-start: 0;
 
     /* In some edge case the cursor is not changed to n-resize */
     cursor: n-resize;
   }
 
   .devtools-responsive-container > .devtools-sidebar-tabs:not([pane-collapsed]) {
     /* When the panel is collapsed min/max height should not be applied because
        collapsing relies on negative margins, which implies constant height. */
@@ -1118,17 +1120,17 @@
   color: var(--theme-body-color);
 }
 
 /* Table Widget */
 
 /* Table body */
 
 .table-widget-body > .devtools-side-splitter {
-  border: none;
+  background-color: transparent;
 }
 
 .table-widget-body {
   overflow: auto;
 }
 
 .table-widget-body,
 .table-widget-empty-text {
--- a/devtools/client/webconsole/test/browser_webconsole_netlogging.js
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging.js
@@ -16,16 +16,24 @@ const TEST_DATA_JSON_CONTENT =
 
 const PAGE_REQUEST_PREDICATE =
   ({ request }) => request.url.endsWith("test-network-request.html");
 
 const TEST_DATA_REQUEST_PREDICATE =
   ({ request }) => request.url.endsWith("test-data.json");
 
 add_task(function* testPageLoad() {
+  // Enable logging in the UI.  Not needed to pass test but makes it easier
+  // to debug interactively.
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set":
+      [["devtools.webconsole.filter.networkinfo", true]
+    ]}, resolve);
+  });
+
   let finishedRequest = waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
   let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
   let request = yield finishedRequest;
 
   ok(request, "Page load was logged");
 
   let client = hud.ui.webConsoleClient;
   let args = [request.actor];
@@ -35,16 +43,18 @@ add_task(function* testPageLoad() {
   is(request.request.url, TEST_NETWORK_REQUEST_URI,
     "Logged network entry is page load");
   is(request.request.method, "GET", "Method is correct");
   ok(!postData.postData.text, "No request body was stored");
   ok(!postData.postDataDiscarded,
     "Request body was not discarded");
   is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0,
     "Response body's beginning is okay");
+
+  yield closeTabAndToolbox();
 });
 
 add_task(function* testXhrGet() {
   let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
 
   let finishedRequest = waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE);
   content.wrappedJSObject.testXhrGet();
   let request = yield finishedRequest;
@@ -57,16 +67,18 @@ add_task(function* testXhrGet() {
   const responseContent = yield getPacket(client, "getResponseContent", args);
 
   is(request.request.method, "GET", "Method is correct");
   ok(!postData.postData.text, "No request body was sent");
   ok(!postData.postDataDiscarded,
     "Request body was not discarded");
   is(responseContent.content.text, TEST_DATA_JSON_CONTENT,
     "Response is correct");
+
+  yield closeTabAndToolbox();
 });
 
 add_task(function* testXhrPost() {
   let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
 
   let finishedRequest = waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE);
   content.wrappedJSObject.testXhrPost();
   let request = yield finishedRequest;
@@ -77,16 +89,18 @@ add_task(function* testXhrPost() {
   let args = [request.actor];
   const postData = yield getPacket(client, "getRequestPostData", args);
   const responseContent = yield getPacket(client, "getResponseContent", args);
 
   is(request.request.method, "POST", "Method is correct");
   is(postData.postData.text, "Hello world!", "Request body was logged");
   is(responseContent.content.text, TEST_DATA_JSON_CONTENT,
     "Response is correct");
+
+  yield closeTabAndToolbox();
 });
 
 add_task(function* testFormSubmission() {
   let pageLoadRequestFinished = waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
   let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
 
   info("Waiting for the page load to be finished.")
   yield pageLoadRequestFinished;
@@ -112,9 +126,11 @@ add_task(function* testFormSubmission() 
     .indexOf("Content-Type: application/x-www-form-urlencoded"), -1,
     "Content-Type is correct");
   isnot(postData.postData.text
     .indexOf("Content-Length: 20"), -1, "Content-length is correct");
   isnot(postData.postData.text
     .indexOf("name=foo+bar&age=144"), -1, "Form data is correct");
   is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0,
     "Response body's beginning is okay");
+
+  yield closeTabAndToolbox();
 });
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -278,17 +278,17 @@ function waitForContextMenu(popup, butto
 
     deferred.resolve(popup);
   }
 
   popup.addEventListener("popupshown", onPopupShown);
 
   info("wait for the context menu to open");
   let eventDetails = {type: "contextmenu", button: 2};
-  EventUtils.synthesizeMouse(button, 2, 2, eventDetails,
+  EventUtils.synthesizeMouse(button, 5, 2, eventDetails,
                              button.ownerDocument.defaultView);
   return deferred.promise;
 }
 
 /**
  * Listen for a new tab to open and return a promise that resolves when one
  * does and completes the load event.
  * @return a promise that resolves to the tab object
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -63,17 +63,16 @@ public class AppConstants {
          * If our MAX_SDK_VERSION is lower than ICS, we must not be an ICS device.
          * Otherwise, we need a range check.
          */
         public static final boolean preMarshmallow = MAX_SDK_VERSION < 23 || (MIN_SDK_VERSION < 23 && Build.VERSION.SDK_INT < 23);
         public static final boolean preLollipop = MAX_SDK_VERSION < 21 || (MIN_SDK_VERSION < 21 && Build.VERSION.SDK_INT < 21);
         public static final boolean preJBMR2 = MAX_SDK_VERSION < 18 || (MIN_SDK_VERSION < 18 && Build.VERSION.SDK_INT < 18);
         public static final boolean preJBMR1 = MAX_SDK_VERSION < 17 || (MIN_SDK_VERSION < 17 && Build.VERSION.SDK_INT < 17);
         public static final boolean preJB = MAX_SDK_VERSION < 16 || (MIN_SDK_VERSION < 16 && Build.VERSION.SDK_INT < 16);
-        public static final boolean preICS = MAX_SDK_VERSION < 14 || (MIN_SDK_VERSION < 14 && Build.VERSION.SDK_INT < 14);
     }
 
     /**
      * The name of the Java class that represents the android application.
      */
     public static final String MOZ_ANDROID_APPLICATION_CLASS = "@MOZ_ANDROID_APPLICATION_CLASS@";
     /**
      * The name of the Java class that launches the browser activity.
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -3207,18 +3207,17 @@ public class BrowserApp extends GeckoApp
         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);
 
         // Only show the "Quit" menu item on pre-ICS, television devices,
         // or if the user has explicitly enabled the clear on shutdown pref.
         // (We check the pref last to save the pref read.)
         // In ICS+, it's easy to kill an app through the task switcher.
-        final boolean visible = Versions.preICS ||
-                                HardwareUtils.isTelevision() ||
+        final boolean visible = HardwareUtils.isTelevision() ||
                                 !PrefUtils.getStringSet(GeckoSharedPrefs.forProfile(this),
                                                         ClearOnShutdownPref.PREF,
                                                         new HashSet<String>()).isEmpty();
         aMenu.findItem(R.id.quit).setVisible(visible);
 
         // If tab data is unavailable we disable most of the context menu and related items and
         // return early.
         if (tab == null || tab.getURL() == null) {
--- a/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
@@ -323,21 +323,16 @@ public class DoorHangerPopup extends Anc
         // Make the popup focusable for accessibility. This gets done here
         // so the node can be accessibility focused, but on pre-ICS devices this
         // causes crashes, so it is done after the popup is shown.
         if (Versions.feature14Plus) {
             setFocusable(true);
         }
 
         show();
-
-        if (Versions.preICS) {
-            // Make the popup focusable for keyboard accessibility.
-            setFocusable(true);
-        }
     }
 
     //Show all inter-DoorHanger dividers (ie. Dividers on all visible DoorHangers except the last one)
     private void showDividers() {
         int count = mContent.getChildCount();
         DoorHanger lastVisibleDoorHanger = null;
 
         for (int i = 0; i < count; i++) {
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1404,24 +1404,16 @@ public abstract class GeckoApp
         // cause a loop! See Bug 1011008, Comment 12.
         super.onConfigurationChanged(getResources().getConfiguration());
     }
 
     protected void initializeChrome() {
         mDoorHangerPopup = new DoorHangerPopup(this);
         mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
         mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
-
-        if (mCameraView == null) {
-            // Pre-ICS devices need the camera surface in a visible layout.
-            if (Versions.preICS) {
-                mCameraView = new SurfaceView(this);
-                ((SurfaceView)mCameraView).getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-            }
-        }
     }
 
     /**
      * Loads the initial tab at Fennec startup. If we don't restore tabs, this
      * tab will be about:home, or the homepage if the use has set one.
      * If we restore tabs, we don't need to create a new tab.
      */
     protected void loadStartupTab(final int flags) {
@@ -2695,20 +2687,16 @@ public abstract class GeckoApp
         if (resultant == null) {
             return;
         }
 
         onLocaleChanged(resultant);
     }
 
     private void setSystemUiVisible(final boolean visible) {
-        if (Versions.preICS) {
-            return;
-        }
-
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 if (visible) {
                     mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
                 } else {
                     mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
                 }
--- a/mobile/android/base/java/org/mozilla/gecko/MemoryMonitor.java
+++ b/mobile/android/base/java/org/mozilla/gecko/MemoryMonitor.java
@@ -1,16 +1,15 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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;
 
-import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.home.ImageLoader;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
@@ -87,21 +86,16 @@ class MemoryMonitor extends BroadcastRec
             if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
                 GeckoThread.waitOnGecko();
             }
         }
     }
 
     public void onTrimMemory(int level) {
         Log.d(LOGTAG, "onTrimMemory() notification received with level " + level);
-        if (Versions.preICS) {
-            // This won't even get called pre-ICS.
-            return;
-        }
-
         if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
             // We seem to get this just by entering the task switcher or hitting the home button.
             // Seems bogus, because we are the foreground app, or at least not at the end of the LRU list.
             // Just ignore it, and if there is a real memory pressure event (CRITICAL, MODERATE, etc),
             // we'll respond appropriately.
             return;
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenuItem.java
+++ b/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenuItem.java
@@ -1,15 +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/. */
 
 package org.mozilla.gecko.menu;
 
-import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.widget.GeckoActionProvider;
 
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.view.ActionProvider;
 import android.view.ContextMenu;
@@ -112,20 +111,16 @@ public class GeckoMenuItem implements Me
     }
 
     @Override
     public boolean expandActionView() {
         return false;
     }
 
     public boolean hasActionProvider() {
-        if (Versions.preICS) {
-            return false;
-        }
-
         return (mActionProvider != null);
     }
 
     public int getActionEnum() {
         return mActionEnum;
     }
 
     public GeckoActionProvider getGeckoActionProvider() {
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -1460,22 +1460,17 @@ OnSharedPreferenceChangeListener
                     ((TwoStatePreference) preference).setChecked(value);
                 }
             }
         }
 
         @Override
         public void prefValue(String prefName, final boolean value) {
             final Preference pref = getField(prefName);
-            final CheckBoxPrefSetter prefSetter;
-            if (Versions.preICS) {
-                prefSetter = new CheckBoxPrefSetter();
-            } else {
-                prefSetter = new TwoStatePrefSetter();
-            }
+            final CheckBoxPrefSetter prefSetter = new TwoStatePrefSetter();
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     prefSetter.setBooleanPref(pref, value);
                 }
             });
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/util/InputOptionsUtils.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/InputOptionsUtils.java
@@ -3,23 +3,19 @@
  * 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;
 
 import android.content.Context;
 import android.content.Intent;
 import android.speech.RecognizerIntent;
-import org.mozilla.gecko.AppConstants.Versions;
 
 public class InputOptionsUtils {
     public static boolean supportsVoiceRecognizer(Context context, String prompt) {
-        if (Versions.preICS) {
-            return false;
-        }
         final Intent intent = createVoiceRecognizerIntent(prompt);
         return intent.resolveActivity(context.getPackageManager()) != null;
     }
 
     public static Intent createVoiceRecognizerIntent(String prompt) {
         final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
         intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
         intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -872,19 +872,25 @@ var BrowserApp = {
     });
 
     NativeWindow.contextmenus.add(stringGetter("contextmenu.saveImage"),
       NativeWindow.contextmenus.imageSaveableContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_image");
         UITelemetry.addEvent("save.1", "contextmenu", null, "image");
 
-        ContentAreaUtils.saveImageURL(aTarget.currentURI.spec, null, "SaveImageTitle",
-                                      false, true, aTarget.ownerDocument.documentURIObject,
-                                      aTarget.ownerDocument);
+        RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE).then(function(permissionGranted) {
+            if (!permissionGranted) {
+                return;
+            }
+
+            ContentAreaUtils.saveImageURL(aTarget.currentURI.spec, null, "SaveImageTitle",
+                                          false, true, aTarget.ownerDocument.documentURIObject,
+                                          aTarget.ownerDocument);
+        });
       });
 
     NativeWindow.contextmenus.add(stringGetter("contextmenu.setImageAs"),
       NativeWindow.contextmenus._disableRestricted("SET_IMAGE", NativeWindow.contextmenus.imageSaveableContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image");
 
         let src = aTarget.src;
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
@@ -78,17 +78,21 @@ public class AppMenuComponent extends Ba
             return stringResource;
         }
     };
 
     public AppMenuComponent(final UITestContext testContext) {
         super(testContext);
     }
 
-    private void assertMenuIsNotOpen() {
+    public void assertMenuIsOpen() {
+        fAssertTrue("Menu is open", isMenuOpen());
+    }
+
+    public void assertMenuIsNotOpen() {
         fAssertFalse("Menu is not open", isMenuOpen());
     }
 
     public void assertMenuItemIsDisabledAndVisible(PageMenuItem pageMenuItem) {
         openAppMenu();
 
         // Non-legacy devices have hierarchical menu, check for parent menu item "page".
         final View parentMenuItemView = findAppMenuItemView(MenuItem.PAGE.getString(mSolo));
@@ -266,17 +270,26 @@ public class AppMenuComponent extends Ba
      *                      You must use findAppMenuItemView(menuItemTitle) to obtain it.
      *
      * @return true if app menu is open.
      */
     private boolean isMenuOpen(View menuItemView) {
         return (menuItemView != null) && (menuItemView.getVisibility() == View.VISIBLE);
     }
 
-    private void waitForMenuOpen() {
+    public void waitForMenuOpen() {
         WaitHelper.waitFor("menu to open", new Condition() {
             @Override
             public boolean isSatisfied() {
                 return isMenuOpen();
             }
         });
     }
+
+    public void waitForMenuClose() {
+        WaitHelper.waitFor("menu to close", new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                return !isMenuOpen();
+            }
+        });
+    }
 }
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAppMenuPathways.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAppMenuPathways.java
@@ -5,31 +5,46 @@
 package org.mozilla.gecko.tests;
 
 import org.json.JSONObject;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.tests.components.AppMenuComponent;
 import org.mozilla.gecko.tests.helpers.GeckoHelper;
 import org.mozilla.gecko.tests.helpers.NavigationHelper;
 
+import com.robotium.solo.Solo;
+
 /**
  * Set of tests to test UI App menu and submenus the user interact with.
  */
 public class testAppMenuPathways extends UITest {
 
     /**
      * Robocop supports only a single test function per test class. Therefore, we
      * have a single top-level test function that dispatches to sub-tests.
      */
     public void testAppMenuPathways() {
         GeckoHelper.blockForReady();
 
+        _testHardwareMenuKeyOpenClose();
         _testSaveAsPDFPathway();
     }
 
+    public void _testHardwareMenuKeyOpenClose() {
+        mAppMenu.assertMenuIsNotOpen();
+
+        mSolo.sendKey(Solo.MENU);
+        mAppMenu.waitForMenuOpen();
+        mAppMenu.assertMenuIsOpen();
+
+        mSolo.sendKey(Solo.MENU);
+        mAppMenu.waitForMenuClose();
+        mAppMenu.assertMenuIsNotOpen();
+    }
+
     public void _testSaveAsPDFPathway() {
         // Page menu should be disabled in about:home.
         mAppMenu.assertMenuItemIsDisabledAndVisible(AppMenuComponent.PageMenuItem.SAVE_AS_PDF);
 
         // Generate a mock Content:LocationChange message with video mime-type for the current tab (tabId = 0).
         final JSONObject message = new JSONObject();
         try {
             message.put("contentType", "video/webm");
--- a/mobile/android/themes/core/aboutBase.css
+++ b/mobile/android/themes/core/aboutBase.css
@@ -23,26 +23,27 @@ input {
 }
 
 .header {
   color: #363B40;
   font-size: 1.1em;
   font-weight: bold;
   background-color: #f5f5f5;
   border-bottom: 2px solid;
-  -moz-border-bottom-colors: #ff9100 #f27900;
+  -moz-border-bottom-colors: #FF9500;
   display: flex;
   flex-direction: row;
   align-items: center;
+  height: 48px;
 }
 
 .header > div {
   flex: 1;
-  padding: 1em;
-  -moz-padding-start: 1.5em;
+  padding: 10px;
+  -moz-padding-start: 16px;
 }
 
 #header-button {
   background-repeat: no-repeat;
   background-position: center center;
   background-size: 33px 33px;
   flex: 0;
   height: 100%;
--- a/mobile/android/themes/core/aboutLogins.css
+++ b/mobile/android/themes/core/aboutLogins.css
@@ -30,26 +30,32 @@ body {
 }
 
 .realm {
   /* hostname is not localized, so keep the margin on the left side */
   margin-left: .67em;
 }
 
 .toolbar-buttons {
+  display: flex;
+  align-items: center;
+  justify-content: center;
   list-style: none;
+  margin: 0px;
+  padding: 0px;
+  height: 48px;
+  width: 48px;
 }
 
 .toolbar-buttons > li {
   background-position: center;
   background-size: 24px 24px;
   background-repeat: no-repeat;
   height: 20px;
   width: 20px;
-  margin: 0 15px;
 }
 
 #filter-input-container {
   position: fixed;
   bottom: 0;
   width: 100%;
   padding: 10px 0;
   display: flex;