Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 15 Aug 2014 16:22:55 -0400
changeset 199842 b9da9928d0610a60e7f0158b8536d44ab4b108b8
parent 199841 95bed435b123b960da22675cb43e59e83f6a6ba9 (current diff)
parent 199784 d18d3e14ac3ebce527241cdc585c1f92aed1e818 (diff)
child 199862 174e75a23eaf579ec9700f72c2bac8054f365be4
push id8236
push userryanvm@gmail.com
push dateFri, 15 Aug 2014 21:07:37 +0000
treeherderfx-team@f5d6f273ce06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
Merge fx-team to m-c. a=merge
browser/base/content/browser.js
browser/components/loop/test/mochitest/browser_mozLoop_charPref.js
browser/components/loop/test/xpcshell/test_loopservice_get_loop_char_pref.js
browser/components/loop/test/xpcshell/test_loopservice_set_loop_char_pref.js
browser/themes/linux/in-content/common.css
browser/themes/osx/in-content/common.css
browser/themes/shared/in-content/check.png
browser/themes/shared/in-content/check@2x.png
browser/themes/shared/in-content/common.inc.css
browser/themes/shared/in-content/dropdown-disabled.png
browser/themes/shared/in-content/dropdown-disabled@2x.png
browser/themes/shared/in-content/dropdown.png
browser/themes/shared/in-content/dropdown@2x.png
browser/themes/shared/in-content/help-glyph.png
browser/themes/shared/in-content/help-glyph@2x.png
browser/themes/shared/in-content/sorter.png
browser/themes/shared/in-content/sorter@2x.png
browser/themes/windows/in-content/common.css
mobile/android/base/home/SuggestClient.java
mobile/android/search/java/org/mozilla/search/autocomplete/SuggestClient.java
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1275,18 +1275,18 @@ pref("devtools.toolbar.enabled", true);
 pref("devtools.toolbar.visible", false);
 pref("devtools.commands.dir", "");
 
 // Enable the app manager
 pref("devtools.appmanager.enabled", true);
 pref("devtools.appmanager.lastTab", "help");
 pref("devtools.appmanager.manifestEditor.enabled", true);
 
-// Disable devtools webide until bug 1007059
-pref("devtools.webide.enabled", false);
+// Enable DevTools WebIDE by default
+pref("devtools.webide.enabled", true);
 
 // Toolbox preferences
 pref("devtools.toolbox.footer.height", 250);
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
 pref("devtools.toolbox.selectedTool", "webconsole");
 pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage"]');
 pref("devtools.toolbox.sideEnabled", true);
--- a/browser/base/content/aboutneterror/netError.css
+++ b/browser/base/content/aboutneterror/netError.css
@@ -1,13 +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/. */
 
-@import url("chrome://browser/skin/in-content/common.css");
+@import url("chrome://global/skin/in-content/common.css");
 
 body {
   display: flex;
   box-sizing: padding-box;
   min-height: 100vh;
   padding: 0 48px;
   align-items: center;
   justify-content: center;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -18,16 +18,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
                                   "resource://gre/modules/CharsetMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
                                   "resource://gre/modules/ShortcutUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager",
                                   "resource://gre/modules/GMPInstallManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
+                                  "resource:///modules/ContentSearch.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
+                                  "resource:///modules/AboutHome.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gLastBrowserCharset = null;
 var gProxyFavIcon = null;
@@ -3088,18 +3092,27 @@ const BrowserSearch = {
         win = window.openDialog(getBrowserURL(), "_blank",
                                 "chrome,all,dialog=no", "about:blank");
         Services.obs.addObserver(observer, "browser-delayed-startup-finished", false);
       }
       return;
     }
 #endif
     let openSearchPageIfFieldIsNotActive = function(aSearchBar) {
-      if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField)
+      let doc = gBrowser.selectedBrowser.contentDocument;
+      let url = doc.documentURI.toLowerCase();
+      let mm = gBrowser.selectedBrowser.messageManager;
+
+      if (url === "about:home") {
+        AboutHome.focusInput(mm);
+      } else if (url === "about:newtab") {
+        ContentSearch.focusInput(mm);
+      } else if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
         openUILinkIn("about:home", "current");
+      }
     };
 
     let searchBar = this.searchBar;
     let placement = CustomizableUI.getPlacementOfWidget("search-container");
     let focusSearchBar = () => {
       searchBar = this.searchBar;
       searchBar.select();
       openSearchPageIfFieldIsNotActive(searchBar);
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -101,16 +101,19 @@ let AboutHomeListener = {
     }
   },
 
   receiveMessage: function(aMessage) {
     switch (aMessage.name) {
       case "AboutHome:Update":
         this.onUpdate(aMessage.data);
         break;
+      case "AboutHome:FocusInput":
+        this.onFocusInput();
+        break;
     }
   },
 
   onUpdate: function(aData) {
     let doc = content.document;
     if (doc.documentURI.toLowerCase() != "about:home")
       return;
 
@@ -133,16 +136,17 @@ let AboutHomeListener = {
     if (doc.documentURI.toLowerCase() != "about:home" ||
         doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       return;
     }
 
     doc.documentElement.setAttribute("hasBrowserHandlers", "true");
     let self = this;
     addMessageListener("AboutHome:Update", self);
+    addMessageListener("AboutHome:FocusInput", self);
     addEventListener("click", this.onClick, true);
     addEventListener("pagehide", function onPageHide(event) {
       if (event.target.defaultView.frameElement)
         return;
       removeMessageListener("AboutHome:Update", self);
       removeEventListener("click", self.onClick, true);
       removeEventListener("pagehide", onPageHide, true);
       if (event.target.documentElement)
@@ -207,16 +211,20 @@ let AboutHomeListener = {
         sendAsyncMessage("AboutHome:Sync");
         break;
 
       case "settings":
         sendAsyncMessage("AboutHome:Settings");
         break;
     }
   },
+
+  onFocusInput: function () {
+    content.document.getElementById("searchText").focus();
+  },
 };
 AboutHomeListener.init(this);
 
 
 // An event listener for custom "WebChannelMessageToChrome" events on pages
 addEventListener("WebChannelMessageToChrome", function (e) {
   // if target is window then we want the document principal, otherwise fallback to target itself.
   let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
--- a/browser/base/content/newtab/search.js
+++ b/browser/base/content/newtab/search.js
@@ -71,16 +71,20 @@ let gSearch = {
 
   onCurrentEngine: function (engineName) {
     if (this._initialStateReceived) {
       this._nodes.panel.hidePopup();
       this._setCurrentEngine(engineName);
     }
   },
 
+  onFocusInput: function () {
+    this._nodes.text.focus();
+  },
+
   _nodeIDSuffixes: [
     "form",
     "logo",
     "manage",
     "panel",
     "text",
   ],
 
--- a/browser/base/content/test/general/browser_aboutHome.js
+++ b/browser/base/content/test/general/browser_aboutHome.js
@@ -414,16 +414,32 @@ let gTests = [
 
       // Empty the search input, causing the suggestions to be hidden.
       EventUtils.synthesizeKey("a", { accelKey: true });
       EventUtils.synthesizeKey("VK_DELETE", {});
       ok(table.hidden, "Search suggestion table hidden");
     });
   }
 },
+{
+  desc: "Cmd+k should focus the search bar element",
+  setup: function () {},
+  run: Task.async(function* () {
+    let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
+    let logo = doc.getElementById("brandLogo");
+    let searchInput = doc.getElementById("searchText");
+
+    EventUtils.synthesizeMouseAtCenter(logo, {});
+    isnot(searchInput, doc.activeElement, "Search input should not be the active element.");
+
+    EventUtils.synthesizeKey("k", { accelKey: true });
+    yield promiseWaitForCondition(() => doc.activeElement === searchInput);
+    is(searchInput, doc.activeElement, "Search input should be the active element.");
+  })
+},
 
 ];
 
 function test()
 {
   waitForExplicitFinish();
   requestLongerTimeout(2);
   ignoreAllUncaughtExceptions();
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -77,16 +77,22 @@ function waitForCondition(condition, nex
     if (conditionPassed) {
       moveOn();
     }
     tries++;
   }, 100);
   var moveOn = function() { clearInterval(interval); nextTest(); };
 }
 
+function promiseWaitForCondition(aConditionFn) {
+  let deferred = Promise.defer();
+  waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.");
+  return deferred.promise;
+}
+
 function getTestPlugin(aName) {
   var pluginName = aName || "Test Plug-in";
   var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
   var tags = ph.getPluginTags();
 
   // Find the test plugin
   for (var i = 0; i < tags.length; i++) {
     if (tags[i].name == pluginName)
--- a/browser/base/content/test/newtab/browser_newtab_search.js
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -181,16 +181,26 @@ function runTests() {
   yield undefined;
   yield suggestionsPromise.then(TestRunner.next);
 
   // Empty the search input, causing the suggestions to be hidden.
   EventUtils.synthesizeKey("a", { accelKey: true });
   EventUtils.synthesizeKey("VK_DELETE", {});
   ok(table.hidden, "Search suggestion table hidden");
 
+  // Focus a different element than the search input.
+  let btn = getContentDocument().getElementById("newtab-customize-button");
+  yield promiseClick(btn).then(TestRunner.next);
+
+  isnot(input, getContentDocument().activeElement, "Search input should not be focused");
+  // Test that Ctrl/Cmd + K will focus the input field.
+  EventUtils.synthesizeKey("k", { accelKey: true });
+  yield promiseSearchEvents(["FocusInput"]).then(TestRunner.next);
+  is(input, getContentDocument().activeElement, "Search input should be focused");
+
   // Done.  Revert the current engine and remove the new engines.
   Services.search.currentEngine = oldCurrentEngine;
   yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
 
   let events = [];
   for (let engine of gNewEngines) {
     Services.search.removeEngine(engine);
     events.push("CurrentState");
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -304,16 +304,18 @@ function openLinkIn(url, where, params) 
       loadInBackground = false;
     }
   }
 
   // Raise the target window before loading the URI, since loading it may
   // result in a new frontmost window (e.g. "javascript:window.open('');").
   w.focus();
 
+  let newTab;
+
   switch (where) {
   case "current":
     let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
 
     if (aAllowThirdPartyFixup) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
     }
@@ -327,32 +329,39 @@ function openLinkIn(url, where, params) 
 
     w.gBrowser.loadURIWithFlags(url, flags, aReferrerURI, null, aPostData);
     break;
   case "tabshifted":
     loadInBackground = !loadInBackground;
     // fall through
   case "tab":
     let browser = w.gBrowser;
-    browser.loadOneTab(url, {
-                       referrerURI: aReferrerURI,
-                       charset: aCharset,
-                       postData: aPostData,
-                       inBackground: loadInBackground,
-                       allowThirdPartyFixup: aAllowThirdPartyFixup,
-                       relatedToCurrent: aRelatedToCurrent,
-                       skipAnimation: aSkipTabAnimation,
-                       allowMixedContent: aAllowMixedContent });
+    newTab = browser.loadOneTab(url, {
+                                referrerURI: aReferrerURI,
+                                charset: aCharset,
+                                postData: aPostData,
+                                inBackground: loadInBackground,
+                                allowThirdPartyFixup: aAllowThirdPartyFixup,
+                                relatedToCurrent: aRelatedToCurrent,
+                                skipAnimation: aSkipTabAnimation,
+                                allowMixedContent: aAllowMixedContent });
     break;
   }
 
   w.gBrowser.selectedBrowser.focus();
 
-  if (!loadInBackground && w.isBlankPageURL(url))
+  if (!loadInBackground && w.isBlankPageURL(url)) {
+    if (newTab) {
+      // Remote tab content does not focus synchronously, so we set the flag
+      // on this tab to skip focusing the content if we want to focus the URL
+      // bar instead.
+      newTab._urlbarFocused = true;
+    }
     w.focusAndSelectUrlBar();
+  }
 }
 
 // Used as an onclick handler for UI elements with link-like behavior.
 // e.g. onclick="checkForMiddleClick(this, event);"
 function checkForMiddleClick(node, event) {
   // We should be using the disabled property here instead of the attribute,
   // but some elements that this function is used with don't support it (e.g.
   // menuitem).
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -156,16 +156,37 @@ function injectLoopAPI(targetWindow) {
       enumerable: true,
       writable: true,
       value: function(prefName) {
         return MozLoopService.getLoopCharPref(prefName);
       }
     },
 
     /**
+     * Return any preference under "loop." that's coercible to a boolean
+     * preference.
+     *
+     * @param {String} prefName The name of the pref without the preceding
+     * "loop."
+     *
+     * Any errors thrown by the Mozilla pref API are logged to the console
+     * and cause null to be returned. This includes the case of the preference
+     * not being found.
+     *
+     * @return {String} on success, null on error
+     */
+    getLoopBoolPref: {
+      enumerable: true,
+      writable: true,
+      value: function(prefName) {
+        return MozLoopService.getLoopBoolPref(prefName);
+      }
+    },
+
+    /**
      * Starts alerting the user about an incoming call
      */
     startAlerting: {
       enumerable: true,
       writable: true,
       value: function() {
         let chromeWindow = getChromeWindow(targetWindow);
         chromeWindow.getAttention();
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -609,16 +609,39 @@ this.MozLoopService = {
     } catch (ex) {
       console.log("getLoopCharPref had trouble getting " + prefName +
         "; exception: " + ex);
       return null;
     }
   },
 
   /**
+   * Return any preference under "loop." that's coercible to a character
+   * preference.
+   *
+   * @param {String} prefName The name of the pref without the preceding
+   * "loop."
+   *
+   * Any errors thrown by the Mozilla pref API are logged to the console
+   * and cause null to be returned. This includes the case of the preference
+   * not being found.
+   *
+   * @return {String} on success, null on error
+   */
+  getLoopBoolPref: function(prefName) {
+    try {
+      return Services.prefs.getBoolPref("loop." + prefName);
+    } catch (ex) {
+      console.log("getLoopBoolPref had trouble getting " + prefName +
+        "; exception: " + ex);
+      return null;
+    }
+  },
+
+  /**
    * Performs a hawk based request to the loop server.
    *
    * @param {String} path The path to make the request to.
    * @param {String} method The request method, e.g. 'POST', 'GET'.
    * @param {Object} payloadObj An object which is converted to JSON and
    *                            transmitted with the request.
    * @returns {Promise}
    *        Returns a promise that resolves to the response of the API call,
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -27,34 +27,37 @@ loop.conversation = (function(OT, mozL10
       model: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {showDeclineMenu: false};
     },
 
     componentDidMount: function() {
-      window.addEventListener('click', this.clickHandler);
-      window.addEventListener('blur', this._hideDeclineMenu);
+      window.addEventListener("click", this.clickHandler);
+      window.addEventListener("blur", this._hideDeclineMenu);
     },
 
     componentWillUnmount: function() {
-      window.removeEventListener('click', this.clickHandler);
-      window.removeEventListener('blur', this._hideDeclineMenu);
+      window.removeEventListener("click", this.clickHandler);
+      window.removeEventListener("blur", this._hideDeclineMenu);
     },
 
     clickHandler: function(e) {
       var target = e.target;
       if (!target.classList.contains('btn-chevron')) {
         this._hideDeclineMenu();
       }
     },
 
-    _handleAccept: function() {
-      this.props.model.trigger("accept");
+    _handleAccept: function(callType) {
+      return () => {
+        this.props.model.set("selectedCallType", callType);
+        this.props.model.trigger("accept");
+      };
     },
 
     _handleDecline: function() {
       this.props.model.trigger("decline");
     },
 
     _handleDeclineBlock: function(e) {
       this.props.model.trigger("declineAndBlock");
@@ -69,50 +72,64 @@ loop.conversation = (function(OT, mozL10
     },
 
     _hideDeclineMenu: function() {
       this.setState({showDeclineMenu: false});
     },
 
     render: function() {
       /* jshint ignore:start */
-      var btnClassAccept = "btn btn-success btn-accept";
+      var btnClassAccept = "btn btn-success btn-accept call-audio-video";
       var btnClassBlock = "btn btn-error btn-block";
       var btnClassDecline = "btn btn-error btn-decline";
       var conversationPanelClass = "incoming-call " +
                                   loop.shared.utils.getTargetPlatform();
       var cx = React.addons.classSet;
-      var declineDropdownMenuClasses = cx({
+      var dropdownMenuClassesDecline = cx({
         "native-dropdown-menu": true,
-        "decline-block-menu": true,
+        "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showDeclineMenu
       });
       return (
         React.DOM.div({className: conversationPanelClass}, 
           React.DOM.h2(null, __("incoming_call")), 
           React.DOM.div({className: "button-group incoming-call-action-group"}, 
             React.DOM.div({className: "button-chevron-menu-group"}, 
               React.DOM.div({className: "button-group-chevron"}, 
                 React.DOM.div({className: "button-group"}, 
-                  React.DOM.button({className: btnClassDecline, onClick: this._handleDecline}, 
+
+                  React.DOM.button({className: btnClassDecline, 
+                          onClick: this._handleDecline}, 
                     __("incoming_call_decline_button")
                   ), 
                   React.DOM.div({className: "btn-chevron", 
-                    onClick: this._toggleDeclineMenu}
+                       onClick: this._toggleDeclineMenu}
                   )
                 ), 
-                React.DOM.ul({className: declineDropdownMenuClasses}, 
+
+                React.DOM.ul({className: dropdownMenuClassesDecline}, 
                   React.DOM.li({className: "btn-block", onClick: this._handleDeclineBlock}, 
                     __("incoming_call_decline_and_block_button")
                   )
                 )
+
               )
             ), 
-            React.DOM.button({className: btnClassAccept, onClick: this._handleAccept}, 
-              __("incoming_call_answer_button")
+
+            React.DOM.div({className: "button-chevron-menu-group"}, 
+              React.DOM.div({className: "button-group"}, 
+                React.DOM.button({className: btnClassAccept, 
+                        onClick: this._handleAccept("audio-video")}, 
+                  __("incoming_call_answer_button")
+                ), 
+                React.DOM.div({className: "call-audio-only", 
+                     onClick: this._handleAccept("audio"), 
+                     title: __("incoming_call_answer_audio_only_tooltip")}
+                )
+              )
             )
           )
         )
       );
       /* jshint ignore:end */
     }
   });
 
@@ -176,19 +193,20 @@ loop.conversation = (function(OT, mozL10
           this._notifier.errorL10n("cannot_start_call_session_not_ready");
           return;
         }
         // XXX For incoming calls we might have more than one call queued.
         // For now, we'll just assume the first call is the right information.
         // We'll probably really want to be getting this data from the
         // background worker on the desktop client.
         // Bug 1032700 should fix this.
-        this._conversation.setSessionData(sessionData[0]);
+        this._conversation.setIncomingSessionData(sessionData[0]);
         this.loadReactComponent(loop.conversation.IncomingCallView({
-          model: this._conversation
+          model: this._conversation,
+          video: {enabled: this._conversation.hasVideoStream("incoming")}
         }));
       });
     },
 
     /**
      * Accepts an incoming call.
      */
     accept: function() {
@@ -208,17 +226,17 @@ loop.conversation = (function(OT, mozL10
     /**
      * Decline and block an incoming call
      * @note:
      * - loopToken is the callUrl identifier. It gets set in the panel
      *   after a callUrl is received
      */
     declineAndBlock: function() {
       navigator.mozLoop.stopAlerting();
-      var token = navigator.mozLoop.getLoopCharPref('loopToken');
+      var token = navigator.mozLoop.getLoopCharPref("loopToken");
       this._client.deleteCallUrl(token, function(error) {
         // XXX The conversation window will be closed when this cb is triggered
         // figure out if there is a better way to report the error to the user
         // (bug 1048909).
         console.log(error);
       });
       window.close();
     },
@@ -230,20 +248,24 @@ loop.conversation = (function(OT, mozL10
     conversation: function() {
       if (!this._conversation.isSessionReady()) {
         console.error("Error: navigated to conversation route without " +
           "the start route to initialise the call first");
         this._notifier.errorL10n("cannot_start_call_session_not_ready");
         return;
       }
 
+      var callType = this._conversation.get("selectedCallType");
+      var videoStream = callType === "audio" ? false : true;
+
       /*jshint newcap:false*/
       this.loadReactComponent(sharedViews.ConversationView({
         sdk: OT,
-        model: this._conversation
+        model: this._conversation,
+        video: {enabled: videoStream}
       }));
     },
 
     /**
      * Call has ended, display a feedback form.
      */
     feedback: function() {
       document.title = mozL10n.get("call_has_ended");
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -27,34 +27,37 @@ loop.conversation = (function(OT, mozL10
       model: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {showDeclineMenu: false};
     },
 
     componentDidMount: function() {
-      window.addEventListener('click', this.clickHandler);
-      window.addEventListener('blur', this._hideDeclineMenu);
+      window.addEventListener("click", this.clickHandler);
+      window.addEventListener("blur", this._hideDeclineMenu);
     },
 
     componentWillUnmount: function() {
-      window.removeEventListener('click', this.clickHandler);
-      window.removeEventListener('blur', this._hideDeclineMenu);
+      window.removeEventListener("click", this.clickHandler);
+      window.removeEventListener("blur", this._hideDeclineMenu);
     },
 
     clickHandler: function(e) {
       var target = e.target;
       if (!target.classList.contains('btn-chevron')) {
         this._hideDeclineMenu();
       }
     },
 
-    _handleAccept: function() {
-      this.props.model.trigger("accept");
+    _handleAccept: function(callType) {
+      return () => {
+        this.props.model.set("selectedCallType", callType);
+        this.props.model.trigger("accept");
+      };
     },
 
     _handleDecline: function() {
       this.props.model.trigger("decline");
     },
 
     _handleDeclineBlock: function(e) {
       this.props.model.trigger("declineAndBlock");
@@ -69,51 +72,65 @@ loop.conversation = (function(OT, mozL10
     },
 
     _hideDeclineMenu: function() {
       this.setState({showDeclineMenu: false});
     },
 
     render: function() {
       /* jshint ignore:start */
-      var btnClassAccept = "btn btn-success btn-accept";
+      var btnClassAccept = "btn btn-success btn-accept call-audio-video";
       var btnClassBlock = "btn btn-error btn-block";
       var btnClassDecline = "btn btn-error btn-decline";
       var conversationPanelClass = "incoming-call " +
                                   loop.shared.utils.getTargetPlatform();
       var cx = React.addons.classSet;
-      var declineDropdownMenuClasses = cx({
+      var dropdownMenuClassesDecline = cx({
         "native-dropdown-menu": true,
-        "decline-block-menu": true,
+        "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showDeclineMenu
       });
       return (
         <div className={conversationPanelClass}>
           <h2>{__("incoming_call")}</h2>
           <div className="button-group incoming-call-action-group">
             <div className="button-chevron-menu-group">
               <div className="button-group-chevron">
                 <div className="button-group">
-                  <button className={btnClassDecline} onClick={this._handleDecline}>
+
+                  <button className={btnClassDecline}
+                          onClick={this._handleDecline}>
                     {__("incoming_call_decline_button")}
                   </button>
                   <div className="btn-chevron"
-                    onClick={this._toggleDeclineMenu}>
+                       onClick={this._toggleDeclineMenu}>
                   </div>
                 </div>
-                <ul className={declineDropdownMenuClasses}>
+
+                <ul className={dropdownMenuClassesDecline}>
                   <li className="btn-block" onClick={this._handleDeclineBlock}>
                     {__("incoming_call_decline_and_block_button")}
                   </li>
                 </ul>
+
               </div>
             </div>
-            <button className={btnClassAccept} onClick={this._handleAccept}>
-              {__("incoming_call_answer_button")}
-            </button>
+
+            <div className="button-chevron-menu-group">
+              <div className="button-group">
+                <button className={btnClassAccept}
+                        onClick={this._handleAccept("audio-video")}>
+                  {__("incoming_call_answer_button")}
+                </button>
+                <div className="call-audio-only"
+                     onClick={this._handleAccept("audio")}
+                     title={__("incoming_call_answer_audio_only_tooltip")} >
+                </div>
+              </div>
+            </div>
           </div>
         </div>
       );
       /* jshint ignore:end */
     }
   });
 
   /**
@@ -176,19 +193,20 @@ loop.conversation = (function(OT, mozL10
           this._notifier.errorL10n("cannot_start_call_session_not_ready");
           return;
         }
         // XXX For incoming calls we might have more than one call queued.
         // For now, we'll just assume the first call is the right information.
         // We'll probably really want to be getting this data from the
         // background worker on the desktop client.
         // Bug 1032700 should fix this.
-        this._conversation.setSessionData(sessionData[0]);
+        this._conversation.setIncomingSessionData(sessionData[0]);
         this.loadReactComponent(loop.conversation.IncomingCallView({
-          model: this._conversation
+          model: this._conversation,
+          video: {enabled: this._conversation.hasVideoStream("incoming")}
         }));
       });
     },
 
     /**
      * Accepts an incoming call.
      */
     accept: function() {
@@ -208,17 +226,17 @@ loop.conversation = (function(OT, mozL10
     /**
      * Decline and block an incoming call
      * @note:
      * - loopToken is the callUrl identifier. It gets set in the panel
      *   after a callUrl is received
      */
     declineAndBlock: function() {
       navigator.mozLoop.stopAlerting();
-      var token = navigator.mozLoop.getLoopCharPref('loopToken');
+      var token = navigator.mozLoop.getLoopCharPref("loopToken");
       this._client.deleteCallUrl(token, function(error) {
         // XXX The conversation window will be closed when this cb is triggered
         // figure out if there is a better way to report the error to the user
         // (bug 1048909).
         console.log(error);
       });
       window.close();
     },
@@ -230,20 +248,24 @@ loop.conversation = (function(OT, mozL10
     conversation: function() {
       if (!this._conversation.isSessionReady()) {
         console.error("Error: navigated to conversation route without " +
           "the start route to initialise the call first");
         this._notifier.errorL10n("cannot_start_call_session_not_ready");
         return;
       }
 
+      var callType = this._conversation.get("selectedCallType");
+      var videoStream = callType === "audio" ? false : true;
+
       /*jshint newcap:false*/
       this.loadReactComponent(sharedViews.ConversationView({
         sdk: OT,
-        model: this._conversation
+        model: this._conversation,
+        video: {enabled: videoStream}
       }));
     },
 
     /**
      * Call has ended, display a feedback form.
      */
     feedback: function() {
       document.title = mozL10n.get("call_has_ended");
--- a/browser/components/loop/content/shared/css/common.css
+++ b/browser/components/loop/content/shared/css/common.css
@@ -104,16 +104,21 @@ h1, h2, h3 {
 .btn-large {
   /* Dimensions from spec
    * https://people.mozilla.org/~dhenein/labs/loop-link-spec/#call-start */
   padding: .5em;
   font-size: 18px;
   height: auto;
 }
 
+  .btn-large + .btn-chevron {
+    padding: 1rem;
+    height: 100%; /* match full height of button */
+  }
+
 /*
  * Left / Right padding elements
  * used to center components
  * */
 .flex-padding-1 {
   display: flex;
   flex: 1;
 }
@@ -128,27 +133,30 @@ h1, h2, h3 {
     border: 1px solid #008acb;
   }
 
   .btn-info:active {
     background-color: #006b9d;
     border: 1px solid #006b9d;
   }
 
-.btn-success {
+.btn-success,
+.btn-success + .btn-chevron {
   background-color: #74bf43;
   border: 1px solid #74bf43;
 }
 
-  .btn-success:hover {
+  .btn-success:hover,
+  .btn-success + .btn-chevron:hover {
     background-color: #6cb23e;
     border: 1px solid #6cb23e;
   }
 
-  .btn-success:active {
+  .btn-success:active,
+  .btn-success + .btn-chevron:active {
     background-color: #64a43a;
     border: 1px solid #64a43a;
   }
 
 .btn-warning {
   background-color: #f0ad4e;
 }
 
@@ -229,16 +237,18 @@ h1, h2, h3 {
   display: flex;
   width: 100%;
   align-content: space-between;
   justify-content: center;
 }
 
 .button-group .btn {
   flex: 1;
+  border-bottom-right-radius: 0;
+  border-top-right-radius: 0;
 }
 
 /* Alerts */
 .alert {
   background: #eee;
   padding: .2em 1em;
   margin-bottom: 1em;
 }
@@ -287,34 +297,46 @@ h1, h2, h3 {
 }
 
 /* Transitions */
 .fade-out {
   transition: opacity 0.5s ease-in;
   opacity: 0;
 }
 
-.btn-large .icon {
-  display: inline-block;
-  width: 20px;
-  height: 20px;
+.icon,
+.icon-small,
+.icon-audio,
+.icon-video {
   background-size: 20px;
   background-repeat: no-repeat;
   vertical-align: top;
-  margin-left: 10px;
+  background-position: 80% center;
+}
+
+.icon-small {
+  background-size: 10px;
 }
 
 .icon-video {
   background-image: url("../img/video-inverse-14x14.png");
 }
 
+.icon-audio {
+  background-image: url("../img/audio-default-16x16@1.5x.png");
+}
+
 @media (min-resolution: 2dppx) {
   .icon-video {
     background-image: url("../img/video-inverse-14x14@2x.png");
   }
+
+  .icon-audio {
+    background-image: url("../img/audio-default-16x16@2x.png");
+  }
 }
 
 /*
  * Platform specific styles
  * The UI should match the user OS
  * Specific font sizes for text paragraphs to match
  * the interface on that platform.
  */
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -189,16 +189,51 @@
   margin-left: .5em;
 }
 
 .incoming-call h2 {
   font-size: 1.5em;
   font-weight: normal;
 }
 
+.call-audio-only {
+  width: 26px;
+  height: 26px;
+  border-left: 1px solid rgba(255,255,255,.4);
+  border-top-right-radius: 2px;
+  border-bottom-right-radius: 2px;
+  background-color: #74BF43;
+  background-image: url("../img/audio-inverse-14x14.png");
+  background-size: 1rem;
+  background-position: center;
+  background-repeat: no-repeat;
+  cursor: pointer;
+}
+
+  .call-audio-only:hover {
+    background-color: #6cb23e;
+  }
+
+
+.call-audio-video {
+  background-image: url("../img/video-inverse-14x14.png");
+  background-position: 96% center;
+  background-repeat: no-repeat;
+  background-size: 1rem;
+}
+
+@media (min-resolution: 2dppx) {
+  .call-audio-only {
+    background-image: url("../img/audio-inverse-14x14@2x.png");
+  }
+  .call-audio-video {
+    background-image: url("../img/video-inverse-14x14@2x.png");
+  }
+}
+
 /* Expired call url page */
 
 .expired-url-info {
   width: 400px;
   margin: 0 auto;
 }
 
 .promote-firefox {
@@ -207,43 +242,66 @@
   line-height: 24px;
   margin: 2em 0;
 }
 
 .promote-firefox h3 {
   font-weight: 300;
 }
 
-/* Block incoming call */
+/*
+ * Dropdown menu hidden behind a chevron
+ *
+ * .native-dropdown-menu[-large-parent] Generic class, contains common styles
+ * .standalone-dropdown-menu Initiate call dropdown menu
+ * .conversation-window-dropdown Dropdown menu for answer/decline/block options
+ */
 
-.native-dropdown-menu {
+.native-dropdown-menu,
+.native-dropdown-large-parent {
   /* Should match a native select menu */
   padding: 0;
   position: absolute; /* element can be wider than the parent */
   background: #fff;
   margin: 0;
   box-shadow: 0 4px 5px rgba(30, 30, 30, .3);
   border-style: solid;
   border-width: 1px 1px 1px 2px;
   border-color: #aaa #111 #111 #aaa;
 }
 
-.decline-block-menu li {
+  /*
+   * If the component is smaller than the parent
+   * we need it to display block to occupy full width
+   * Same as above but overrides apropriate styles
+   */
+  .native-dropdown-large-parent {
+    position: relative;
+    display: block;
+  }
+
+  .native-dropdown-menu li,
+  .native-dropdown-large-parent li {
+    list-style: none;
+    cursor: pointer;
+    color: #000;
+  }
+
+  .native-dropdown-menu li:hover,
+  .native-dropdown-large-parent li:hover,
+  .native-dropdown-large-parent li:hover button {
+    color: #fff;
+    background-color: #111;
+  }
+
+.conversation-window-dropdown li {
   padding: 0 10px 0 5px;
-  list-style: none;
   font-size: .9em;
-  color: #000;
-  cursor: pointer;
 }
 
-  .decline-block-menu li:hover {
-    color: #FFF;
-    background: #111;
-  }
-
 /* Expired call url page */
 
 .expired-url-info {
   width: 400px;
   margin: 0 auto;
 }
 
 .promote-firefox {
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fdef44157a2a18c0362e1219341de82a3ffdc12f
GIT binary patch
literal 424
zc$@*K0ayNsP)<h;3K|Lk000e1NJLTq000;O000;W1ONa4N`Cco0004RNkl<Zcmd7S
zv1=4T0EO}IjdKtKiU>lmNh61Xuwtw%6l0S@1w{$QG9X%sg;)uKofcxJe}SMy8Y{s@
z!A8O4MB$292o@58#3Gv8)n~EGF|&6~I)7jDn(1anjF>+YZmRGm@3~8l7G0+0zHZTQ
zr3Ido(E=&Y>J_P<?(&^PFln44w_B$~S`?W>v>hZ9$yg$1k%?w|0{`y;Ut^g$#?Wn+
zecaSVQwviR>@j2PSD{@rVqkkB4VU6?M<p#T-3hoHy&8Q`!3u3X6;Smn>OwQ{2hi#2
znFpl4($i6|#cz?iFdVC1;z*qHb#Kw|+MjtnVuA-_-^14QM9-7ltCr#-1)F+TK8<&D
zg^&FiN*bDhp#z6^ykOd^eyJ5ts^F~l^7_{W))~@yQgL6r!J?+9efHz(2?G+kMBA56
zcSAeDZ#cn-7-{R4GJpn*5k%HiebNsm0esTye(PD2l>Zt&riZlXcX^QwhW-JB9goF0
S*`kL40000<MNUMnLSTY#O}w)J
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..71ed8d8e3bf9df9217455979547bfbf64bf274b8
GIT binary patch
literal 536
zc$@(k0_XjSP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80005sNkl<ZSi@sr
zAQ6OyhVn;7Mz#R){-~&^cR>6Q$le$c5s?IxW@Mlx09xV=wCp+1(*M{nQ0^E|jRH+9
z4-XI51X}VHm*q(K3^0_~sO|uu?@kk8IT!;&p`NOi0}WLr)^cc6+@iMcJxFxGZ)zt#
zKT;e3!a!|IqYfB#z^DT#wKN-w4as!?P+uU(3}7+tLr&=jD^$wJbpTNBA*dN!MjbHp
z901hx4XU{j=m2LDE2D2vPn?B{ts=((cc7Y=0vi|-K>9BUjyMGh0cevA<e&-UIAAW+
zu=frQ4je%KN)j3*vB;qY%8ule0}e=r)&j90r~yhu@Gk>3PJ!&>Q2r}lUtcbA8zQ@4
z4Gdsm;t6Cc0r5^;jSwL20$B#6`;p}G$u13naW4&P1q10nK->!y<AJu2nnBHRAYKB*
zr9g2lXwz*ElB2d#+*Aifg)GpZi%5n(0!2w!SeP9!RFpv$gNj<9x@kbUA4uxv8XFt4
zQl&D2`WocZKSU(L_dv%K(xh(yiV{$h8<eoHSbQERmjG15$v|rWv}_KRL>Rz8FEAhu
z00ylt(2{DPWmACkH7rrE4OHR*>72m8Kw)ZkOe#sN3qAm`4OJ`$`bG?B=vttG>qx{z
amHz-e&RiG!)_<%30000<MNUMnLSTZtwbGCP
--- a/browser/components/loop/content/shared/js/models.js
+++ b/browser/components/loop/content/shared/js/models.js
@@ -9,27 +9,31 @@ loop.shared = loop.shared || {};
 loop.shared.models = (function() {
   "use strict";
 
   /**
    * Conversation model.
    */
   var ConversationModel = Backbone.Model.extend({
     defaults: {
-      connected:    false,     // Session connected flag
-      ongoing:      false,     // Ongoing call flag
-      callerId:     undefined, // Loop caller id
-      loopToken:    undefined, // Loop conversation token
-      loopVersion:  undefined, // Loop version for /calls/ information. This
-                               // is the version received from the push
-                               // notification and is used by the server to
-                               // determine the pending calls
-      sessionId:    undefined, // OT session id
-      sessionToken: undefined, // OT session token
-      apiKey:       undefined  // OT api key
+      connected:    false,         // Session connected flag
+      ongoing:      false,         // Ongoing call flag
+      callerId:     undefined,     // Loop caller id
+      loopToken:    undefined,     // Loop conversation token
+      loopVersion:  undefined,     // Loop version for /calls/ information. This
+                                   // is the version received from the push
+                                   // notification and is used by the server to
+                                   // determine the pending calls
+      sessionId:    undefined,     // OT session id
+      sessionToken: undefined,     // OT session token
+      apiKey:       undefined,     // OT api key
+      callType:     undefined,     // The type of incoming call selected by
+                                   // other peer ("audio" or "audio-video")
+      selectedCallType: undefined  // The selected type for the call that was
+                                   // initiated ("audio" or "audio-video")
     },
 
     /**
      * SDK object.
      * @type {OT}
      */
     sdk: undefined,
 
@@ -109,44 +113,60 @@ loop.shared.models = (function() {
           this.trigger("timeout").endSession();
         }
       }
 
       // Setup pending call timeout.
       this._pendingCallTimer = setTimeout(
         handleOutgoingCallTimeout.bind(this), this.pendingCallTimeout);
 
-      this.setSessionData(sessionData);
+      this.setOutgoingSessionData(sessionData);
       this.trigger("call:outgoing");
     },
 
     /**
      * Checks that the session is ready.
      *
      * @return {Boolean}
      */
     isSessionReady: function() {
       return !!this.get("sessionId");
     },
 
     /**
      * Sets session information.
+     * Session data received by creating an outgoing call.
      *
      * @param {Object} sessionData Conversation session information.
      */
-    setSessionData: function(sessionData) {
+    setOutgoingSessionData: function(sessionData) {
       // Explicit property assignment to prevent later "surprises"
       this.set({
         sessionId:    sessionData.sessionId,
         sessionToken: sessionData.sessionToken,
         apiKey:       sessionData.apiKey
       });
     },
 
     /**
+     * Sets session information about the incoming call.
+     *
+     * @param {Object} sessionData Conversation session information.
+     */
+    setIncomingSessionData: function(sessionData) {
+      // Explicit property assignment to prevent later "surprises"
+      this.set({
+        sessionId:    sessionData.sessionId,
+        sessionToken: sessionData.sessionToken,
+        apiKey:       sessionData.apiKey,
+        callType:     sessionData.callType || "audio-video"
+      });
+    },
+
+    /**
      * Starts a SDK session and subscribe to call events.
      */
     startSession: function() {
       if (!this.isSessionReady()) {
         throw new Error("Can't start session as it's not ready");
       }
       this.session = this.sdk.initSession(this.get("sessionId"));
       this.listenTo(this.session, "streamCreated", this._streamCreated);
@@ -165,16 +185,32 @@ loop.shared.models = (function() {
      */
     endSession: function() {
       this.session.disconnect();
       this.set("ongoing", false)
           .once("session:ended", this.stopListening, this);
     },
 
     /**
+     * Helper function to determine if video stream is available for the
+     * incoming or outgoing call
+     *
+     * @param {string} callType Incoming or outgoing call
+     */
+    hasVideoStream: function(callType) {
+      if (callType === "incoming") {
+        return this.get("callType") === "audio-video";
+      }
+      if (callType === "outgoing") {
+        return this.get("selectedCallType") === "audio-video";
+      }
+      return undefined;
+    },
+
+    /**
      * Handle a loop-server error, which has an optional `errno` property which
      * is server error identifier.
      *
      * Triggers the following events:
      *
      * - `session:expired` for expired call urls
      * - `session:error` for other generic errors
      *
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -212,23 +212,34 @@ loop.shared.views = (function(_, OT, l10
       height: "100%",
       style: {
         bugDisplayMode: "off",
         buttonDisplayMode: "off",
         nameDisplayMode: "off"
       }
     },
 
+    getInitialProps: function() {
+      return {
+        video: {enabled: true},
+        audio: {enabled: true}
+      };
+    },
+
     getInitialState: function() {
       return {
-        video: {enabled: false},
-        audio: {enabled: false}
+        video: this.props.video,
+        audio: this.props.audio
       };
     },
 
+    componentWillMount: function() {
+      this.publisherConfig.publishVideo = this.props.video.enabled;
+    },
+
     componentDidMount: function() {
       this.listenTo(this.props.model, "session:connected",
                                       this.startPublishing);
       this.listenTo(this.props.model, "session:stream-created",
                                       this._streamCreated);
       this.listenTo(this.props.model, ["session:peer-hungup",
                                        "session:network-disconnected",
                                        "session:ended"].join(" "),
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -212,23 +212,34 @@ loop.shared.views = (function(_, OT, l10
       height: "100%",
       style: {
         bugDisplayMode: "off",
         buttonDisplayMode: "off",
         nameDisplayMode: "off"
       }
     },
 
+    getInitialProps: function() {
+      return {
+        video: {enabled: true},
+        audio: {enabled: true}
+      };
+    },
+
     getInitialState: function() {
       return {
-        video: {enabled: false},
-        audio: {enabled: false}
+        video: this.props.video,
+        audio: this.props.audio
       };
     },
 
+    componentWillMount: function() {
+      this.publisherConfig.publishVideo = this.props.video.enabled;
+    },
+
     componentDidMount: function() {
       this.listenTo(this.props.model, "session:connected",
                                       this.startPublishing);
       this.listenTo(this.props.model, "session:stream-created",
                                       this._streamCreated);
       this.listenTo(this.props.model, ["session:peer-hungup",
                                        "session:network-disconnected",
                                        "session:ended"].join(" "),
--- a/browser/components/loop/standalone/content/css/webapp.css
+++ b/browser/components/loop/standalone/content/css/webapp.css
@@ -119,8 +119,44 @@ header {
   font-weight: lighter;
 }
 
 .light-color-font {
   opacity: .4;
   font-weight: normal;
 }
 
+.start-audio-only-call,
+.start-audio-video-call {
+  background-color: none;
+  background-image: url("../shared/img/audio-default-16x16@1.5x.png");
+  background-position: 80% center;
+  background-size: 10px;
+  background-repeat: no-repeat;
+  cursor: pointer;
+}
+
+.start-audio-only-call {
+  border: none;
+  width: 100%;
+}
+
+.start-audio-only-call:hover {
+  background-image: url("../shared/img/audio-inverse-14x14.png");
+}
+
+.start-audio-video-call {
+  background-size: 20px;
+  background-image: url("../shared/img/video-inverse-14x14.png");
+}
+
+@media (min-resolution: 2dppx) {
+  .start-audio-only-call {
+    background-image: url("../shared/img/audio-default-16x16@2x.png");
+  }
+  .start-audio-only-call:hover {
+    background-image: url("../shared/img/audio-inverse-14x14@2x.png");
+  }
+  .start-audio-video-call {
+    background-image: url("../shared/img/video-inverse-14x14@2x.png");
+  }
+}
+
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -139,76 +139,109 @@ loop.webapp = (function($, _, OT, webL10
      * - {loop.shared.model.ConversationModel}    model    Conversation model.
      * - {loop.shared.views.NotificationListView} notifier Notifier component.
      *
      */
 
     getInitialState: function() {
       return {
         urlCreationDateString: '',
-        disableCallButton: false
+        disableCallButton: false,
+        showCallOptionsMenu: false
       };
     },
 
     propTypes: {
       model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                                        .isRequired,
       // XXX Check more tightly here when we start injecting window.loop.*
       notifier: React.PropTypes.object.isRequired,
       client: React.PropTypes.object.isRequired
     },
 
     componentDidMount: function() {
+      // Listen for events & hide dropdown menu if user clicks away
+      window.addEventListener("click", this.clickHandler);
       this.props.model.listenTo(this.props.model, "session:error",
                                 this._onSessionError);
       this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
                                            this._setConversationTimestamp);
       // XXX DOM element does not exist before React view gets instantiated
       // We should turn the notifier into a react component
       this.props.notifier.$el = $("#messages");
     },
 
     _onSessionError: function(error) {
       console.error(error);
       this.props.notifier.errorL10n("unable_retrieve_call_info");
     },
 
     /**
      * Initiates the call.
+     * Takes in a call type parameter "audio" or "audio-video" and returns
+     * a function that initiates the call. React click handler requires a function
+     * to be called when that event happenes.
+     *
+     * @param {string} User call type choice "audio" or "audio-video"
      */
-    _initiateOutgoingCall: function() {
-      this.setState({disableCallButton: true});
-      this.props.model.setupOutgoingCall();
+    _initiateOutgoingCall: function(callType) {
+      return function() {
+        this.props.model.set("selectedCallType", callType);
+        this.setState({disableCallButton: true});
+        this.props.model.setupOutgoingCall();
+      }.bind(this);
     },
 
     _setConversationTimestamp: function(err, callUrlInfo) {
       if (err) {
         this.props.notifier.errorL10n("unable_retrieve_call_info");
       } else {
         var date = (new Date(callUrlInfo.urlCreationDate * 1000));
         var options = {year: "numeric", month: "long", day: "numeric"};
         var timestamp = date.toLocaleDateString(navigator.language, options);
 
         this.setState({urlCreationDateString: timestamp});
       }
     },
 
+    componentWillUnmount: function() {
+      window.removeEventListener("click", this.clickHandler);
+    },
+
+    clickHandler: function(e) {
+      if (!e.target.classList.contains('btn-chevron') &&
+          this.state.showCallOptionsMenu) {
+            this._toggleCallOptionsMenu();
+      }
+    },
+
+    _toggleCallOptionsMenu: function() {
+      var state = this.state.showCallOptionsMenu;
+      this.setState({showCallOptionsMenu: !state});
+    },
+
     render: function() {
       var tos_link_name = __("terms_of_use_link_text");
       var privacy_notice_name = __("privacy_notice_link_text");
 
       var tosHTML = __("legal_text_and_links", {
         "terms_of_use_url": "<a target=_blank href='" +
           "https://accounts.firefox.com/legal/terms'>" + tos_link_name + "</a>",
         "privacy_notice_url": "<a target=_blank href='" +
           "https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
       });
 
-      var callButtonClasses = "btn btn-success btn-large " +
+      var btnClassStartCall = "btn btn-large btn-success " +
+                              "start-audio-video-call " +
                               loop.shared.utils.getTargetPlatform();
+      var dropdownMenuClasses = React.addons.classSet({
+        "native-dropdown-large-parent": true,
+        "standalone-dropdown-menu": true,
+        "visually-hidden": !this.state.showCallOptionsMenu
+      });
 
       return (
         /* jshint ignore:start */
         React.DOM.div({className: "container"}, 
           React.DOM.div({className: "container-box"}, 
 
             ConversationHeader({
               urlCreationDateString: this.state.urlCreationDateString}), 
@@ -216,21 +249,47 @@ loop.webapp = (function($, _, OT, webL10
             React.DOM.p({className: "large-font light-weight-font"}, 
               __("initiate_call_button_label")
             ), 
 
             React.DOM.div({id: "messages"}), 
 
             React.DOM.div({className: "button-group"}, 
               React.DOM.div({className: "flex-padding-1"}), 
-              React.DOM.button({ref: "submitButton", onClick: this._initiateOutgoingCall, 
-                className: callButtonClasses, 
-                disabled: this.state.disableCallButton}, 
-                __("initiate_call_button"), 
-                React.DOM.i({className: "icon icon-video"})
+              React.DOM.div({className: "button-chevron-menu-group"}, 
+                React.DOM.div({className: "button-group-chevron"}, 
+                  React.DOM.div({className: "button-group"}, 
+
+                    React.DOM.button({className: btnClassStartCall, 
+                            onClick: this._initiateOutgoingCall("audio-video"), 
+                            disabled: this.state.disableCallButton, 
+                            title: __("initiate_audio_video_call_tooltip")}, 
+                      __("initiate_audio_video_call_button")
+                    ), 
+
+                    React.DOM.div({className: "btn-chevron", 
+                      onClick: this._toggleCallOptionsMenu}
+                    )
+
+                  ), 
+
+                  React.DOM.ul({className: dropdownMenuClasses}, 
+                    React.DOM.li(null, 
+                      /*
+                       Button required for disabled state.
+                       */
+                      React.DOM.button({className: "start-audio-only-call", 
+                              onClick: this._initiateOutgoingCall("audio"), 
+                              disabled: this.state.disableCallButton}, 
+                        __("initiate_audio_call_button")
+                      )
+                    )
+                  )
+
+                )
               ), 
               React.DOM.div({className: "flex-padding-1"})
             ), 
 
             React.DOM.p({className: "terms-service", 
                dangerouslySetInnerHTML: {__html: tosHTML}})
           ), 
 
@@ -275,22 +334,22 @@ loop.webapp = (function($, _, OT, webL10
      * server.
      */
     setupOutgoingCall: function() {
       var loopToken = this._conversation.get("loopToken");
       if (!loopToken) {
         this._notifier.errorL10n("missing_conversation_info");
         this.navigate("home", {trigger: true});
       } else {
+        var callType = this._conversation.get("selectedCallType");
+
         this._conversation.once("call:outgoing", this.startCall, this);
 
-        // XXX For now, we assume both audio and video as there is no
-        // other option to select (bug 1048333)
-        this._client.requestCallInfo(this._conversation.get("loopToken"), "audio-video",
-                                     function(err, sessionData) {
+        this._client.requestCallInfo(this._conversation.get("loopToken"),
+                                     callType, function(err, sessionData) {
           if (err) {
             switch (err.errno) {
               // loop-server sends 404 + INVALID_TOKEN (errno 105) whenever a token is
               // missing OR expired; we treat this information as if the url is always
               // expired.
               case 105:
                 this._onSessionExpired();
                 break;
@@ -384,17 +443,18 @@ loop.webapp = (function($, _, OT, webL10
      */
     loadConversation: function(loopToken) {
       if (!this._conversation.isSessionReady()) {
         // User has loaded this url directly, actually setup the call.
         return this.navigate("call/" + loopToken, {trigger: true});
       }
       this.loadReactComponent(sharedViews.ConversationView({
         sdk: OT,
-        model: this._conversation
+        model: this._conversation,
+        video: {enabled: this._conversation.hasVideoStream("outgoing")}
       }));
     }
   });
 
   /**
    * Local helpers.
    */
   function WebappHelper() {
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -139,76 +139,109 @@ loop.webapp = (function($, _, OT, webL10
      * - {loop.shared.model.ConversationModel}    model    Conversation model.
      * - {loop.shared.views.NotificationListView} notifier Notifier component.
      *
      */
 
     getInitialState: function() {
       return {
         urlCreationDateString: '',
-        disableCallButton: false
+        disableCallButton: false,
+        showCallOptionsMenu: false
       };
     },
 
     propTypes: {
       model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                                        .isRequired,
       // XXX Check more tightly here when we start injecting window.loop.*
       notifier: React.PropTypes.object.isRequired,
       client: React.PropTypes.object.isRequired
     },
 
     componentDidMount: function() {
+      // Listen for events & hide dropdown menu if user clicks away
+      window.addEventListener("click", this.clickHandler);
       this.props.model.listenTo(this.props.model, "session:error",
                                 this._onSessionError);
       this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
                                            this._setConversationTimestamp);
       // XXX DOM element does not exist before React view gets instantiated
       // We should turn the notifier into a react component
       this.props.notifier.$el = $("#messages");
     },
 
     _onSessionError: function(error) {
       console.error(error);
       this.props.notifier.errorL10n("unable_retrieve_call_info");
     },
 
     /**
      * Initiates the call.
+     * Takes in a call type parameter "audio" or "audio-video" and returns
+     * a function that initiates the call. React click handler requires a function
+     * to be called when that event happenes.
+     *
+     * @param {string} User call type choice "audio" or "audio-video"
      */
-    _initiateOutgoingCall: function() {
-      this.setState({disableCallButton: true});
-      this.props.model.setupOutgoingCall();
+    _initiateOutgoingCall: function(callType) {
+      return function() {
+        this.props.model.set("selectedCallType", callType);
+        this.setState({disableCallButton: true});
+        this.props.model.setupOutgoingCall();
+      }.bind(this);
     },
 
     _setConversationTimestamp: function(err, callUrlInfo) {
       if (err) {
         this.props.notifier.errorL10n("unable_retrieve_call_info");
       } else {
         var date = (new Date(callUrlInfo.urlCreationDate * 1000));
         var options = {year: "numeric", month: "long", day: "numeric"};
         var timestamp = date.toLocaleDateString(navigator.language, options);
 
         this.setState({urlCreationDateString: timestamp});
       }
     },
 
+    componentWillUnmount: function() {
+      window.removeEventListener("click", this.clickHandler);
+    },
+
+    clickHandler: function(e) {
+      if (!e.target.classList.contains('btn-chevron') &&
+          this.state.showCallOptionsMenu) {
+            this._toggleCallOptionsMenu();
+      }
+    },
+
+    _toggleCallOptionsMenu: function() {
+      var state = this.state.showCallOptionsMenu;
+      this.setState({showCallOptionsMenu: !state});
+    },
+
     render: function() {
       var tos_link_name = __("terms_of_use_link_text");
       var privacy_notice_name = __("privacy_notice_link_text");
 
       var tosHTML = __("legal_text_and_links", {
         "terms_of_use_url": "<a target=_blank href='" +
           "https://accounts.firefox.com/legal/terms'>" + tos_link_name + "</a>",
         "privacy_notice_url": "<a target=_blank href='" +
           "https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
       });
 
-      var callButtonClasses = "btn btn-success btn-large " +
+      var btnClassStartCall = "btn btn-large btn-success " +
+                              "start-audio-video-call " +
                               loop.shared.utils.getTargetPlatform();
+      var dropdownMenuClasses = React.addons.classSet({
+        "native-dropdown-large-parent": true,
+        "standalone-dropdown-menu": true,
+        "visually-hidden": !this.state.showCallOptionsMenu
+      });
 
       return (
         /* jshint ignore:start */
         <div className="container">
           <div className="container-box">
 
             <ConversationHeader
               urlCreationDateString={this.state.urlCreationDateString} />
@@ -216,22 +249,48 @@ loop.webapp = (function($, _, OT, webL10
             <p className="large-font light-weight-font">
               {__("initiate_call_button_label")}
             </p>
 
             <div id="messages"></div>
 
             <div className="button-group">
               <div className="flex-padding-1"></div>
-              <button ref="submitButton" onClick={this._initiateOutgoingCall}
-                className={callButtonClasses}
-                disabled={this.state.disableCallButton}>
-                {__("initiate_call_button")}
-                <i className="icon icon-video"></i>
-              </button>
+              <div className="button-chevron-menu-group">
+                <div className="button-group-chevron">
+                  <div className="button-group">
+
+                    <button className={btnClassStartCall}
+                            onClick={this._initiateOutgoingCall("audio-video")}
+                            disabled={this.state.disableCallButton}
+                            title={__("initiate_audio_video_call_tooltip")} >
+                      {__("initiate_audio_video_call_button")}
+                    </button>
+
+                    <div className="btn-chevron"
+                      onClick={this._toggleCallOptionsMenu}>
+                    </div>
+
+                  </div>
+
+                  <ul className={dropdownMenuClasses}>
+                    <li>
+                      {/*
+                       Button required for disabled state.
+                       */}
+                      <button className="start-audio-only-call"
+                              onClick={this._initiateOutgoingCall("audio")}
+                              disabled={this.state.disableCallButton} >
+                        {__("initiate_audio_call_button")}
+                      </button>
+                    </li>
+                  </ul>
+
+                </div>
+              </div>
               <div className="flex-padding-1"></div>
             </div>
 
             <p className="terms-service"
                dangerouslySetInnerHTML={{__html: tosHTML}}></p>
           </div>
 
           <ConversationFooter />
@@ -275,22 +334,22 @@ loop.webapp = (function($, _, OT, webL10
      * server.
      */
     setupOutgoingCall: function() {
       var loopToken = this._conversation.get("loopToken");
       if (!loopToken) {
         this._notifier.errorL10n("missing_conversation_info");
         this.navigate("home", {trigger: true});
       } else {
+        var callType = this._conversation.get("selectedCallType");
+
         this._conversation.once("call:outgoing", this.startCall, this);
 
-        // XXX For now, we assume both audio and video as there is no
-        // other option to select (bug 1048333)
-        this._client.requestCallInfo(this._conversation.get("loopToken"), "audio-video",
-                                     function(err, sessionData) {
+        this._client.requestCallInfo(this._conversation.get("loopToken"),
+                                     callType, function(err, sessionData) {
           if (err) {
             switch (err.errno) {
               // loop-server sends 404 + INVALID_TOKEN (errno 105) whenever a token is
               // missing OR expired; we treat this information as if the url is always
               // expired.
               case 105:
                 this._onSessionExpired();
                 break;
@@ -384,17 +443,18 @@ loop.webapp = (function($, _, OT, webL10
      */
     loadConversation: function(loopToken) {
       if (!this._conversation.isSessionReady()) {
         // User has loaded this url directly, actually setup the call.
         return this.navigate("call/" + loopToken, {trigger: true});
       }
       this.loadReactComponent(sharedViews.ConversationView({
         sdk: OT,
-        model: this._conversation
+        model: this._conversation,
+        video: {enabled: this._conversation.hasVideoStream("outgoing")}
       }));
     }
   });
 
   /**
    * Local helpers.
    */
   function WebappHelper() {
--- a/browser/components/loop/standalone/content/l10n/data.ini
+++ b/browser/components/loop/standalone/content/l10n/data.ini
@@ -20,17 +20,19 @@ sorry_device_unsupported=Sorry, Loop doe
 use_firefox_windows_mac_linux=Please open this page using the latest Firefox on Windows, Android, Mac or Linux.
 connection_error_see_console_notification=Call failed; see console for details.
 call_url_unavailable_notification_heading=Oops!
 call_url_unavailable_notification_message=This URL is unavailable.
 promote_firefox_hello_heading=Download Firefox to make free audio and video calls!
 get_firefox_button=Get Firefox
 call_url_unavailable_notification=This URL is unavailable.
 initiate_call_button_label=Click Call to start a video chat
-initiate_call_button=Call
+initiate_audio_video_call_button=Call
+initiate_audio_video_call_tooltip=Start a video call
+initiate_audio_call_button=Voice call
 ## LOCALIZATION NOTE (legal_text_and_links): In this item, don't translate the
 ## part between {{..}}
 legal_text_and_links=By using this product you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}
 terms_of_use_link_text=Terms of use
 privacy_notice_link_text=Privacy notice
 brandShortname=Firefox
 clientShortname=WebRTC!
 ## LOCALIZATION NOTE (call_url_creation_date_label): Example output: (from May 26, 2014)
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -114,17 +114,18 @@ describe("loop.conversation", function()
 
     beforeEach(function() {
       client = new loop.Client();
       conversation = new loop.shared.models.ConversationModel({}, {
         sdk: {},
         pendingCallTimeout: 1000,
       });
       sandbox.stub(client, "requestCallsInfo");
-      sandbox.stub(conversation, "setSessionData");
+      sandbox.stub(conversation, "setIncomingSessionData");
+      sandbox.stub(conversation, "setOutgoingSessionData");
     });
 
     describe("Routes", function() {
       var router;
 
       beforeEach(function() {
         router = new ConversationRouter({
           client: client,
@@ -187,36 +188,51 @@ describe("loop.conversation", function()
 
         describe("requestCallsInfo successful", function() {
           var fakeSessionData;
 
           beforeEach(function() {
             fakeSessionData  = {
               sessionId:    "sessionId",
               sessionToken: "sessionToken",
-              apiKey:       "apiKey"
+              apiKey:       "apiKey",
+              callType:     "callType"
             };
 
             client.requestCallsInfo.callsArgWith(1, null, [fakeSessionData]);
           });
 
           it("should store the session data", function() {
             router.incoming(42);
 
-            sinon.assert.calledOnce(conversation.setSessionData);
-            sinon.assert.calledWithExactly(conversation.setSessionData,
+            sinon.assert.calledOnce(conversation.setIncomingSessionData);
+            sinon.assert.calledWithExactly(conversation.setIncomingSessionData,
                                            fakeSessionData);
           });
 
+          it("should call the view with video.enabled=false", function() {
+            sandbox.stub(conversation, "get").withArgs("callType").returns("audio");
+            router.incoming("fakeVersion");
+
+            sinon.assert.calledOnce(conversation.get);
+            sinon.assert.calledOnce(loop.conversation.IncomingCallView);
+            sinon.assert.calledWithExactly(loop.conversation.IncomingCallView,
+                                           {model: conversation,
+                                           video: {enabled: false}});
+          });
+
           it("should display the incoming call view", function() {
+            sandbox.stub(conversation, "get").withArgs("callType")
+                                                      .returns("audio-video");
             router.incoming("fakeVersion");
 
             sinon.assert.calledOnce(loop.conversation.IncomingCallView);
             sinon.assert.calledWithExactly(loop.conversation.IncomingCallView,
-                                           {model: conversation});
+                                           {model: conversation,
+                                           video: {enabled: true}});
             sinon.assert.calledOnce(router.loadReactComponent);
             sinon.assert.calledWith(router.loadReactComponent,
               sinon.match(function(value) {
                 return TestUtils.isDescriptorOfType(value,
                   loop.conversation.IncomingCallView);
               }));
           });
         });
@@ -434,19 +450,42 @@ describe("loop.conversation", function()
     });
 
     describe("click event on .btn-accept", function() {
       it("should trigger an 'accept' conversation model event", function() {
         var buttonAccept = view.getDOMNode().querySelector(".btn-accept");
 
         TestUtils.Simulate.click(buttonAccept);
 
-        sinon.assert.calledOnce(model.trigger);
+        /* Setting a model property triggers 2 events */
+        sinon.assert.calledThrice(model.trigger);
         sinon.assert.calledWith(model.trigger, "accept");
-        });
+        sinon.assert.calledWith(model.trigger, "change:selectedCallType");
+        sinon.assert.calledWith(model.trigger, "change");
+      });
+
+      it("should set selectedCallType to audio-video", function() {
+        var buttonAccept = view.getDOMNode().querySelector(".call-audio-video");
+        sandbox.stub(model, "set");
+
+        TestUtils.Simulate.click(buttonAccept);
+
+        sinon.assert.calledOnce(model.set);
+        sinon.assert.calledWithExactly(model.set, "selectedCallType", "audio-video");
+      });
+
+      it("should set selectedCallType to audio", function() {
+        var buttonAccept = view.getDOMNode().querySelector(".call-audio-only");
+        sandbox.stub(model, "set");
+
+        TestUtils.Simulate.click(buttonAccept);
+
+        sinon.assert.calledOnce(model.set);
+        sinon.assert.calledWithExactly(model.set, "selectedCallType", "audio");
+      });
     });
 
     describe("click event on .btn-decline", function() {
       it("should trigger an 'decline' conversation model event", function() {
         var buttonDecline = view.getDOMNode().querySelector(".btn-decline");
 
         TestUtils.Simulate.click(buttonDecline);
 
--- a/browser/components/loop/test/mochitest/browser.ini
+++ b/browser/components/loop/test/mochitest/browser.ini
@@ -1,8 +1,8 @@
 [DEFAULT]
 support-files =
     head.js
 
 [browser_mozLoop_appVersionInfo.js]
-[browser_mozLoop_charPref.js]
+[browser_mozLoop_prefs.js]
 [browser_mozLoop_doNotDisturb.js]
 skip-if = buildapp == 'mulet'
rename from browser/components/loop/test/mochitest/browser_mozLoop_charPref.js
rename to browser/components/loop/test/mochitest/browser_mozLoop_prefs.js
--- a/browser/components/loop/test/mochitest/browser_mozLoop_charPref.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_prefs.js
@@ -19,8 +19,22 @@ add_task(function* test_mozLoop_charPref
   gMozLoopAPI.setLoopCharPref("test", "foo");
   Assert.equal(Services.prefs.getCharPref("loop.test"), "foo",
                "should set loop pref value correctly");
 
   // Test getLoopCharPref
   Assert.equal(gMozLoopAPI.getLoopCharPref("test"), "foo",
                "should get loop pref value correctly");
 });
+
+add_task(function* test_mozLoop_boolPref() {
+  registerCleanupFunction(function () {
+    Services.prefs.clearUserPref("loop.testBool");
+  });
+
+  Assert.ok(gMozLoopAPI, "mozLoop should exist");
+
+  Services.prefs.setBoolPref("loop.testBool", true);
+
+  // Test getLoopCharPref
+  Assert.equal(gMozLoopAPI.getLoopBoolPref("testBool"), true,
+               "should get loop pref value correctly");
+});
--- a/browser/components/loop/test/shared/models_test.js
+++ b/browser/components/loop/test/shared/models_test.js
@@ -19,17 +19,18 @@ describe("loop.shared.models", function(
     requests = [];
     // https://github.com/cjohansen/Sinon.JS/issues/393
     fakeXHR.xhr.onCreate = function(xhr) {
       requests.push(xhr);
     };
     fakeSessionData = {
       sessionId:    "sessionId",
       sessionToken: "sessionToken",
-      apiKey:       "apiKey"
+      apiKey:       "apiKey",
+      callType:     "callType"
     };
     fakeSession = _.extend({
       connect: function () {},
       endSession: sandbox.stub(),
       set: sandbox.stub(),
       disconnect: sandbox.spy(),
       unpublish: sandbox.spy()
     }, Backbone.Events);
@@ -96,23 +97,24 @@ describe("loop.shared.models", function(
 
           conversation.setupOutgoingCall();
         });
       });
 
       describe("#outgoing", function() {
         beforeEach(function() {
           sandbox.stub(conversation, "endSession");
-          sandbox.stub(conversation, "setSessionData");
+          sandbox.stub(conversation, "setOutgoingSessionData");
+          sandbox.stub(conversation, "setIncomingSessionData");
         });
 
-        it("should save the sessionData", function() {
+        it("should save the outgoing sessionData", function() {
           conversation.outgoing(fakeSessionData);
 
-          sinon.assert.calledOnce(conversation.setSessionData);
+          sinon.assert.calledOnce(conversation.setOutgoingSessionData);
         });
 
         it("should trigger a `call:outgoing` event", function(done) {
           conversation.once("call:outgoing", function() {
             done();
           });
 
           conversation.outgoing();
@@ -134,23 +136,34 @@ describe("loop.shared.models", function(
 
             conversation.outgoing();
 
             sandbox.clock.tick(1001);
           });
       });
 
       describe("#setSessionData", function() {
-        it("should update conversation session information", function() {
-          conversation.setSessionData(fakeSessionData);
+        it("should update outgoing conversation session information",
+           function() {
+             conversation.setOutgoingSessionData(fakeSessionData);
+
+             expect(conversation.get("sessionId")).eql("sessionId");
+             expect(conversation.get("sessionToken")).eql("sessionToken");
+             expect(conversation.get("apiKey")).eql("apiKey");
+           });
 
-          expect(conversation.get("sessionId")).eql("sessionId");
-          expect(conversation.get("sessionToken")).eql("sessionToken");
-          expect(conversation.get("apiKey")).eql("apiKey");
-        });
+        it("should update incoming conversation session information",
+           function() {
+             conversation.setIncomingSessionData(fakeSessionData);
+
+             expect(conversation.get("sessionId")).eql("sessionId");
+             expect(conversation.get("sessionToken")).eql("sessionToken");
+             expect(conversation.get("apiKey")).eql("apiKey");
+             expect(conversation.get("callType")).eql("callType");
+           });
       });
 
       describe("#startSession", function() {
         var model;
 
         beforeEach(function() {
           sandbox.stub(sharedModels.ConversationModel.prototype,
                        "_clearPendingCallTimer");
@@ -354,11 +367,35 @@ describe("loop.shared.models", function(
             sandbox.stub(model, "stopListening");
 
             model.endSession();
             model.trigger("session:ended");
 
             sinon.assert.calledOnce(model.stopListening);
           });
       });
+
+      describe("#hasVideoStream", function() {
+        var model;
+
+        beforeEach(function() {
+          model = new sharedModels.ConversationModel(fakeSessionData, {
+            sdk: fakeSDK,
+            pendingCallTimeout: 1000
+          });
+          model.startSession();
+        });
+
+        it("should return true for incoming callType", function() {
+          model.set("callType", "audio-video");
+
+          expect(model.hasVideoStream("incoming")).to.eql(true);
+        });
+
+        it("should return true for outgoing callType", function() {
+          model.set("selectedCallType", "audio-video");
+
+          expect(model.hasVideoStream("outgoing")).to.eql(true);
+        });
+      });
     });
   });
 });
--- a/browser/components/loop/test/shared/views_test.js
+++ b/browser/components/loop/test/shared/views_test.js
@@ -207,27 +207,47 @@ describe("loop.shared.views", function()
         pendingCallTimeout: 1000
       });
     });
 
     describe("#componentDidMount", function() {
       it("should start a session", function() {
         sandbox.stub(model, "startSession");
 
-        mountTestComponent({sdk: fakeSDK, model: model});
+        mountTestComponent({
+          sdk: fakeSDK,
+          model: model,
+          video: {enabled: true}
+        });
 
         sinon.assert.calledOnce(model.startSession);
       });
+
+      it("should set the correct stream publish options", function() {
+
+        var component = mountTestComponent({
+          sdk: fakeSDK,
+          model: model,
+          video: {enabled: false}
+        });
+
+        expect(component.publisherConfig.publishVideo).to.eql(false);
+
+      });
     });
 
     describe("constructed", function() {
       var comp;
 
       beforeEach(function() {
-        comp = mountTestComponent({sdk: fakeSDK, model: model});
+        comp = mountTestComponent({
+          sdk: fakeSDK,
+          model: model,
+          video: {enabled: false}
+        });
       });
 
       describe("#hangup", function() {
         beforeEach(function() {
           comp.startPublishing();
         });
 
         it("should disconnect the session", function() {
@@ -288,17 +308,21 @@ describe("loop.shared.views", function()
             sinon.assert.calledOnce(fakePublisher.off);
           });
       });
 
       describe("#publishStream", function() {
         var comp;
 
         beforeEach(function() {
-          comp = mountTestComponent({sdk: fakeSDK, model: model});
+          comp = mountTestComponent({
+            sdk: fakeSDK,
+            model: model,
+            video: {enabled: false}
+          });
           comp.startPublishing();
         });
 
         it("should start streaming local audio", function() {
           comp.publishStream("audio", true);
 
           sinon.assert.calledOnce(fakePublisher.publishAudio);
           sinon.assert.calledWithExactly(fakePublisher.publishAudio, true);
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -297,16 +297,17 @@ describe("loop.webapp", function() {
 
             sinon.assert.calledOnce(notifier.errorL10n);
           });
         });
 
         describe("Has loop token", function() {
           beforeEach(function() {
             conversation.set("loopToken", "fakeToken");
+            conversation.set("selectedCallType", "audio-video");
             sandbox.stub(conversation, "outgoing");
           });
 
           it("should call requestCallInfo on the client",
             function() {
               conversation.setupOutgoingCall();
 
               sinon.assert.calledOnce(client.requestCallInfo);
@@ -395,30 +396,68 @@ describe("loop.webapp", function() {
               model: conversation,
               notifier: notifier,
               client: standaloneClientStub
             })
         );
       });
 
       it("should start the conversation establishment process", function() {
-        var button = view.getDOMNode().querySelector("button");
+        var button = view.getDOMNode().querySelector(".start-audio-video-call");
+        React.addons.TestUtils.Simulate.click(button);
+
+        sinon.assert.calledOnce(setupOutgoingCall);
+        sinon.assert.calledWithExactly(setupOutgoingCall);
+      });
+
+      it("should start the conversation establishment process", function() {
+        var button = view.getDOMNode().querySelector(".start-audio-only-call");
         React.addons.TestUtils.Simulate.click(button);
 
         sinon.assert.calledOnce(setupOutgoingCall);
+        sinon.assert.calledWithExactly(setupOutgoingCall);
       });
 
-      it("should disable current form once session is initiated", function() {
-        conversation.set("loopToken", "fake");
+      it("should disable audio-video button once session is initiated",
+         function() {
+           conversation.set("loopToken", "fake");
+
+           var button = view.getDOMNode().querySelector(".start-audio-video-call");
+           React.addons.TestUtils.Simulate.click(button);
+
+           expect(button.disabled).to.eql(true);
+         });
+
+      it("should disable audio-only button once session is initiated",
+         function() {
+           conversation.set("loopToken", "fake");
+
+           var button = view.getDOMNode().querySelector(".start-audio-only-call");
+           React.addons.TestUtils.Simulate.click(button);
 
-        var button = view.getDOMNode().querySelector("button");
-        React.addons.TestUtils.Simulate.click(button);
+           expect(button.disabled).to.eql(true);
+         });
+
+         it("should set selectedCallType to audio", function() {
+           conversation.set("loopToken", "fake");
+
+           var button = view.getDOMNode().querySelector(".start-audio-only-call");
+           React.addons.TestUtils.Simulate.click(button);
 
-        expect(button.disabled).to.eql(true);
-      });
+           expect(conversation.get("selectedCallType")).to.eql("audio");
+         });
+
+         it("should set selectedCallType to audio-video", function() {
+           conversation.set("loopToken", "fake");
+
+           var button = view.getDOMNode().querySelector(".start-audio-video-call");
+           React.addons.TestUtils.Simulate.click(button);
+
+           expect(conversation.get("selectedCallType")).to.eql("audio-video");
+         });
 
       it("should set state.urlCreationDateString to a locale date string",
          function() {
         // wrap in a jquery object because text is broken up
         // into several span elements
         var date = new Date(0);
         var options = {year: "numeric", month: "long", day: "numeric"};
         var timestamp = date.toLocaleDateString(navigator.language, options);
rename from browser/components/loop/test/xpcshell/test_loopservice_get_loop_char_pref.js
rename to browser/components/loop/test/xpcshell/test_loopservice_loop_prefs.js
--- a/browser/components/loop/test/xpcshell/test_loopservice_get_loop_char_pref.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_loop_prefs.js
@@ -1,47 +1,106 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 /*global XPCOMUtils, Services, Assert */
 
-var fakePrefName = "color";
+var fakeCharPrefName = "color";
+var fakeBoolPrefName = "boolean";
 var fakePrefValue = "green";
 
 function test_getLoopCharPref()
 {
-  Services.prefs.setCharPref("loop." + fakePrefName, fakePrefValue);
+  Services.prefs.setCharPref("loop." + fakeCharPrefName, fakePrefValue);
 
-  var returnedPref = MozLoopService.getLoopCharPref(fakePrefName);
+  var returnedPref = MozLoopService.getLoopCharPref(fakeCharPrefName);
 
   Assert.equal(returnedPref, fakePrefValue,
     "Should return a char pref under the loop. branch");
-  Services.prefs.clearUserPref("loop." + fakePrefName);
+  Services.prefs.clearUserPref("loop." + fakeCharPrefName);
 }
 
 function test_getLoopCharPref_not_found()
 {
-  var returnedPref = MozLoopService.getLoopCharPref(fakePrefName);
+  var returnedPref = MozLoopService.getLoopCharPref(fakeCharPrefName);
 
   Assert.equal(returnedPref, null,
     "Should return null if a preference is not found");
 }
 
 function test_getLoopCharPref_non_coercible_type()
 {
-  Services.prefs.setBoolPref("loop." + fakePrefName, false );
+  Services.prefs.setBoolPref("loop." + fakeCharPrefName, false);
 
-  var returnedPref = MozLoopService.getLoopCharPref(fakePrefName);
+  var returnedPref = MozLoopService.getLoopCharPref(fakeCharPrefName);
 
   Assert.equal(returnedPref, null,
     "Should return null if the preference exists & is of a non-coercible type");
 }
 
+function test_setLoopCharPref()
+{
+  Services.prefs.setCharPref("loop." + fakeCharPrefName, "red");
+  MozLoopService.setLoopCharPref(fakeCharPrefName, fakePrefValue);
+
+  var returnedPref = Services.prefs.getCharPref("loop." + fakeCharPrefName);
+
+  Assert.equal(returnedPref, fakePrefValue,
+    "Should set a char pref under the loop. branch");
+  Services.prefs.clearUserPref("loop." + fakeCharPrefName);
+}
+
+function test_setLoopCharPref_new()
+{
+  Services.prefs.clearUserPref("loop." + fakeCharPrefName);
+  MozLoopService.setLoopCharPref(fakeCharPrefName, fakePrefValue);
+
+  var returnedPref = Services.prefs.getCharPref("loop." + fakeCharPrefName);
+
+  Assert.equal(returnedPref, fakePrefValue,
+               "Should set a new char pref under the loop. branch");
+  Services.prefs.clearUserPref("loop." + fakeCharPrefName);
+}
+
+function test_setLoopCharPref_non_coercible_type()
+{
+  MozLoopService.setLoopCharPref(fakeCharPrefName, true);
+
+  ok(true, "Setting non-coercible type should not fail");
+}
+
+
+function test_getLoopBoolPref()
+{
+  Services.prefs.setBoolPref("loop." + fakeBoolPrefName, true);
+
+  var returnedPref = MozLoopService.getLoopBoolPref(fakeBoolPrefName);
+
+  Assert.equal(returnedPref, true,
+    "Should return a bool pref under the loop. branch");
+  Services.prefs.clearUserPref("loop." + fakeBoolPrefName);
+}
+
+function test_getLoopBoolPref_not_found()
+{
+  var returnedPref = MozLoopService.getLoopBoolPref(fakeBoolPrefName);
+
+  Assert.equal(returnedPref, null,
+    "Should return null if a preference is not found");
+}
+
 
 function run_test()
 {
   test_getLoopCharPref();
   test_getLoopCharPref_not_found();
   test_getLoopCharPref_non_coercible_type();
+  test_setLoopCharPref();
+  test_setLoopCharPref_new();
+  test_setLoopCharPref_non_coercible_type();
+
+  test_getLoopBoolPref();
+  test_getLoopBoolPref_not_found();
 
   do_register_cleanup(function() {
-    Services.prefs.clearUserPref("loop." + fakePrefName);
+    Services.prefs.clearUserPref("loop." + fakeCharPrefName);
+    Services.prefs.clearUserPref("loop." + fakeBoolPrefName);
   });
 }
deleted file mode 100644
--- a/browser/components/loop/test/xpcshell/test_loopservice_set_loop_char_pref.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-/*global XPCOMUtils, Services, Assert */
-
-var fakePrefName = "color";
-var fakePrefValue = "green";
-
-function test_setLoopCharPref()
-{
-  Services.prefs.setCharPref("loop." + fakePrefName, "red");
-  MozLoopService.setLoopCharPref(fakePrefName, fakePrefValue);
-
-  var returnedPref = Services.prefs.getCharPref("loop." + fakePrefName);
-
-  Assert.equal(returnedPref, fakePrefValue,
-    "Should set a char pref under the loop. branch");
-  Services.prefs.clearUserPref("loop." + fakePrefName);
-}
-
-function test_setLoopCharPref_new()
-{
-  Services.prefs.clearUserPref("loop." + fakePrefName);
-  MozLoopService.setLoopCharPref(fakePrefName, fakePrefValue);
-
-  var returnedPref = Services.prefs.getCharPref("loop." + fakePrefName);
-
-  Assert.equal(returnedPref, fakePrefValue,
-               "Should set a new char pref under the loop. branch");
-  Services.prefs.clearUserPref("loop." + fakePrefName);
-}
-
-function test_setLoopCharPref_non_coercible_type()
-{
-  MozLoopService.setLoopCharPref(fakePrefName, true);
-
-  ok(true, "Setting non-coercible type should not fail");
-}
-
-
-function run_test()
-{
-  test_setLoopCharPref();
-  test_setLoopCharPref_new();
-  test_setLoopCharPref_non_coercible_type();
-
-  do_register_cleanup(function() {
-    Services.prefs.clearUserPref("loop." + fakePrefName);
-  });
-}
--- a/browser/components/loop/test/xpcshell/xpcshell.ini
+++ b/browser/components/loop/test/xpcshell/xpcshell.ini
@@ -1,17 +1,16 @@
 [DEFAULT]
 head = head.js
 tail =
 firefox-appdir = browser
 
 [test_looppush_initialize.js]
 [test_loopservice_dnd.js]
 [test_loopservice_expiry.js]
-[test_loopservice_get_loop_char_pref.js]
-[test_loopservice_set_loop_char_pref.js]
+[test_loopservice_loop_prefs.js]
 [test_loopservice_initialize.js]
 [test_loopservice_locales.js]
 [test_loopservice_registration.js]
 [test_loopservice_token_invalid.js]
 [test_loopservice_token_save.js]
 [test_loopservice_token_send.js]
 [test_loopservice_token_validation.js]
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -3,17 +3,17 @@
    - 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/. -->
 
 <?xml-stylesheet href="chrome://global/skin/global.css"?>
 
 <?xml-stylesheet href="chrome://mozapps/content/preferences/preferences.css"?>
 
 <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
-<?xml-stylesheet href="chrome://browser/skin/in-content/common.css"?>
+<?xml-stylesheet href="chrome://global/skin/in-content/common.css"?>
 <?xml-stylesheet
   href="chrome://browser/skin/preferences/in-content/preferences.css"?>
 <?xml-stylesheet
   href="chrome://browser/content/preferences/handlers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
 
 <!DOCTYPE page [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -6,17 +6,17 @@
 
 let gSubDialog = {
   _closingCallback: null,
   _frame: null,
   _overlay: null,
   _box: null,
   _injectedStyleSheets: ["chrome://mozapps/content/preferences/preferences.css",
                          "chrome://browser/skin/preferences/preferences.css",
-                         "chrome://browser/skin/in-content/common.css",
+                         "chrome://global/skin/in-content/common.css",
                          "chrome://browser/skin/preferences/in-content/preferences.css"],
 
   init: function() {
     this._frame = document.getElementById("dialogFrame");
     this._overlay = document.getElementById("dialogOverlay");
     this._box = document.getElementById("dialogBox");
 
     // Make the close button work.
--- a/browser/devtools/framework/test/browser_dynamic_tool_enabling.js
+++ b/browser/devtools/framework/test/browser_dynamic_tool_enabling.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that toggling prefs immediately (de)activates the relevant menuitem
 
 let gItemsToTest = {
   "menu_devToolbar": "devtools.toolbar.enabled",
-  "menu_devAppMgr": "devtools.appmanager.enabled",
   "menu_browserToolbox": ["devtools.chrome.enabled", "devtools.debugger.remote-enabled", "devtools.debugger.chrome-enabled"],
   "javascriptConsole": "devtools.errorconsole.enabled",
   "menu_devtools_connect": "devtools.debugger.remote-enabled",
 };
 
 function expectedAttributeValueFromPrefs(prefs) {
   return prefs.every((pref) => Services.prefs.getBoolPref(pref)) ?
          "" : "true";
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -71,16 +71,17 @@ function Toolbox(target, selectedTool, h
   this._refreshHostTitle = this._refreshHostTitle.bind(this);
   this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
   this.destroy = this.destroy.bind(this);
   this.highlighterUtils = getHighlighterUtils(this);
   this._highlighterReady = this._highlighterReady.bind(this);
   this._highlighterHidden = this._highlighterHidden.bind(this);
   this._prefChanged = this._prefChanged.bind(this);
   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
+  this._onFocus = this._onFocus.bind(this);
 
   this._target.on("close", this.destroy);
 
   if (!hostType) {
     hostType = Services.prefs.getCharPref(this._prefs.LAST_HOST);
   }
   if (!selectedTool) {
     selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
@@ -248,36 +249,39 @@ Toolbox.prototype = {
         gDevTools.on("pref-changed", this._prefChanged);
 
         this._buildDockButtons();
         this._buildOptions();
         this._buildTabs();
         this._applyCacheSettings();
         this._addKeysToWindow();
         this._addReloadKeys();
-        this._addToolSwitchingKeys();
+        this._addHostListeners();
         this._addZoomKeys();
         this._loadInitialZoom();
 
         this.webconsolePanel = this.doc.querySelector("#toolbox-panel-webconsole");
         this.webconsolePanel.height =
           Services.prefs.getIntPref(SPLITCONSOLE_HEIGHT_PREF);
         this.webconsolePanel.addEventListener("resize",
           this._saveSplitConsoleHeight);
 
-        let splitConsolePromise = promise.resolve();
-        if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
-          splitConsolePromise = this.openSplitConsole();
-        }
-
         let buttonsPromise = this._buildButtons();
 
         this._telemetry.toolOpened("toolbox");
 
         this.selectTool(this._defaultToolId).then(panel => {
+
+          // Wait until the original tool is selected so that the split
+          // console input will receive focus.
+          let splitConsolePromise = promise.resolve();
+          if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
+            splitConsolePromise = this.openSplitConsole();
+          }
+
           promise.all([
             splitConsolePromise,
             buttonsPromise
           ]).then(() => {
             this.emit("ready");
             deferred.resolve();
           }, deferred.reject);
         });
@@ -340,25 +344,27 @@ Toolbox.prototype = {
       ["toolbox-force-reload-key2", true]
     ].forEach(([id, force]) => {
       this.doc.getElementById(id).addEventListener("command", (event) => {
         this.reloadTarget(force);
       }, true);
     });
   },
 
-  _addToolSwitchingKeys: function() {
+  _addHostListeners: function() {
     let nextKey = this.doc.getElementById("toolbox-next-tool-key");
     nextKey.addEventListener("command", this.selectNextTool.bind(this), true);
     let prevKey = this.doc.getElementById("toolbox-previous-tool-key");
     prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
 
     // Split console uses keypress instead of command so the event can be
     // cancelled with stopPropagation on the keypress, and not preventDefault.
     this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
+
+    this.doc.addEventListener("focus", this._onFocus, true);
   },
 
   _saveSplitConsoleHeight: function() {
     Services.prefs.setIntPref(SPLITCONSOLE_HEIGHT_PREF,
       this.webconsolePanel.height);
   },
 
   /**
@@ -971,49 +977,71 @@ Toolbox.prototype = {
     let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);
     iframe.focus();
   },
 
   /**
    * Focus split console's input line
    */
   focusConsoleInput: function() {
-    let hud = this.getPanel("webconsole").hud;
-    if (hud && hud.jsterm) {
-      hud.jsterm.inputNode.focus();
+    let consolePanel = this.getPanel("webconsole");
+    if (consolePanel) {
+      consolePanel.focusInput();
     }
   },
 
   /**
+   * If the console is split and we are focusing an element outside
+   * of the console, then store the newly focused element, so that
+   * it can be restored once the split console closes.
+   */
+  _onFocus: function({originalTarget}) {
+    // Ignore any non element nodes, or any elements contained
+    // within the webconsole frame.
+    let webconsoleURL = gDevTools.getToolDefinition("webconsole").url;
+    if (originalTarget.nodeType !== 1 ||
+        originalTarget.baseURI === webconsoleURL) {
+      return;
+    }
+
+    this._lastFocusedElement = originalTarget;
+  },
+
+  /**
    * Opens the split console.
    *
    * @returns {Promise} a promise that resolves once the tool has been
    *          loaded and focused.
    */
   openSplitConsole: function() {
     this._splitConsole = true;
     Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, true);
     this._refreshConsoleDisplay();
     this.emit("split-console");
+
     return this.loadTool("webconsole").then(() => {
       this.focusConsoleInput();
     });
   },
 
   /**
    * Closes the split console.
    *
    * @returns {Promise} a promise that resolves once the tool has been
    *          closed.
    */
   closeSplitConsole: function() {
     this._splitConsole = false;
     Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, false);
     this._refreshConsoleDisplay();
     this.emit("split-console");
+
+    if (this._lastFocusedElement) {
+      this._lastFocusedElement.focus();
+    }
     return promise.resolve();
   },
 
   /**
    * Toggles the split state of the webconsole.  If the webconsole panel
    * is already selected then this command is ignored.
    *
    * @returns {Promise} a promise that resolves once the tool has been
@@ -1307,16 +1335,17 @@ Toolbox.prototype = {
   /**
    * Destroy the current host, and remove event listeners from its frame.
    *
    * @return {promise} to be resolved when the host is destroyed.
    */
   destroyHost: function() {
     this.doc.removeEventListener("keypress",
       this._splitConsoleOnKeypress, false);
+    this.doc.removeEventListener("focus", this._onFocus, true);
     return this._host.destroy();
   },
 
   /**
    * Remove all UI elements, detach from target and clear up
    */
   destroy: function() {
     // If several things call destroy then we give them all the same
@@ -1329,16 +1358,17 @@ Toolbox.prototype = {
     this.off("select", this._refreshHostTitle);
     this.off("host-changed", this._refreshHostTitle);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
     gDevTools.off("pref-changed", this._prefChanged);
 
+    this._lastFocusedElement = null;
     this._saveSplitConsoleHeight();
     this.webconsolePanel.removeEventListener("resize",
       this._saveSplitConsoleHeight);
     this.closeButton.removeEventListener("command", this.destroy, true);
 
     let outstanding = [];
     for (let [id, panel] of this._toolPanels) {
       try {
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -1609,18 +1609,21 @@ var Scratchpad = {
     };
 
     this.editor = new Editor(config);
     let editorElement = document.querySelector("#scratchpad-editor");
     this.editor.appendTo(editorElement).then(() => {
       var lines = initialText.split("\n");
 
       this.editor.on("change", this._onChanged);
+      let okstring = this.strings.GetStringFromName("selfxss.okstring");
+      let msg = this.strings.formatStringFromName("selfxss.msg", [okstring], 1);
       this._onPaste = WebConsoleUtils.pasteHandlerGen(this.editor.container.contentDocument.body,
-                                                      document.querySelector('#scratchpad-notificationbox'));
+                                                      document.querySelector('#scratchpad-notificationbox'),
+                                                      msg, okstring);
       editorElement.addEventListener("paste", this._onPaste);
       editorElement.addEventListener("drop", this._onPaste);
       this.editor.on("save", () => this.saveFile());
       this.editor.focus();
       this.editor.setCursor({ line: lines.length, ch: lines.pop().length });
 
       if (state)
         this.dirty = !state.saved;
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
@@ -53,14 +53,14 @@ function spawnTest() {
 
   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
   yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
 
   ok(!isVisible($("#web-audio-editor-details-pane-empty")),
     "Empty message hides even when loading node while open.");
   ok(isVisible($("#web-audio-editor-tabs")),
     "Switches to tab view when loading node while open.");
-  is($("#web-audio-inspector-title").value, "OscillatorNode (" + nodeIds[1] + ")",
+  is($("#web-audio-inspector-title").value, "Oscillator",
     "Inspector title updates when loading node while open.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
@@ -38,23 +38,23 @@ function spawnTest() {
   ]);
 
   ok(WebAudioInspectorView.isVisible(), "InspectorView shown once node selected.");
   ok(!isVisible($("#web-audio-editor-details-pane-empty")),
     "InspectorView empty message hidden when node selected.");
   ok(isVisible($("#web-audio-editor-tabs")),
     "InspectorView tabs view visible when node selected.");
 
-  is($("#web-audio-inspector-title").value, "OscillatorNode (" + nodeIds[1] + ")",
+  is($("#web-audio-inspector-title").value, "Oscillator",
     "Inspector should have the node title when a node is selected.");
 
   is($("#web-audio-editor-tabs").selectedIndex, 0,
     "default tab selected should be the parameters tab.");
 
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
   yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
 
-  is($("#web-audio-inspector-title").value, "GainNode (" + nodeIds[2] + ")",
+  is($("#web-audio-inspector-title").value, "Gain",
     "Inspector title updates when a new node is selected.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/webaudioeditor/webaudioeditor-view.js
+++ b/browser/devtools/webaudioeditor/webaudioeditor-view.js
@@ -151,17 +151,21 @@ let WebAudioGraphView = {
     // Clear out previous SVG information
     this.clearGraph();
 
     let graph = new dagreD3.Digraph();
     let edges = [];
 
     AudioNodes.forEach(node => {
       // Add node to graph
-      graph.addNode(node.id, { label: node.type, id: node.id });
+      graph.addNode(node.id, {
+        type: node.type,                        // Just for storing type data
+        label: node.type.replace(/Node$/, ""),  // Displayed in SVG node
+        id: node.id                             // Identification
+      });
 
       // Add all of the connections from this node to the edge array to be added
       // after all the nodes are added, otherwise edges will attempted to be created
       // for nodes that have not yet been added
       AudioNodeConnections.get(node, new Set()).forEach(dest => edges.push([node, dest]));
     });
 
     edges.forEach(([node, dest]) => graph.addEdge(null, node.id, dest.id, {
@@ -172,17 +176,17 @@ let WebAudioGraphView = {
     let renderer = new dagreD3.Renderer();
 
     // Post-render manipulation of the nodes
     let oldDrawNodes = renderer.drawNodes();
     renderer.drawNodes(function(graph, root) {
       let svgNodes = oldDrawNodes(graph, root);
       svgNodes.attr("class", (n) => {
         let node = graph.node(n);
-        return "audionode type-" + node.label;
+        return "audionode type-" + node.type;
       });
       svgNodes.attr("data-id", (n) => {
         let node = graph.node(n);
         return node.id;
       });
       return svgNodes;
     });
 
@@ -447,17 +451,17 @@ let WebAudioInspectorView = {
     this.toggleInspector({ visible: false, animated: false, delayed: false });
   },
 
   /**
    * Sets the title of the Inspector view
    */
   _setTitle: function () {
     let node = this._currentNode;
-    let title = node.type + " (" + node.id + ")";
+    let title = node.type.replace(/Node$/, "");
     $("#web-audio-inspector-title").setAttribute("value", title);
   },
 
   /**
    * Reconstructs the `Properties` tab in the inspector
    * with the `this._currentNode` as it's source.
    */
   _buildPropertiesView: Task.async(function* () {
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -276,16 +276,17 @@ skip-if = buildapp == 'mulet'
 [browser_webconsole_notifications.js]
 [browser_webconsole_open-links-without-callback.js]
 [browser_webconsole_output_copy_newlines.js]
 [browser_webconsole_output_order.js]
 [browser_webconsole_property_provider.js]
 [browser_webconsole_scratchpad_panel_link.js]
 [browser_webconsole_split.js]
 [browser_webconsole_split_escape_key.js]
+[browser_webconsole_split_focus.js]
 [browser_webconsole_split_persist.js]
 [browser_webconsole_view_source.js]
 [browser_webconsole_reflow.js]
 [browser_webconsole_log_file_filter.js]
 [browser_webconsole_expandable_timestamps.js]
 [browser_webconsole_autocomplete_in_debugger_stackframe.js]
 [browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
 [browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_split_focus.js
@@ -0,0 +1,74 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  info("Test that the split console state is persisted");
+
+  let toolbox;
+  let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for splitting</p>";
+
+  Task.spawn(runner).then(finish);
+
+  function* runner() {
+    info("Opening a tab while there is no user setting on split console pref");
+    let {tab} = yield loadTab(TEST_URI);
+    let target = TargetFactory.forTab(tab);
+    toolbox = yield gDevTools.showToolbox(target, "inspector");
+
+    ok(!toolbox.splitConsole, "Split console is hidden by default");
+
+    info ("Focusing the search box before opening the split console");
+    let inspector = toolbox.getPanel("inspector");
+    inspector.searchBox.focus();
+
+    // Use the binding element since inspector.searchBox is a XUL element.
+    let activeElement = getActiveElement(inspector.panelDoc);
+    activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
+    is (activeElement, inspector.searchBox, "Search box is focused");
+
+    yield toolbox.openSplitConsole();
+
+    ok(toolbox.splitConsole, "Split console is now visible");
+
+    // Use the binding element since jsterm.inputNode is a XUL textarea element.
+    let activeElement = getActiveElement(toolbox.doc);
+    activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
+    let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
+    is(activeElement, inputNode, "Split console input is focused by default");
+
+    yield toolbox.closeSplitConsole();
+
+    info ("Making sure that the search box is refocused after closing the split console");
+    // Use the binding element since inspector.searchBox is a XUL element.
+    let activeElement = getActiveElement(inspector.panelDoc);
+    activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
+    is (activeElement, inspector.searchBox, "Search box is focused");
+
+    yield toolbox.destroy();
+  }
+
+  function getActiveElement(doc) {
+    let activeElement = doc.activeElement;
+    while (activeElement && activeElement.contentDocument) {
+      activeElement = activeElement.contentDocument.activeElement;
+    }
+    return activeElement;
+  }
+
+  function toggleSplitConsoleWithEscape() {
+    let onceSplitConsole = toolbox.once("split-console");
+    let contentWindow = toolbox.frame.contentWindow;
+    contentWindow.focus();
+    EventUtils.sendKey("ESCAPE", contentWindow);
+    return onceSplitConsole;
+  }
+
+  function finish() {
+    toolbox = TEST_URI = null;
+    Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+    Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
+    finishTest();
+  }
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_split_persist.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_split_persist.js
@@ -30,16 +30,22 @@ function test() {
     info("Opening a tab while there is a true user setting on split console pref");
     let {tab} = yield loadTab(TEST_URI);
     let target = TargetFactory.forTab(tab);
     toolbox = yield gDevTools.showToolbox(target, "inspector");
 
     ok(toolbox.splitConsole, "Split console is visible by default.");
     is(getHeightPrefValue(), 200, "Height is set based on panel height after closing");
 
+    // Use the binding element since jsterm.inputNode is a XUL textarea element.
+    let activeElement = getActiveElement(toolbox.doc);
+    activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
+    let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
+    is(activeElement, inputNode, "Split console input is focused by default");
+
     toolbox.webconsolePanel.height = 1;
     ok (toolbox.webconsolePanel.clientHeight > 1,
         "The actual height of the console is bound with a min height");
 
     toolbox.webconsolePanel.height = 10000;
     ok (toolbox.webconsolePanel.clientHeight < 10000,
         "The actual height of the console is bound with a max height");
 
@@ -58,16 +64,24 @@ function test() {
     toolbox = yield gDevTools.showToolbox(target, "inspector");
 
     ok(!toolbox.splitConsole, "Split console is hidden by default.");
     ok(!getVisiblePrefValue(), "Visibility pref is false");
 
     yield toolbox.destroy();
   }
 
+  function getActiveElement(doc) {
+    let activeElement = doc.activeElement;
+    while (activeElement && activeElement.contentDocument) {
+      activeElement = activeElement.contentDocument.activeElement;
+    }
+    return activeElement;
+  }
+
   function getVisiblePrefValue() {
     return Services.prefs.getBoolPref("devtools.toolbox.splitconsoleEnabled");
   }
 
   function getHeightPrefValue() {
     return Services.prefs.getIntPref("devtools.toolbox.splitconsoleHeight");
   }
 
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -3132,17 +3132,21 @@ JSTerm.prototype = {
     this.completeNode = doc.querySelector(".jsterm-complete-node");
     this.inputNode = doc.querySelector(".jsterm-input-node");
 
     if (this.hud.owner._browserConsole &&
         !Services.prefs.getBoolPref("devtools.chrome.enabled")) {
       inputContainer.style.display = "none";
     }
     else {
-      this._onPaste = WebConsoleUtils.pasteHandlerGen(this.inputNode, doc.getElementById("webconsole-notificationbox"));
+      let okstring = l10n.getStr("selfxss.okstring");
+      let msg = l10n.getFormatStr("selfxss.msg", [okstring]);
+      this._onPaste = WebConsoleUtils.pasteHandlerGen(this.inputNode,
+                                                      doc.getElementById("webconsole-notificationbox"),
+                                                      msg, okstring);
       this.inputNode.addEventListener("keypress", this._keyPress, false);
       this.inputNode.addEventListener("paste", this._onPaste);
       this.inputNode.addEventListener("drop", this._onPaste);
       this.inputNode.addEventListener("input", this._inputEventHandler, false);
       this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
       this.inputNode.addEventListener("focus", this._focusEventHandler, false);
     }
 
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
@@ -98,8 +98,18 @@ scratchpad.label=Scratchpad
 # LOCALIZATION NOTE (scratchpad.panelLabel): this is used as the
 # label for the toolbox panel.
 scratchpad.panelLabel=Scratchpad Panel
 
 # LOCALIZATION NOTE (scratchpad.tooltip):  This string is displayed in the
 # tooltip of the tab when the Scratchpad is displayed inside the developer tools
 # window.
 scratchpad.tooltip=Scratchpad
+
+# LOCALIZATION NOTE (selfxss.msg): the text that is displayed when
+# a new user of the developer tools pastes code into the console
+# %1 is the text of selfxss.okstring
+selfxss.msg=Scam Warning: Take care when pasting things you don't understand. This could allow attackers to steal your identity or take control of your computer. Please type '%S' in the scratchpad below to allow pasting.
+
+# LOCALIZATION NOTE (selfxss.msg): the string to be typed
+# in by a new user of the developer tools when they receive the sefxss.msg prompt.
+# Please avoid using non-keyboard characters here
+selfxss.okstring=allow pasting
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -12,16 +12,17 @@ display_name_available_status=Available
 
 unable_retrieve_url=Sorry, we were unable to retrieve a call url.
 
 # Conversation Window Strings
 
 incoming_call_title=Incoming Call…
 incoming_call=Incoming call
 incoming_call_answer_button=Answer
+incoming_call_answer_audio_only_tooltip=Answer with voice
 incoming_call_decline_button=Decline
 incoming_call_decline_and_block_button=Decline and Block
 incoming_call_block_button=Block
 hangup_button_title=Hangup
 mute_local_audio_button_title=Mute your audio
 unmute_local_audio_button_title=Unmute your audio
 mute_local_video_button_title=Mute your video
 unmute_local_video_button_title=Unmute your video
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -246,9 +246,18 @@ let AboutHome = {
       } else {
         let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
         mm.broadcastAsyncMessage("AboutHome:Update", data);
       }
     }).then(null, function onError(x) {
       Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
     });
   },
+
+  /**
+   * Focuses the search input in the page with the given message manager.
+   * @param  messageManager
+   *         The MessageManager object of the selected browser.
+   */
+  focusInput: function (messageManager) {
+    messageManager.sendAsyncMessage("AboutHome:FocusInput");
+  }
 };
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -89,16 +89,27 @@ this.ContentSearch = {
 
   init: function () {
     Cc["@mozilla.org/globalmessagemanager;1"].
       getService(Ci.nsIMessageListenerManager).
       addMessageListener(INBOUND_MESSAGE, this);
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
   },
 
+  /**
+   * Focuses the search input in the page with the given message manager.
+   * @param  messageManager
+   *         The MessageManager object of the selected browser.
+   */
+  focusInput: function (messageManager) {
+    messageManager.sendAsyncMessage(OUTBOUND_MESSAGE, {
+      type: "FocusInput"
+    });
+  },
+
   receiveMessage: function (msg) {
     // Add a temporary event handler that exists only while the message is in
     // the event queue.  If the message's source docshell changes browsers in
     // the meantime, then we need to update msg.target.  event.detail will be
     // the docshell's new parent <xul:browser> element.
     msg.handleEvent = event => {
       let browserData = this._suggestionMap.get(msg.target);
       if (browserData) {
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -876,17 +876,16 @@ toolbarbutton[sdk-button="true"][cui-are
   -moz-box-direction: reverse;
 }
 
 #urlbar-icons {
   -moz-box-align: center;
 }
 
 .urlbar-icon {
-  cursor: pointer;
   padding: 0 3px;
 }
 
 #urlbar-search-splitter {
   -moz-appearance: none;
   width: 8px;
   -moz-margin-start: -4px;
 }
@@ -1519,17 +1518,16 @@ richlistitem[type~="action"][actiontype=
   border-top: 1px solid GrayText;
 }
 
 /* Combined go/reload/stop button in location bar */
 
 #urlbar > toolbarbutton {
   -moz-appearance: none;
   padding: 0 2px;
-  cursor: pointer;
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
 }
 
 #urlbar-reload-button {
   -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 #urlbar-reload-button:not([disabled]):hover {
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -30,27 +30,16 @@ browser.jar:
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/identity.png
   skin/classic/browser/identity-icons-generic.png
   skin/classic/browser/identity-icons-https.png
   skin/classic/browser/identity-icons-https-ev.png
   skin/classic/browser/identity-icons-https-mixed-active.png
   skin/classic/browser/identity-icons-https-mixed-display.png
   skin/classic/browser/Info.png
-* skin/classic/browser/in-content/common.css                    (in-content/common.css)
-  skin/classic/browser/in-content/check.png                 (../shared/in-content/check.png)
-  skin/classic/browser/in-content/check@2x.png              (../shared/in-content/check@2x.png)
-  skin/classic/browser/in-content/dropdown.png              (../shared/in-content/dropdown.png)
-  skin/classic/browser/in-content/dropdown@2x.png           (../shared/in-content/dropdown@2x.png)
-  skin/classic/browser/in-content/dropdown-disabled.png     (../shared/in-content/dropdown-disabled.png)
-  skin/classic/browser/in-content/dropdown-disabled@2x.png  (../shared/in-content/dropdown-disabled@2x.png)
-  skin/classic/browser/in-content/help-glyph.png            (../shared/in-content/help-glyph.png)
-  skin/classic/browser/in-content/help-glyph@2x.png         (../shared/in-content/help-glyph@2x.png)
-  skin/classic/browser/in-content/sorter.png                (../shared/in-content/sorter.png)
-  skin/classic/browser/in-content/sorter@2x.png             (../shared/in-content/sorter@2x.png)
   skin/classic/browser/menuPanel.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-small.png
   skin/classic/browser/mixed-content-blocked-16.png
   skin/classic/browser/mixed-content-blocked-64.png
   skin/classic/browser/monitor.png
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -52,17 +52,16 @@
 /* Search go button */
 .search-go-container {
   -moz-box-align: center;
 }
 
 .search-go-button {
   padding: 1px;
   list-style-image: url(moz-icon://stock/gtk-find?size=menu);
-  cursor: pointer;
 }
 
 menuitem[cmd="cmd_clearhistory"] {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
 }
 
 menuitem[cmd="cmd_clearhistory"][disabled] {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -38,27 +38,16 @@ browser.jar:
   skin/classic/browser/identity-icons-https@2x.png
   skin/classic/browser/identity-icons-https-ev.png
   skin/classic/browser/identity-icons-https-ev@2x.png
   skin/classic/browser/identity-icons-https-mixed-active.png
   skin/classic/browser/identity-icons-https-mixed-active@2x.png
   skin/classic/browser/identity-icons-https-mixed-display.png
   skin/classic/browser/identity-icons-https-mixed-display@2x.png
   skin/classic/browser/Info.png
-* skin/classic/browser/in-content/common.css                (in-content/common.css)
-  skin/classic/browser/in-content/check.png                 (../shared/in-content/check.png)
-  skin/classic/browser/in-content/check@2x.png              (../shared/in-content/check@2x.png)
-  skin/classic/browser/in-content/dropdown.png              (../shared/in-content/dropdown.png)
-  skin/classic/browser/in-content/dropdown@2x.png           (../shared/in-content/dropdown@2x.png)
-  skin/classic/browser/in-content/dropdown-disabled.png     (../shared/in-content/dropdown-disabled.png)
-  skin/classic/browser/in-content/dropdown-disabled@2x.png  (../shared/in-content/dropdown-disabled@2x.png)
-  skin/classic/browser/in-content/help-glyph.png            (../shared/in-content/help-glyph.png)
-  skin/classic/browser/in-content/help-glyph@2x.png         (../shared/in-content/help-glyph@2x.png)
-  skin/classic/browser/in-content/sorter.png                (../shared/in-content/sorter.png)
-  skin/classic/browser/in-content/sorter@2x.png             (../shared/in-content/sorter@2x.png)
   skin/classic/browser/keyhole-circle.png
   skin/classic/browser/keyhole-circle@2x.png
   skin/classic/browser/KUI-background.png
   skin/classic/browser/subtle-pattern.png
   skin/classic/browser/menu-back.png
   skin/classic/browser/menu-forward.png
   skin/classic/browser/notification-16.png
   skin/classic/browser/notification-16@2x.png
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -892,24 +892,24 @@ toolbarbutton[panel-multiview-anchor="tr
   background-repeat: no-repeat;
   background-color: Highlight;
   background-position: left 10px center, 0; /* this doesn't need to be changed for RTL */
 }
 
 toolbarbutton[panel-multiview-anchor="true"] {
   background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted.png),
                     linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
-  background-position: right 5px center;
+  background-position: right calc(@menuPanelButtonWidth@ / 2 - @exitSubviewGutterWidth@ + 2px) center;
   background-repeat: no-repeat, repeat;
 }
 
 toolbarbutton[panel-multiview-anchor="true"]:-moz-locale-dir(rtl) {
   background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl.png),
                     linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
-  background-position: left 5px center;
+  background-position: left calc(@menuPanelButtonWidth@ / 2 - @exitSubviewGutterWidth@ + 2px) center;
 }
 
 toolbarpaletteitem[place="palette"] > .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
 #bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menubutton-dropmarker {
   display: none;
 }
 
 #search-container[cui-areatype="menu-panel"],
--- a/browser/themes/shared/devtools/webaudioeditor.inc.css
+++ b/browser/themes/shared/devtools/webaudioeditor.inc.css
@@ -94,16 +94,20 @@ text {
 .theme-light g.selected text {
   fill: #f0f1f2; /* Toolbars */
 }
 
 /**
  * Inspector Styles
  */
 
+#web-audio-inspector-title {
+  margin: 6px;
+}
+
 .web-audio-inspector .error {
   background-image: url(alerticon-warning.png);
   background-size: 13px 12px;
   -moz-appearance: none;
   opacity: 0;
   transition: opacity .5s ease-out 0s;
 }
 
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -162,32 +162,32 @@ prefpane {
   padding: 0 10px;
 }
 
 #typeColumn > .treecol-sortdirection[sortDirection=ascending],
 #actionColumn > .treecol-sortdirection[sortDirection=ascending],
 #typeColumn > .treecol-sortdirection[sortDirection=descending],
 #actionColumn > .treecol-sortdirection[sortDirection=descending] {
   -moz-appearance: none;
-  list-style-image: url("chrome://browser/skin/in-content/sorter.png");
+  list-style-image: url("chrome://global/skin/in-content/sorter.png");
 }
 
 #typeColumn > .treecol-sortdirection[sortDirection=descending],
 #actionColumn > .treecol-sortdirection[sortDirection=descending] {
   transform: scaleY(-1);
 }
 
 @media (min-resolution: 2dppx) {
   #typeColumn > .treecol-sortdirection[sortDirection=ascending],
   #actionColumn > .treecol-sortdirection[sortDirection=ascending],
   #typeColumn > .treecol-sortdirection[sortDirection=descending],
   #actionColumn > .treecol-sortdirection[sortDirection=descending] {
     width: 12px;
     height: 8px;
-    list-style-image: url("chrome://browser/skin/in-content/sorter@2x.png");
+    list-style-image: url("chrome://global/skin/in-content/sorter@2x.png");
   }
 }
 
 #handlersView > richlistitem {
   min-height: 40px !important;
 }
 
 .typeIcon {
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -32,27 +32,16 @@ browser.jar:
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png
         skin/classic/browser/identity.png
         skin/classic/browser/identity-icons-generic.png
         skin/classic/browser/identity-icons-https.png
         skin/classic/browser/identity-icons-https-ev.png
         skin/classic/browser/identity-icons-https-mixed-active.png
         skin/classic/browser/identity-icons-https-mixed-display.png
-*       skin/classic/browser/in-content/common.css                  (in-content/common.css)
-        skin/classic/browser/in-content/check.png                   (../shared/in-content/check.png)
-        skin/classic/browser/in-content/check@2x.png                (../shared/in-content/check@2x.png)
-        skin/classic/browser/in-content/dropdown.png                (../shared/in-content/dropdown.png)
-        skin/classic/browser/in-content/dropdown@2x.png             (../shared/in-content/dropdown@2x.png)
-        skin/classic/browser/in-content/dropdown-disabled.png       (../shared/in-content/dropdown-disabled.png)
-        skin/classic/browser/in-content/dropdown-disabled@2x.png    (../shared/in-content/dropdown-disabled@2x.png)
-        skin/classic/browser/in-content/help-glyph.png              (../shared/in-content/help-glyph.png)
-        skin/classic/browser/in-content/help-glyph@2x.png           (../shared/in-content/help-glyph@2x.png)
-        skin/classic/browser/in-content/sorter.png                  (../shared/in-content/sorter.png)
-        skin/classic/browser/in-content/sorter@2x.png               (../shared/in-content/sorter@2x.png)
         skin/classic/browser/keyhole-forward-mask.svg
         skin/classic/browser/KUI-background.png
         skin/classic/browser/livemark-folder.png
         skin/classic/browser/menu-back.png
         skin/classic/browser/menu-forward.png
         skin/classic/browser/menuPanel.png
         skin/classic/browser/menuPanel-customize.png
         skin/classic/browser/menuPanel-exit.png
@@ -452,27 +441,16 @@ browser.jar:
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/identity-icons-generic.png
         skin/classic/aero/browser/identity-icons-https.png
         skin/classic/aero/browser/identity-icons-https-ev.png
         skin/classic/aero/browser/identity-icons-https-mixed-active.png
         skin/classic/aero/browser/identity-icons-https-mixed-display.png
-*       skin/classic/aero/browser/in-content/common.css               (in-content/common.css)
-        skin/classic/aero/browser/in-content/check.png                (../shared/in-content/check.png)
-        skin/classic/aero/browser/in-content/check@2x.png             (../shared/in-content/check@2x.png)
-        skin/classic/aero/browser/in-content/dropdown.png             (../shared/in-content/dropdown.png)
-        skin/classic/aero/browser/in-content/dropdown@2x.png          (../shared/in-content/dropdown@2x.png)
-        skin/classic/aero/browser/in-content/dropdown-disabled.png    (../shared/in-content/dropdown-disabled.png)
-        skin/classic/aero/browser/in-content/dropdown-disabled@2x.png (../shared/in-content/dropdown-disabled@2x.png)
-        skin/classic/aero/browser/in-content/help-glyph.png           (../shared/in-content/help-glyph.png)
-        skin/classic/aero/browser/in-content/help-glyph@2x.png        (../shared/in-content/help-glyph@2x.png)
-        skin/classic/aero/browser/in-content/sorter.png               (../shared/in-content/sorter.png)
-        skin/classic/aero/browser/in-content/sorter@2x.png            (../shared/in-content/sorter@2x.png)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/livemark-folder.png                (livemark-folder-aero.png)
         skin/classic/aero/browser/menu-back.png                      (menu-back-aero.png)
         skin/classic/aero/browser/menu-forward.png                   (menu-forward-aero.png)
         skin/classic/aero/browser/menuPanel.png
         skin/classic/aero/browser/menuPanel-aero.png
         skin/classic/aero/browser/menuPanel-customize.png
--- a/dom/plugins/base/nsIPluginTag.idl
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(0e56f04d-cda4-4a55-ab83-e5e29ddd370e)]
+[scriptable, uuid(231df043-3a32-43c4-aaac-7ad2da81e84f)]
 interface nsIPluginTag : nsISupports
 {
   // enabledState is stored as one of the following as an integer in prefs,
   // so if new states are added, they must not renumber the existing states.
   const unsigned long STATE_DISABLED = 0;
   const unsigned long STATE_CLICKTOPLAY = 1;
   const unsigned long STATE_ENABLED = 2;
 
@@ -20,16 +20,21 @@ interface nsIPluginTag : nsISupports
   readonly attribute AUTF8String version;
   readonly attribute AUTF8String name;
 
   /**
    * true only if this plugin is "hardblocked" and cannot be enabled.
    */
   readonly attribute boolean blocklisted;
 
+  /**
+   * true if the state is non-default and locked, false otherwise.
+   */
+  readonly attribute boolean isEnabledStateLocked;
+
   readonly attribute boolean disabled;
   readonly attribute boolean clicktoplay;
            attribute unsigned long enabledState;
 
   readonly attribute PRTime lastModifiedTime;
 
   void getMimeTypes([optional] out unsigned long aCount,
                     [retval, array, size_is(aCount)] out wstring aResults);
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -11,16 +11,17 @@
 #include "nsPluginsDir.h"
 #include "nsPluginHost.h"
 #include "nsIBlocklistService.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIPlatformCharset.h"
 #include "nsPluginLogging.h"
 #include "nsNPAPIPlugin.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/unused.h"
 #include <cctype>
 #include "mozilla/dom/EncodingUtils.h"
 
 using mozilla::dom::EncodingUtils;
 using namespace mozilla;
 
 // These legacy flags are used in the plugin registry. The states are now
 // stored in prefs, but we still need to be able to import them.
@@ -335,16 +336,32 @@ nsPluginTag::IsBlocklisted()
 
 NS_IMETHODIMP
 nsPluginTag::GetBlocklisted(bool* aBlocklisted)
 {
   *aBlocklisted = IsBlocklisted();
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsPluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked)
+{
+  *aIsEnabledStateLocked = false;
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+  if (NS_WARN_IF(!prefs)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  unused << prefs->PrefIsLocked(GetStatePrefNameForPlugin(this).get(),
+                                aIsEnabledStateLocked);
+
+  return NS_OK;
+}
+
 bool
 nsPluginTag::IsClicktoplay()
 {
   const PluginState state = GetPluginState();
   return (state == ePluginState_Clicktoplay);
 }
 
 NS_IMETHODIMP
rename from mobile/android/base/home/SuggestClient.java
rename to mobile/android/base/SuggestClient.java
--- a/mobile/android/base/home/SuggestClient.java
+++ b/mobile/android/base/SuggestClient.java
@@ -1,16 +1,18 @@
 /* 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.home;
+package org.mozilla.gecko;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.mozglue.RobocopTarget;
+import org.mozilla.gecko.util.HardwareUtils;
 
 import org.json.JSONArray;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.text.TextUtils;
 import android.util.Log;
@@ -23,17 +25,21 @@ import java.net.URL;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 
 /**
  * Use network-based search suggestions.
  */
 public class SuggestClient {
     private static final String LOGTAG = "GeckoSuggestClient";
-    private static final String USER_AGENT = GeckoAppShell.getGeckoInterface().getDefaultUAString();
+
+    // This should go through GeckoInterface to get the UA, but the search activity
+    // doesn't use a GeckoView yet. Until it does, get the UA directly.
+    private static final String USER_AGENT = HardwareUtils.isTablet() ?
+        AppConstants.USER_AGENT_FENNEC_TABLET : AppConstants.USER_AGENT_FENNEC_MOBILE;
 
     private final Context mContext;
     private final int mTimeout;
 
     // should contain the string "__searchTerms__", which is replaced with the query
     private final String mSuggestTemplate;
 
     // the maximum number of suggestions to return
@@ -107,17 +113,17 @@ public class SuggestClient {
 
             if (json != null) {
                 /*
                  * Sample result:
                  * ["foo",["food network","foothill college","foot locker",...]]
                  */
                 JSONArray results = new JSONArray(json);
                 JSONArray jsonSuggestions = results.getJSONArray(1);
-                
+
                 int added = 0;
                 for (int i = 0; (i < jsonSuggestions.length()) && (added < mMaxResults); i++) {
                     String suggestion = jsonSuggestions.getString(i);
                     if (!suggestion.equalsIgnoreCase(query)) {
                         suggestions.add(suggestion);
                         added++;
                     }
                 }
--- a/mobile/android/base/home/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -14,16 +14,17 @@ import java.util.Locale;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.SuggestClient;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserContract.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.SearchLoader.SearchCursorLoader;
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -297,17 +297,16 @@ gbjar.sources += [
     'home/PinSiteDialog.java',
     'home/ReadingListPanel.java',
     'home/ReadingListRow.java',
     'home/RecentTabsPanel.java',
     'home/SearchEngine.java',
     'home/SearchEngineRow.java',
     'home/SearchLoader.java',
     'home/SimpleCursorLoader.java',
-    'home/SuggestClient.java',
     'home/TabMenuStrip.java',
     'home/TabMenuStripLayout.java',
     'home/TopSitesGridItemView.java',
     'home/TopSitesGridView.java',
     'home/TopSitesPanel.java',
     'home/TopSitesThumbnailView.java',
     'home/TwoLinePageRow.java',
     'InputMethods.java',
@@ -374,16 +373,17 @@ gbjar.sources += [
     'SessionParser.java',
     'SharedPreferencesHelper.java',
     'SiteIdentity.java',
     'SmsManager.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
     'sqlite/SQLiteBridgeException.java',
+    'SuggestClient.java',
     'SurfaceBits.java',
     'Tab.java',
     'Tabs.java',
     'tabs/PrivateTabsPanel.java',
     'tabs/RemoteTabsContainerPanel.java',
     'tabs/RemoteTabsList.java',
     'tabs/RemoteTabsPanel.java',
     'tabs/RemoteTabsSetupPanel.java',
--- a/mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml
+++ b/mobile/android/base/resources/layout-large-land-v11/tabs_panel.xml
@@ -41,21 +41,20 @@
                                          gecko:tabs="tabs_normal"/>
 
         <org.mozilla.gecko.tabs.PrivateTabsPanel
                 android:id="@+id/private_tabs_panel"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:visibility="gone"/>
 
-        <org.mozilla.gecko.tabs.RemoteTabsPanel
-                android:id="@+id/remote_tabs"
-                android:layout_height="match_parent"
-                android:layout_width="match_parent"
-                android:visibility="gone"/>
+        <ViewStub android:id="@+id/remote_tabs_panel_stub"
+                  android:layout="@layout/remote_tabs_panel_view"
+                  android:layout_width="match_parent"
+                  android:layout_height="match_parent"/>
 
     </view>
 
     <RelativeLayout android:id="@+id/tabs_panel_footer"
                     android:layout_width="match_parent"
                     android:layout_height="@dimen/browser_toolbar_height">
 
         <view class="org.mozilla.gecko.tabs.TabsPanel$TabsPanelToolbar"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/remote_tabs_panel_view.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<org.mozilla.gecko.tabs.RemoteTabsPanel xmlns:android="http://schemas.android.com/apk/res/android"
+                                        android:id="@+id/remote_tabs_panel"
+                                        android:layout_width="match_parent"
+                                        android:layout_height="match_parent"/>
--- a/mobile/android/base/resources/layout/tabs_panel.xml
+++ b/mobile/android/base/resources/layout/tabs_panel.xml
@@ -40,17 +40,16 @@
                                          gecko:tabs="tabs_normal"/>
 
         <org.mozilla.gecko.tabs.PrivateTabsPanel
                 android:id="@+id/private_tabs_panel"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:visibility="gone"/>
 
-        <org.mozilla.gecko.tabs.RemoteTabsPanel
-                android:id="@+id/remote_tabs"
-                android:layout_height="match_parent"
-                android:layout_width="match_parent"
-                android:visibility="gone"/>
+        <ViewStub android:id="@+id/remote_tabs_panel_stub"
+                  android:layout="@layout/remote_tabs_panel_view"
+                  android:layout_width="match_parent"
+                  android:layout_height="match_parent"/>
 
     </view>
 
 </merge>
--- a/mobile/android/base/tabs/TabsPanel.java
+++ b/mobile/android/base/tabs/TabsPanel.java
@@ -27,16 +27,17 @@ import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewStub;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
 public class TabsPanel extends LinearLayout
                        implements GeckoPopupMenu.OnMenuItemClickListener,
@@ -108,43 +109,56 @@ public class TabsPanel extends LinearLay
         LayoutInflater.from(context).inflate(R.layout.tabs_panel, this);
         initialize();
 
         mAppStateListener = new AppStateListener() {
             @Override
             public void onResume() {
                 if (mPanel == mPanelRemote) {
                     // Refresh the remote panel.
-                    mPanelRemote.show();
+                    getRemotePanelView().show();
                 }
             }
 
             @Override
             public void onOrientationChanged() {
                 // Remote panel is already refreshed by chrome refresh.
             }
 
             @Override
             public void onPause() {}
         };
     }
 
+    /**
+     * Initializes views in tabs_panel layout
+     *
+     * @throws IllegalStateException
+     *             mCurrentPanel must have a non-null value
+     */
     private void initialize() {
+        if (mCurrentPanel == null) {
+            throw new IllegalStateException(
+                    "mCurrentPanel cannot be null in order for RemotePanelView to be initialized");
+        }
+
+        if (mCurrentPanel == Panel.REMOTE_TABS) {
+            // Initializes mPanelRemote
+            getRemotePanelView();
+        }
+
         mHeader = (RelativeLayout) findViewById(R.id.tabs_panel_header);
         mTabsContainer = (TabsListContainer) findViewById(R.id.tabs_container);
 
         mPanelNormal = (PanelView) findViewById(R.id.normal_tabs);
         mPanelNormal.setTabsPanel(this);
 
         mPanelPrivate = (PanelView) findViewById(R.id.private_tabs_panel);
         mPanelPrivate.setTabsPanel(this);
 
-        mPanelRemote = (PanelView) findViewById(R.id.remote_tabs);
-        mPanelRemote.setTabsPanel(this);
-
         mFooter = (RelativeLayout) findViewById(R.id.tabs_panel_footer);
 
         mAddTab = (ImageButton) findViewById(R.id.add_tab);
         mAddTab.setOnClickListener(new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
                 TabsPanel.this.addTab();
             }
@@ -411,17 +425,17 @@ public class TabsPanel extends LinearLay
         switch (panelToShow) {
             case NORMAL_TABS:
                 mPanel = mPanelNormal;
                 break;
             case PRIVATE_TABS:
                 mPanel = mPanelPrivate;
                 break;
             case REMOTE_TABS:
-                mPanel = mPanelRemote;
+                mPanel = getRemotePanelView();
                 break;
 
             default:
                 throw new IllegalArgumentException("Unknown panel type " + panelToShow);
         }
         mPanel.show();
 
         if (mCurrentPanel == Panel.REMOTE_TABS) {
@@ -467,16 +481,19 @@ public class TabsPanel extends LinearLay
             mPopupMenu.dismiss();
             dispatchLayoutChange(0, 0);
         }
     }
 
     public void refresh() {
         removeAllViews();
 
+        // The View that mPanelRemote points to is invalidated because the layout is invalidated.
+        // mPanelRemote must be null in order to properly initialize RemotePanelView.
+        mPanelRemote = null;
         LayoutInflater.from(mContext).inflate(R.layout.tabs_panel, this);
         initialize();
 
         if (mVisible)
             show(mCurrentPanel);
     }
 
     public void autoHidePanel() {
@@ -565,9 +582,23 @@ public class TabsPanel extends LinearLay
      */
     public Drawable getIconDrawable(Panel panel) {
         return mTabWidget.getIconDrawable(panel.ordinal());
     }
 
     public void setIconDrawable(Panel panel, int resource) {
         mTabWidget.setIconDrawable(panel.ordinal(), resource);
     }
+
+    /**
+     * Initializes mPanelRemote if necessary and provides getter because you
+     * should probably not access mPanelRemote directly
+     *
+     * @return PanelView
+     */
+    private PanelView getRemotePanelView() {
+        if (mPanelRemote == null) {
+            mPanelRemote = (PanelView) ((ViewStub) findViewById(R.id.remote_tabs_panel_stub)).inflate();
+            mPanelRemote.setTabsPanel(TabsPanel.this);
+        }
+        return mPanelRemote;
+    }
 }
--- a/mobile/android/base/tests/testSearchSuggestions.java
+++ b/mobile/android/base/tests/testSearchSuggestions.java
@@ -1,17 +1,17 @@
 package org.mozilla.gecko.tests;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 
 import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.SuggestClient;
 import org.mozilla.gecko.home.BrowserSearch;
-import org.mozilla.gecko.home.SuggestClient;
 
 import android.app.Activity;
 import android.support.v4.app.Fragment;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
 /**
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ef76eb342035c74cec6b6426231a9a75eded0a47
GIT binary patch
literal 4381
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
Xqq;F^(s`ka00000NkvXXu0mjfX4dV}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ef76eb342035c74cec6b6426231a9a75eded0a47
GIT binary patch
literal 4381
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
Xqq;F^(s`ka00000NkvXXu0mjfX4dV}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d759d069f1d572bf6f609ea5e9d006834ba001dc
GIT binary patch
literal 3131
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
V%ji#j@%;b*002ovPDHLkV1oQwpeX<V
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f13d3fa5d395f516a88f49424d96421b403cb371
GIT binary patch
literal 2115
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ef76eb342035c74cec6b6426231a9a75eded0a47
GIT binary patch
literal 4381
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
Xqq;F^(s`ka00000NkvXXu0mjfX4dV}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c93181cbf7771b45fbb89e2cb3601c2b63698db5
GIT binary patch
literal 7209
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ef76eb342035c74cec6b6426231a9a75eded0a47
GIT binary patch
literal 4381
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
Xqq;F^(s`ka00000NkvXXu0mjfX4dV}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ef76eb342035c74cec6b6426231a9a75eded0a47
GIT binary patch
literal 4381
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
Xqq;F^(s`ka00000NkvXXu0mjfX4dV}
deleted file mode 100644
--- a/mobile/android/search/java/org/mozilla/search/autocomplete/SuggestClient.java
+++ /dev/null
@@ -1,145 +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/. */
-
-package org.mozilla.search.autocomplete;
-
-import org.json.JSONArray;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-
-/**
- * Use network-based search suggestions.
- */
-public class SuggestClient {
-    private static final String LOGTAG = "GeckoSuggestClient";
-    private static final String USER_AGENT = "";
-
-    private final Context mContext;
-    private final int mTimeout;
-
-    // should contain the string "__searchTerms__", which is replaced with the query
-    private final String mSuggestTemplate;
-
-    // the maximum number of suggestions to return
-    private final int mMaxResults;
-
-    // used by robocop for testing
-    private boolean mCheckNetwork;
-
-    // used to make suggestions appear instantly after opt-in
-    private String mPrevQuery;
-    private ArrayList<String> mPrevResults;
-
-    public SuggestClient(Context context, String suggestTemplate, int timeout, int maxResults) {
-        mContext = context;
-        mMaxResults = maxResults;
-        mSuggestTemplate = suggestTemplate;
-        mTimeout = timeout;
-        mCheckNetwork = true;
-    }
-
-    /**
-     * Queries for a given search term and returns an ArrayList of suggestions.
-     */
-    public ArrayList<String> query(String query) {
-        if (query.equals(mPrevQuery))
-            return mPrevResults;
-
-        ArrayList<String> suggestions = new ArrayList<String>();
-        if (TextUtils.isEmpty(mSuggestTemplate) || TextUtils.isEmpty(query)) {
-            return suggestions;
-        }
-
-        if (!isNetworkConnected() && mCheckNetwork) {
-            Log.i(LOGTAG, "Not connected to network");
-            return suggestions;
-        }
-
-        try {
-            String encoded = URLEncoder.encode(query, "UTF-8");
-            String suggestUri = mSuggestTemplate.replace("__searchTerms__", encoded);
-
-            URL url = new URL(suggestUri);
-            String json = null;
-            HttpURLConnection urlConnection = null;
-            InputStream in = null;
-            try {
-                urlConnection = (HttpURLConnection) url.openConnection();
-                urlConnection.setConnectTimeout(mTimeout);
-                urlConnection.setRequestProperty("User-Agent", USER_AGENT);
-                in = new BufferedInputStream(urlConnection.getInputStream());
-                json = convertStreamToString(in);
-            } finally {
-                if (urlConnection != null)
-                    urlConnection.disconnect();
-                if (in != null) {
-                    try {
-                        in.close();
-                    } catch (IOException e) {
-                        Log.e(LOGTAG, "error", e);
-                    }
-                }
-            }
-
-            if (json != null) {
-                /*
-                 * Sample result:
-                 * ["foo",["food network","foothill college","foot locker",...]]
-                 */
-                JSONArray results = new JSONArray(json);
-                JSONArray jsonSuggestions = results.getJSONArray(1);
-
-                int added = 0;
-                for (int i = 0; (i < jsonSuggestions.length()) && (added < mMaxResults); i++) {
-                    String suggestion = jsonSuggestions.getString(i);
-                    if (!suggestion.equalsIgnoreCase(query)) {
-                        suggestions.add(suggestion);
-                        added++;
-                    }
-                }
-            } else {
-                Log.e(LOGTAG, "Suggestion query failed");
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Error", e);
-        }
-
-        mPrevQuery = query;
-        mPrevResults = suggestions;
-        return suggestions;
-    }
-
-    private boolean isNetworkConnected() {
-        NetworkInfo networkInfo = getActiveNetworkInfo();
-        return networkInfo != null && networkInfo.isConnected();
-    }
-
-    private NetworkInfo getActiveNetworkInfo() {
-        ConnectivityManager connectivity = (ConnectivityManager) mContext
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (connectivity == null)
-            return null;
-        return connectivity.getActiveNetworkInfo();
-    }
-
-    private String convertStreamToString(java.io.InputStream is) {
-        try {
-            return new java.util.Scanner(is).useDelimiter("\\A").next();
-        } catch (java.util.NoSuchElementException e) {
-            return "";
-        }
-    }
-}
--- a/mobile/android/search/java/org/mozilla/search/autocomplete/SuggestionsFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/autocomplete/SuggestionsFragment.java
@@ -16,16 +16,17 @@ import android.text.SpannableString;
 import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ListView;
 
+import org.mozilla.gecko.SuggestClient;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.search.AcceptsSearchQuery;
 import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
 import org.mozilla.search.Constants;
 import org.mozilla.search.R;
 import org.mozilla.search.providers.SearchEngine;
 import org.mozilla.search.providers.SearchEngineManager;
index fc0c8bb0eef4b3248e886bd9ebef1b6de9109895..a016543610e0485b0b44b6242c4fd2934aacf787
GIT binary patch
literal 397
zc$@)^0doF{P)<h;3K|Lk000e1NJLTq002P%002@50{{R3JZnip00004XF*Lt006O%
z3;baP00001b5ch_0Itp)=>Px#Y*0*8MF0Q*{r&ysp*qUS%KQ8K{QUg>{{H&<`Tzg`
z=bkeE|Nr>-_~xNJ(V$r6qd?)MOx>kZn|Vswqg2M4Skt0h<)ugR^72<kEa#y*yu7@)
zlULQ%)ytq_)6>)S_4VhVH_gq>^Yio9*Vpdu?sX9>R{#J20d!JMQvg8b*k%9#0J2F$
zK~z}7?byi<0wEAZ(LyOiaGvM!|Nl{1ptYA23lsD1lBCX#gn}q%3nPU7J~qyj<ecGn
zo#MO>XR5F;;#+}@VnNDWlCV*%B?Tf04KNU-wYn!k1_XkA5G);82sFf7IO|&BCI+(5
zhX4Tr1PBlyK!5<@hXT>QLeTMTUm-lB;N`+23ag>CYu>ZgQsLC@dJEC~?(o?bX+EBg
rdPPlU%k9^`i4f{!%l|ez|D5&(?O$QhvwD0z00000NkvXXu0mjfke9mf
index 23ce9b782e70187955f674f49682c54afe042615..515a6c91f6b10ac04019e748f64333669ac34b9a
GIT binary patch
literal 259
zc%17D@N?(olHy`uVBq!ia0vp^3P7yI!3-py+OJ#yq*&4&eH|GXHuiJ>Nn{1`8H<D5
zofy`glX(f`*aY~5xB}_t3(Wui{d;qH{PTH6fByXW_U+rRU%!CjzkmOJy3F&$y!f9#
zfByLK<M~3%&!0a(UhcnjQrz3OZx78&?~cxH02x{m<QL4~@a#q!kQ3qQ;uvCa`t21@
zz6Jvhhd`IWf>nqAzSr8p_D!YMGyLytHCBehdMi@D$$qo5?GSO;lpr3-<mB7@;n(p4
znF_Wq9FHpe{n~%-sD)~r^xj<An&+j?TTO)@iPRVgOPR!JtUe30gTd3)&t;ucLK6Ul
C{cH>X
index 588173d197d6de904d3516727c363ba0e4b8309d..c268cbde56cdf26f2d5db0465039802d0b4ddd02
GIT binary patch
literal 1482
zc%17D@N?(olHy`uVBq!ia0vp^i9lS%!3-p4oIHCHNU@|l`Z_W&Z0zU$lgJ9>GZqKA
zJ29*~C-ahlfypqyC&cyFuU`yc@COVSVBq)f-~ayo12Ud2c7_W+fBqau_V@Sy{ri`J
z6!8E5e`1uujeWYr8NW(`;K-39U%!6+^5qNAgFufwc<^A=s#V{=e+P;Hxj;dP2Z7>1
zUvJyC4afz`?%%)v_3PJ(zD96!ki!H5SeY2|!i|B9M@xO4FSLBLJOCoTp*#4}f{49S
zB7p4MOF_|bY-Tt_>d6upsH>)j@USyM*w5yh0?Efqy`C>Hv(XV_W`>$y6lDe^XH|L>
zL|Qyv?yI7r^6AqjE;feq3lmPyjr#WOo3OAjP{HQSo6F0~fg#pi<PGFrxNu>^h7E@g
zAAYvN`_ZFEmoHxidPh!5@Y%CxZ{NOMw{9KKUqJC|*RBB-zkK=f&6_vx-o1PA;suZk
z45*0{CqjHWWy+M9GiREbngY45uC6^jJqs2rSh#TE^Ld~!c|PBiSyfFG$l6g7<QM#l
zk!kS{O<{(g-`IZ@#JioAN%+pp#>yhd!OO`nBqFQFCH*zlT*`${#Yl}uKtuAXnc}Z7
z)qLj|r+`q06jw0?c}rzcdo6WwUN8L#?<Cn0T`x_W@FjSGXw?D%`|fm)@cXveK7k3D
z(+s};`|O!}=;4u^x6xN^9DjP{UCfN*{%8E{Uqe)8WUt@Gmd|_%{#TB?ck;JPecf-G
z+okqn&bnQ`rw&axk+@s#<f8khlrF}9H;Ik1|Ed}r=JS8`pBcamQ0(dA7?N@C?X9De
zZaav$C2mc+sod%=HnZ}~;WIL6Krqk1*x0!EkxlF_-Q7arn>>BX!@lRlmfCwq|DCgT
z0nbz}zaq11jSNg23JnKB987)(J9Nlf7aiNe7&6PI?#d3ENe_#@Kjjo=W}Fz+Xt>z@
z!;9$+KO!f)+?slBT40>Rr!PT)I%<(CT=Y60O}VUP#H!+LY&hZX+77Q}N^v_oIh8(q
zTdDN(yKBhdwu<YD$~p>9uP}YKb2m&9>F!sR<K9%qY0r4~cjcp<3lkhQcb;QVXgT`n
z*4kvpP506mQ#$?U3rp%f_O?$@F8E<u>z#D4;@62oEDHP|ulBM&U!DG@g@13vg-7}~
z_o#fDb#M8`2d^Yg`BgkV-eh_-K*Fwv1LP+L9K;{{hj!KvyEX45s_psP!8%*Jw0Y)-
zM|0)5qy88fKlIe;WlcZc`gzU1OEzC7t)J!={d?#8oL!~sXLIZ;cyq>6?LhzP*g&Nh
zd5T4k7`Pw4IhrjO{cUw>_KJTm>Rx>h&$S3qFA;Jq+rrVyySx1DV(vBbgzsE@uBphh
zUZO7FH}&$t$I)iF>m@uEc1?YKT{m7VM&?z)OBaVdQ@&J4zs{b`mwzYHw!`Uaw)olD
zD9iOaw_XNs&gN2i?YCc`OD5YQXm`3nYt!uK`jMLpTzB4H{^401^T|mUFWr*Oe!r^Q
zg#TsvS<~Cwrd6&!G|O4Fx$Dj>r$URn54UC5s0OT9zUt29)v8P0%u?EJ>9}Xji|XDb
z&o%{}SZXQEw>wMEsV;u&iB(C*J^%LH2?&|#ULXJLgNfFhX9t|PSOgq?lq(+AtNE)g
X6Z~k8i*U6KD5rb6`njxgN@xNAK^b2C
--- a/mobile/android/search/res/drawable/widget_button_left.xml
+++ b/mobile/android/search/res/drawable/widget_button_left.xml
@@ -1,29 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <item android:state_pressed="true">
-        <shape android:shape="rectangle">
-            <corners android:topLeftRadius="4dp"
-                     android:topRightRadius="0dp"
-                     android:bottomLeftRadius="0dp"
-                     android:bottomRightRadius="0dp"/>
-            <solid android:color="@color/widget_button_pressed"/>
-        </shape>
-    </item>
+    <item android:state_pressed="true"
+           android:drawable="@drawable/widget_button_left_pressed"/>
 
     <!-- The left button is gray in its off state -->
-    <item>
-        <shape android:shape="rectangle">
-            <corners android:topLeftRadius="4dp"
-                     android:topRightRadius="0dp"
-                     android:bottomLeftRadius="0dp"
-                     android:bottomRightRadius="0dp"/>
-            <solid android:color="@color/widget_logo_highlight"/>
-        </shape>
-    </item>
+    <item android:drawable="@drawable/widget_button_left_default"/>
 
 </selector>
new file mode 100644
--- /dev/null
+++ b/mobile/android/search/res/drawable/widget_button_left_default.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<!-- These drawables have to be wrapped in a layer-list in order to produce padding at
+     the bottom of the drawable. That padding ensures the drawable doesn't block the
+     orange strip in widget_bg.9.png -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:bottom="@dimen/widget_bg_border_offset">
+        <shape android:shape="rectangle">
+            <corners android:topLeftRadius="@dimen/widget_drawable_corner_radius"
+                     android:topRightRadius="0dp"
+                     android:bottomLeftRadius="0dp"
+                     android:bottomRightRadius="0dp"/>
+            <solid android:color="@color/widget_logo_default"/>
+        </shape>
+    </item>
+</layer-list>
new file mode 100644
--- /dev/null
+++ b/mobile/android/search/res/drawable/widget_button_left_pressed.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<!-- These drawables have to be wrapped in a layer-list in order to produce padding at
+     the bottom of the drawable. That padding ensures the drawable doesn't block the
+     orange strip in widget_bg.9.png -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:bottom="@dimen/widget_bg_border_offset">
+        <shape android:shape="rectangle">
+            <corners android:topLeftRadius="@dimen/widget_drawable_corner_radius"
+                     android:topRightRadius="0dp"
+                     android:bottomLeftRadius="0dp"
+                     android:bottomRightRadius="0dp"/>
+            <solid android:color="@color/widget_button_pressed"/>
+        </shape>
+    </item>
+</layer-list>
--- a/mobile/android/search/res/drawable/widget_button_middle.xml
+++ b/mobile/android/search/res/drawable/widget_button_middle.xml
@@ -1,20 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <item android:state_pressed="true">
-        <shape android:shape="rectangle">
-            <corners android:topLeftRadius="0dp"
-                     android:topRightRadius="0dp"
-                     android:bottomLeftRadius="0dp"
-                     android:bottomRightRadius="0dp"/>
-            <solid android:color="@color/widget_button_pressed"/>
-        </shape>
-    </item>
+    <item android:state_pressed="true"
+           android:drawable="@drawable/widget_button_middle_pressed"/>
 
     <item android:drawable="@android:color/transparent"/>
 
 </selector>
new file mode 100644
--- /dev/null
+++ b/mobile/android/search/res/drawable/widget_button_middle_pressed.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<!-- These drawables have to be wrapped in a layer-list in order to produce padding at
+     the bottom of the drawable. That padding ensures the drawable doesn't block the
+     orange strip in widget_bg.9.png -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:bottom="@dimen/widget_bg_border_offset">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/widget_button_pressed"/>
+        </shape>
+    </item>
+</layer-list>
--- a/mobile/android/search/res/drawable/widget_button_right.xml
+++ b/mobile/android/search/res/drawable/widget_button_right.xml
@@ -1,20 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <item android:state_pressed="true">
-        <shape android:shape="rectangle">
-            <corners android:topLeftRadius="0dp"
-                     android:topRightRadius="4dp"
-                     android:bottomLeftRadius="0dp"
-                     android:bottomRightRadius="0dp"/>
-            <solid android:color="@color/widget_button_pressed"/>
-        </shape>
-    </item>
+    <item android:state_pressed="true"
+          android:drawable="@drawable/widget_button_right_pressed"/>
 
     <item android:drawable="@android:color/transparent"/>
 
 </selector>
new file mode 100644
--- /dev/null
+++ b/mobile/android/search/res/drawable/widget_button_right_pressed.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<!-- These drawables have to be wrapped in a layer-list in order to produce padding at
+     the bottom of the drawable. That padding ensures the drawable doesn't block the
+     orange strip in widget_bg.9.png -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:bottom="@dimen/widget_bg_border_offset">
+        <shape android:shape="rectangle">
+            <corners android:topLeftRadius="0dp"
+                     android:topRightRadius="@dimen/widget_drawable_corner_radius"
+                     android:bottomLeftRadius="0dp"
+                     android:bottomRightRadius="0dp"/>
+            <solid android:color="@color/widget_button_pressed"/>
+        </shape>
+    </item>
+</layer-list>
--- a/mobile/android/search/res/layout/search_widget.xml
+++ b/mobile/android/search/res/layout/search_widget.xml
@@ -10,38 +10,40 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/widget_header_height"
     android:orientation="horizontal"
     android:background="@drawable/widget_bg">
 
     <ImageView android:id="@+id/logo_button"
         android:layout_width="0dp"
         android:layout_weight="1"
+        android:padding="@dimen/widget_padding"
         android:background="@drawable/widget_button_left"
         android:layout_height="match_parent"
-        android:src="@drawable/icon"/>
+        android:src="@drawable/widget_icon"/>
 
     <LinearLayout android:id="@+id/search_button"
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1"
         android:gravity="center"
         android:contentDescription="@string/search_widget_button_label"
         android:orientation="horizontal"
         android:background="@drawable/widget_button_middle">
 
         <TextView android:id="@+id/search_button_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:drawableLeft="@drawable/ic_widget_search"
-            android:drawablePadding="@dimen/widget_drawable_padding"
+            android:drawablePadding="@dimen/widget_padding"
             android:text="@string/search_widget_button_label"
             android:gravity="center"
+            android:fontFamily="sans-serif"
             android:textSize="@dimen/widget_text_size"
-            android:textColor="@color/text_color_secondary"/>
+            android:textColor="@color/widget_text_color"/>
 
     </LinearLayout>
 
     <LinearLayout android:id="@+id/new_tab_button"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="match_parent"
         android:layout_centerVertical="true"
@@ -49,17 +51,18 @@
         android:gravity="center"
         android:orientation="horizontal"
         android:background="@drawable/widget_button_right">
 
         <TextView android:id="@+id/new_tab_button_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:drawableLeft="@drawable/ic_widget_new_tab"
-            android:drawablePadding="@dimen/widget_drawable_padding"
+            android:drawablePadding="@dimen/widget_padding"
             android:gravity="center"
             android:text="@string/new_tab"
+            android:fontFamily="sans-serif"
             android:textSize="@dimen/widget_text_size"
-            android:textColor="@color/text_color_secondary"/>
+            android:textColor="@color/widget_text_color"/>
 
     </LinearLayout>
 
 </LinearLayout>
--- a/mobile/android/search/res/values/search_colors.xml
+++ b/mobile/android/search/res/values/search_colors.xml
@@ -10,13 +10,14 @@
 
     <color name="edit_text_default">#AFB1B3</color>
 
     <!-- card colors -->
     <color name="card_background">#ffffff</color>
     <color name="card_background_pressed">#DCDCE1</color>
     <color name="card_border">#BFBFBF</color>
 
-    <color name="widget_logo_highlight">#11000000</color>
+    <color name="widget_logo_default">#EBEBF0</color>
     <color name="widget_button_pressed">#33000000</color>
+    <color name="widget_text_color">#5F6368</color>
 
     <!-- Search suggestion highlight color is defined in SearchFragment.java -->
 </resources>
--- a/mobile/android/search/res/values/search_dimens.xml
+++ b/mobile/android/search/res/values/search_dimens.xml
@@ -29,12 +29,14 @@
     <dimen name="jump_button_padding_x">15dp</dimen>
 
     <dimen name="search_bar_padding_y">10dp</dimen>
 
     <!-- Widget Buttons -->
     <dimen name="widget_header_height">70dp</dimen>
     <dimen name="widget_button_offset">-50dp</dimen>
     <dimen name="widget_button_padding">45dp</dimen>
-    <dimen name="widget_text_size">13sp</dimen>
-    <dimen name="widget_drawable_padding">4dp</dimen>
+    <dimen name="widget_text_size">14sp</dimen>
+    <dimen name="widget_padding">7dp</dimen>
+    <dimen name="widget_drawable_corner_radius">4dp</dimen>
+    <dimen name="widget_bg_border_offset">3dp</dimen>
 
 </resources>
--- a/mobile/android/search/search_activity_sources.mozbuild
+++ b/mobile/android/search/search_activity_sources.mozbuild
@@ -3,17 +3,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/.
 
 search_activity_sources = [
     'java/org/mozilla/search/AcceptsSearchQuery.java',
     'java/org/mozilla/search/autocomplete/AutoCompleteAdapter.java',
     'java/org/mozilla/search/autocomplete/ClearableEditText.java',
-    'java/org/mozilla/search/autocomplete/SuggestClient.java',
     'java/org/mozilla/search/autocomplete/SuggestionsFragment.java',
     'java/org/mozilla/search/Constants.java',
     'java/org/mozilla/search/MainActivity.java',
     'java/org/mozilla/search/PostSearchFragment.java',
     'java/org/mozilla/search/PreSearchFragment.java',
     'java/org/mozilla/search/providers/BingSearchEngine.java',
     'java/org/mozilla/search/providers/GoogleSearchEngine.java',
     'java/org/mozilla/search/providers/SearchEngine.java',
--- a/toolkit/devtools/webconsole/utils.js
+++ b/toolkit/devtools/webconsole/utils.js
@@ -565,31 +565,29 @@ let WebConsoleUtils = {
   },
   /**
    * The inputNode "paste" event handler generator. Helps prevent self-xss attacks
    *
    * @param nsIDOMElement inputField
    * @param nsIDOMElement notificationBox
    * @returns A function to be added as a handler to 'paste' and 'drop' events on the input field
    */
-  pasteHandlerGen: function WCU_pasteHandlerGen(inputField, notificationBox){
+  pasteHandlerGen: function WCU_pasteHandlerGen(inputField, notificationBox, msg, okstring) {
     let handler = function WCU_pasteHandler(aEvent) {
       if (WebConsoleUtils.usageCount >= CONSOLE_ENTRY_THRESHOLD) {
         inputField.removeEventListener("paste", handler);
         inputField.removeEventListener("drop", handler);
         return true;
       }
       if (notificationBox.getNotificationWithValue("selfxss-notification")) {
         aEvent.preventDefault();
         aEvent.stopPropagation();
         return false;
       }
-      let l10n = new WebConsoleUtils.l10n("chrome://browser/locale/devtools/webconsole.properties");
-      let okstring = l10n.getStr("selfxss.okstring");
-      let msg = l10n.getFormatStr("selfxss.msg", [okstring]);
+
 
       let notification = notificationBox.appendNotification(msg,
         "selfxss-notification", null, notificationBox.PRIORITY_WARNING_HIGH, null,
         function(eventType) {
           // Cleanup function if notification is dismissed
           if (eventType == "removed") {
             inputField.removeEventListener("keyup", pasteKeyUpHandler);
           }
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -3101,31 +3101,34 @@ var gDetailView = {
         warning.textContent = gStrings.ext.GetStringFromName("details.notification.openH264Pending");
       } else {
         this.node.removeAttribute("notification");
       }
     }
 
     let menulist = document.getElementById("detail-state-menulist");
     let addonType = AddonManager.addonTypes[this._addon.type];
-    if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE &&
-        (hasPermission(this._addon, "ask_to_activate") ||
-         hasPermission(this._addon, "enable") ||
-         hasPermission(this._addon, "disable"))) {
+    if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) {
       let askItem = document.getElementById("detail-ask-to-activate-menuitem");
       let alwaysItem = document.getElementById("detail-always-activate-menuitem");
       let neverItem = document.getElementById("detail-never-activate-menuitem");
+      let hasActivatePermission =
+        ["ask_to_activate", "enable", "disable"].some(perm => hasPermission(this._addon, perm));
+
       if (this._addon.userDisabled === true) {
         menulist.selectedItem = neverItem;
       } else if (this._addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE) {
         menulist.selectedItem = askItem;
       } else {
         menulist.selectedItem = alwaysItem;
       }
+
+      menulist.disabled = !hasActivatePermission;
       menulist.hidden = false;
+      menulist.classList.add('no-auto-hide');
     } else {
       menulist.hidden = true;
     }
 
     this.node.setAttribute("active", this._addon.isActive);
   },
 
   clearLoading: function gDetailView_clearLoading() {
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1301,36 +1301,36 @@
               this.removeAttribute("notification");
             }
           }
 
           this._preferencesBtn.hidden = (!this.mAddon.optionsURL) ||
                                         this.mAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_INFO;
 
           let addonType = AddonManager.addonTypes[this.mAddon.type];
-          if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE &&
-              (this.hasPermission("ask_to_activate") ||
-               this.hasPermission("enable") ||
-               this.hasPermission("disable"))) {
+          if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) {
             this._enableBtn.disabled = true;
             this._disableBtn.disabled = true;
             this._askToActivateMenuitem.disabled = !this.hasPermission("ask_to_activate");
             this._alwaysActivateMenuitem.disabled = !this.hasPermission("enable");
             this._neverActivateMenuitem.disabled = !this.hasPermission("disable");
             if (this.mAddon.userDisabled === true) {
               this._stateMenulist.selectedItem = this._neverActivateMenuitem;
             } else if (this.mAddon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE) {
               this._stateMenulist.selectedItem = this._askToActivateMenuitem;
             } else {
               this._stateMenulist.selectedItem = this._alwaysActivateMenuitem;
             }
-            this._stateMenulist.selectedItem.disabled = false;
-            this._stateMenulist.disabled = false;
+            let hasActivatePermission =
+              ["ask_to_activate", "enable", "disable"].some(perm => this.hasPermission(perm));
+            this._stateMenulist.disabled = !hasActivatePermission;
+            this._stateMenulist.hidden = false;
+            this._stateMenulist.classList.add('no-auto-hide');
           } else {
-            this._stateMenulist.disabled = true;
+            this._stateMenulist.hidden = true;
             if (this.hasPermission("enable")) {
               this._enableBtn.hidden = false;
               let tooltip = gViewController.commands["cmd_enableItem"]
                                            .getTooltip(this.mAddon);
               this._enableBtn.setAttribute("tooltiptext", tooltip);
             } else {
               this._enableBtn.hidden = true;
             }
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -456,16 +456,19 @@ function PluginWrapper(aId, aName, aDesc
   });
 
   this.__defineGetter__("operationsRequiringRestart", function() {
     return AddonManager.OP_NEEDS_RESTART_NONE;
   });
 
   this.__defineGetter__("permissions", function() {
     let permissions = 0;
+    if (aTags[0].isEnabledStateLocked) {
+      return permissions;
+    }
     if (!this.appDisabled) {
 
       if (this.userDisabled !== true)
         permissions |= AddonManager.PERM_CAN_DISABLE;
 
       let blocklistState = this.blocklistState;
       let isCTPBlocklisted =
         (blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE ||
--- a/toolkit/mozapps/extensions/test/browser/browser-common.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser-common.ini
@@ -39,16 +39,17 @@ skip-if = e10s # Bug ?????? - test times
 [browser_dragdrop.js]
 skip-if = buildapp == 'mulet'
 [browser_experiments.js]
 [browser_list.js]
 [browser_metadataTimeout.js]
 [browser_searching.js]
 [browser_sorting.js]
 [browser_sorting_plugins.js]
+[browser_plugin_enabled_state_locked.js]
 skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #1760 about:blank]
 [browser_uninstalling.js]
 skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #1760 about:blank]
 [browser_install.js]
 [browser_recentupdates.js]
 [browser_manualupdates.js]
 [browser_globalwarnings.js]
 [browser_globalinformations.js]
--- a/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js
@@ -195,26 +195,26 @@ function part11() {
   });
 }
 
 function part12(aWindow) {
   gManagerWindow = aWindow;
   let pluginEl = get_addon_element(gManagerWindow, gTestPluginId);
   pluginEl.parentNode.ensureElementIsVisible(pluginEl);
   let menu = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "state-menulist");
-  is_element_hidden(menu, "part12: state menu should be hidden");
+  is(menu.disabled, true, "part12: state menu should be disabled");
 
   let details = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "details-btn");
   EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
   wait_for_view_load(gManagerWindow, part13);
 }
 
 function part13() {
   let menu = gManagerWindow.document.getElementById("detail-state-menulist");
-  is_element_hidden(menu, "part13: detail state menu should be hidden");
+  is(menu.disabled, true, "part13: detail state menu should be disabled");
 
   setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", function() {
     run_next_test();
   });
 }
 
 function end_test() {
   Services.prefs.clearUserPref("plugins.click_to_play");
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_plugin_enabled_state_locked.js
@@ -0,0 +1,121 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that state menu is displayed correctly (enabled or disabled) in the add-on manager
+// when the preference is unlocked / locked
+const {classes: Cc, interfaces: Ci} = Components;
+const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
+const gIsOSX = ("nsILocalFileMac" in Ci);
+const gIsLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc) ||
+  ("@mozilla.org/gio-service;1" in Cc);
+
+let gManagerWindow;
+let gCategoryUtilities;
+let gPluginElement;
+
+function getTestPluginPref() {
+  let prefix = "plugin.state.";
+  if (gIsWindows)
+    return prefix + "nptest";
+  else if (gIsLinux)
+    return prefix + "libnptest";
+  else
+    return prefix + "test";
+}
+
+registerCleanupFunction(() => {
+  Services.prefs.unlockPref(getTestPluginPref());
+  Services.prefs.clearUserPref(getTestPluginPref());
+});
+
+function getPlugins() {
+  let deferred = Promise.defer();
+  AddonManager.getAddonsByTypes(["plugin"], plugins => deferred.resolve(plugins));
+  return deferred.promise;
+}
+
+function getTestPlugin(aPlugins) {
+  let testPluginId;
+
+  for (let plugin of aPlugins) {
+    if (plugin.name == "Test Plug-in") {
+      testPluginId = plugin.id;
+      break;
+    }
+  }
+
+  Assert.ok(testPluginId, "Test Plug-in should exist");
+
+  let pluginElement = get_addon_element(gManagerWindow, testPluginId);
+  pluginElement.parentNode.ensureElementIsVisible(pluginElement);
+
+  return pluginElement;
+}
+
+function checkStateMenu(locked) {
+  Assert.equal(Services.prefs.prefIsLocked(getTestPluginPref()), locked,
+    "Preference lock state should be correct.");
+  let menuList = gManagerWindow.document.getAnonymousElementByAttribute(gPluginElement, "anonid", "state-menulist");
+
+  is_element_visible(menuList, "State menu should be visible.");
+  Assert.equal(menuList.disabled, locked,
+    "State menu should" + (locked === true ? "" : " not") + " be disabled.");
+}
+
+function checkStateMenuDetail(locked) {
+  Assert.equal(Services.prefs.prefIsLocked(getTestPluginPref()), locked,
+    "Preference should be " + (locked === true ? "" : "un") + "locked.");
+
+  // open details menu
+  let details = gManagerWindow.document.getAnonymousElementByAttribute(gPluginElement, "anonid", "details-btn");
+  is_element_visible(details, "Details link should be visible.");
+  EventUtils.synthesizeMouseAtCenter(details, {}, gManagerWindow);
+
+  let deferred = Promise.defer();
+  wait_for_view_load(gManagerWindow, function() {
+    let menuList = gManagerWindow.document.getElementById("detail-state-menulist");
+    is_element_visible(menuList, "Details state menu should be visible.");
+    Assert.equal(menuList.disabled, locked,
+      "Details state menu enabled state should be correct.");
+    deferred.resolve();
+  });
+  return deferred.promise;
+}
+
+add_task(function* initializeState() {
+  Services.prefs.setIntPref(getTestPluginPref(), Ci.nsIPluginTag.STATE_ENABLED);
+  Services.prefs.unlockPref(getTestPluginPref());
+  gManagerWindow = yield open_manager();
+  gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+  yield gCategoryUtilities.openType("plugin");
+
+  let plugins = yield getPlugins();
+  gPluginElement = getTestPlugin(plugins);
+});
+
+// Tests that plugin state menu is enabled if the preference is unlocked
+add_task(function* taskCheckStateMenuIsEnabled() {
+  checkStateMenu(false);
+  yield checkStateMenuDetail(false);
+});
+
+// Lock the preference and then reload the plugin category
+add_task(function* reinitializeState() {
+  // lock the preference
+  Services.prefs.lockPref(getTestPluginPref());
+  yield gCategoryUtilities.openType("plugin");
+  // Retrieve the test plugin element
+  let plugins = yield getPlugins();
+  gPluginElement = getTestPlugin(plugins);
+});
+
+// Tests that plugin state menu is disabled if the preference is locked
+add_task(function* taskCheckStateMenuIsDisabled() {
+  checkStateMenu(true);
+  yield checkStateMenuDetail(true);
+});
+
+add_task(function* testCleanup() {
+  yield close_manager(gManagerWindow);
+});
rename from browser/themes/linux/in-content/common.css
rename to toolkit/themes/linux/global/in-content/common.css
--- a/browser/themes/linux/in-content/common.css
+++ b/toolkit/themes/linux/global/in-content/common.css
@@ -1,13 +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/. */
 
-%include ../../shared/in-content/common.inc.css
+%include ../../../shared/in-content/common.inc.css
 
 xul|tab[selected] {
   /* Override styles for tab[selected] from
      toolkit/themes/linux/global/tabbox.css */
   margin-bottom: 0;
   border-bottom-left-radius: 0;
   border-bottom-right-radius: 0;
 }
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -49,9 +49,20 @@ toolkit.jar:
 +  skin/classic/global/icons/panelarrow-horizontal.svg         (icons/panelarrow-horizontal.svg)
 +  skin/classic/global/icons/panelarrow-vertical.svg           (icons/panelarrow-vertical.svg)
 +  skin/classic/global/icons/resizer.png                       (icons/resizer.png)
 +  skin/classic/global/icons/sslWarning.png                    (icons/sslWarning.png)
 +  skin/classic/global/icons/wrap.png                          (icons/wrap.png)
 +  skin/classic/global/icons/webapps-16.png                    (icons/webapps-16.png)
 +  skin/classic/global/icons/webapps-64.png                    (icons/webapps-64.png)
    skin/classic/global/menu/shared-menu-check.png              (../../shared/menu-check.png)
+*  skin/classic/global/in-content/common.css                   (in-content/common.css)
+   skin/classic/global/in-content/check.png                    (../../shared/in-content/check.png)
+   skin/classic/global/in-content/check@2x.png                 (../../shared/in-content/check@2x.png)
+   skin/classic/global/in-content/dropdown.png                 (../../shared/in-content/dropdown.png)
+   skin/classic/global/in-content/dropdown@2x.png              (../../shared/in-content/dropdown@2x.png)
+   skin/classic/global/in-content/dropdown-disabled.png        (../../shared/in-content/dropdown-disabled.png)
+   skin/classic/global/in-content/dropdown-disabled@2x.png     (../../shared/in-content/dropdown-disabled@2x.png)
+   skin/classic/global/in-content/help-glyph.png               (../../shared/in-content/help-glyph.png)
+   skin/classic/global/in-content/help-glyph@2x.png            (../../shared/in-content/help-glyph@2x.png)
+   skin/classic/global/in-content/sorter.png                   (../../shared/in-content/sorter.png)
+   skin/classic/global/in-content/sorter@2x.png                (../../shared/in-content/sorter@2x.png)
 +  skin/classic/global/toolbar/spring.png                      (toolbar/spring.png)
--- a/toolkit/themes/linux/mozapps/extensions/extensions.css
+++ b/toolkit/themes/linux/mozapps/extensions/extensions.css
@@ -879,16 +879,20 @@ setting[type="radio"] > radiogroup {
 
 
 /*** buttons ***/
 
 .addon-control[disabled="true"] {
   display: none;
 }
 
+.addon-control.no-auto-hide {
+  display: block;
+}
+
 .addon-control.enable {
   list-style-image: url("moz-icon://stock/gtk-yes?size=button");
 }
 
 .addon-control.disable {
   list-style-image: url("moz-icon://stock/gtk-no?size=button");
 }
 
rename from browser/themes/osx/in-content/common.css
rename to toolkit/themes/osx/global/in-content/common.css
--- a/browser/themes/osx/in-content/common.css
+++ b/toolkit/themes/osx/global/in-content/common.css
@@ -1,13 +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/. */
 
-%include ../../shared/in-content/common.inc.css
+%include ../../../shared/in-content/common.inc.css
 
 xul|tabs {
   padding-right: 0;
   padding-left: 0;
 }
 
 xul|tab[selected] {
   text-shadow: none;
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -179,16 +179,27 @@ toolkit.jar:
   skin/classic/global/media/volume-full.png                          (media/volume-full.png)
   skin/classic/global/media/volume-full@2x.png                       (media/volume-full@2x.png)
   skin/classic/global/media/clicktoplay-bgtexture.png                (media/clicktoplay-bgtexture.png)
   skin/classic/global/media/videoClickToPlayButton.svg               (media/videoClickToPlayButton.svg)
   skin/classic/global/menu/menu-arrow.png                            (menu/menu-arrow.png)
   skin/classic/global/menu/menu-arrow@2x.png                         (menu/menu-arrow@2x.png)
   skin/classic/global/menu/shared-menu-check.png                     (../../shared/menu-check.png)
   skin/classic/global/menu/shared-menu-check@2x.png                  (../../shared/menu-check@2x.png)
+* skin/classic/global/in-content/common.css                          (in-content/common.css)
+  skin/classic/global/in-content/check.png                           (../../shared/in-content/check.png)
+  skin/classic/global/in-content/check@2x.png                        (../../shared/in-content/check@2x.png)
+  skin/classic/global/in-content/dropdown.png                        (../../shared/in-content/dropdown.png)
+  skin/classic/global/in-content/dropdown@2x.png                     (../../shared/in-content/dropdown@2x.png)
+  skin/classic/global/in-content/dropdown-disabled.png               (../../shared/in-content/dropdown-disabled.png)
+  skin/classic/global/in-content/dropdown-disabled@2x.png            (../../shared/in-content/dropdown-disabled@2x.png)
+  skin/classic/global/in-content/help-glyph.png                      (../../shared/in-content/help-glyph.png)
+  skin/classic/global/in-content/help-glyph@2x.png                   (../../shared/in-content/help-glyph@2x.png)
+  skin/classic/global/in-content/sorter.png                          (../../shared/in-content/sorter.png)
+  skin/classic/global/in-content/sorter@2x.png                       (../../shared/in-content/sorter@2x.png)
   skin/classic/global/scale/scale-tray-horiz.gif                     (scale/scale-tray-horiz.gif)
   skin/classic/global/scale/scale-tray-vert.gif                      (scale/scale-tray-vert.gif)
   skin/classic/global/splitter/dimple.png                            (splitter/dimple.png)
   skin/classic/global/splitter/grip-bottom.gif                       (splitter/grip-bottom.gif)
   skin/classic/global/splitter/grip-top.gif                          (splitter/grip-top.gif)
   skin/classic/global/splitter/grip-left.gif                         (splitter/grip-left.gif)
   skin/classic/global/splitter/grip-right.gif                        (splitter/grip-right.gif)
   skin/classic/global/toolbar/spring.png                             (toolbar/spring.png)
--- a/toolkit/themes/osx/mozapps/extensions/extensions.css
+++ b/toolkit/themes/osx/mozapps/extensions/extensions.css
@@ -1103,16 +1103,20 @@ setting[type="radio"] > radiogroup {
 
 
 /*** buttons ***/
 
 .addon-control[disabled="true"] {
   display: none;
 }
 
+.addon-control.no-auto-hide {
+  display: block;
+}
+
 button.button-link {
   -moz-appearance: none;
   background: transparent;
   border: none;
   box-shadow: none;
   text-decoration: underline;
   color: #0066CC;
   cursor: pointer;
rename from browser/themes/shared/in-content/check.png
rename to toolkit/themes/shared/in-content/check.png
rename from browser/themes/shared/in-content/check@2x.png
rename to toolkit/themes/shared/in-content/check@2x.png
rename from browser/themes/shared/in-content/common.inc.css
rename to toolkit/themes/shared/in-content/common.inc.css
--- a/browser/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -189,17 +189,17 @@ xul|button[type="menu"] > xul|*.button-b
   -moz-appearance: none;
   margin: 1px 0;
   -moz-margin-start: 10px;
   padding: 0;
   width: 10px;
   height: 16px;
   border: none;
   background-color: transparent;
-  list-style-image: url("chrome://browser/skin/in-content/dropdown.png");
+  list-style-image: url("chrome://global/skin/in-content/dropdown.png");
 }
 
 xul|*.help-button {
   min-width: 30px;
   border-radius: 2px;
   border: 1px solid #c1c1c1;
   background-color: #ffcb00;
   background-image: none;
@@ -222,24 +222,24 @@ xul|*.help-button > xul|*.button-box {
   padding-bottom: 0;
   padding-right: 0 !important;
   padding-left: 0 !important;
 }
 
 xul|*.help-button > xul|*.button-box > xul|*.button-icon {
   width: 26px;
   height: 26px;
-  background-image: url("chrome://browser/skin/in-content/help-glyph.png");
+  background-image: url("chrome://global/skin/in-content/help-glyph.png");
   background-position: center;
 }
 
 @media (min-resolution: 2dppx) {
   xul|*.help-button > xul|*.button-box > xul|*.button-icon {
     background-size: 26px 26px;
-    background-image: url("chrome://browser/skin/in-content/help-glyph@2x.png");
+    background-image: url("chrome://global/skin/in-content/help-glyph@2x.png");
   }
 }
 
 xul|*.help-button > xul|*.button-box > xul|*.button-text {
   display: none;
 }
 
 xul|*.spinbuttons-button {
@@ -278,31 +278,31 @@ xul|*.spinbuttons-down[disabled="true"] 
 }
 
 xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
   -moz-appearance: none;
   -moz-margin-end: 10px;
   padding: 0;
   border: none;
   background-color: transparent;
-  list-style-image: url("chrome://browser/skin/in-content/dropdown.png");
+  list-style-image: url("chrome://global/skin/in-content/dropdown.png");
 }
 
 xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker {
-  list-style-image: url("chrome://browser/skin/in-content/dropdown-disabled.png")
+  list-style-image: url("chrome://global/skin/in-content/dropdown-disabled.png")
 }
 
 @media (min-resolution: 2dppx) {
   xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker,
   xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
-    list-style-image: url("chrome://browser/skin/in-content/dropdown@2x.png");
+    list-style-image: url("chrome://global/skin/in-content/dropdown@2x.png");
   }
 
   xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker {
-    list-style-image: url("chrome://browser/skin/in-content/dropdown-disabled@2x.png")
+    list-style-image: url("chrome://global/skin/in-content/dropdown-disabled@2x.png")
   }
 
   xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker > xul|*.dropmarker-icon,
   xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker > xul|*.dropmarker-icon {
     width: 10px;
     height: 16px;
   }
 }
@@ -426,34 +426,34 @@ xul|*.checkbox-check {
   box-shadow: 0 1px 1px 0 #fff, inset 0 2px 0 0 rgba(0,0,0,0.03);
 }
 
 xul|checkbox:not([disabled="true"]):hover > xul|*.checkbox-check {
   border-color: #0095dd;
 }
 
 xul|*.checkbox-check[checked] {
-  background-image: url("chrome://browser/skin/in-content/check.png"),
+  background-image: url("chrome://global/skin/in-content/check.png"),
                     /* !important needed to override toolkit !important rule */
                     linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
 }
 
 xul|checkbox[disabled="true"] > xul|*.checkbox-check {
   opacity: 0.5;
 }
 
 xul|*.checkbox-label-box {
   -moz-margin-start: -1px; /* negative margin for the transparent border */
   -moz-padding-start: 0;
 }
 
 @media (min-resolution: 2dppx) {
   xul|*.checkbox-check[checked] {
     background-size: 12px 12px, auto;
-    background-image: url("chrome://browser/skin/in-content/check@2x.png"),
+    background-image: url("chrome://global/skin/in-content/check@2x.png"),
                       linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
   }
 }
 
 xul|*.radio-check {
   -moz-appearance: none;
   width: 23px;
   height: 23px;
rename from browser/themes/shared/in-content/dropdown-disabled.png
rename to toolkit/themes/shared/in-content/dropdown-disabled.png
rename from browser/themes/shared/in-content/dropdown-disabled@2x.png
rename to toolkit/themes/shared/in-content/dropdown-disabled@2x.png
rename from browser/themes/shared/in-content/dropdown.png
rename to toolkit/themes/shared/in-content/dropdown.png
rename from browser/themes/shared/in-content/dropdown@2x.png
rename to toolkit/themes/shared/in-content/dropdown@2x.png
rename from browser/themes/shared/in-content/help-glyph.png
rename to toolkit/themes/shared/in-content/help-glyph.png
rename from browser/themes/shared/in-content/help-glyph@2x.png
rename to toolkit/themes/shared/in-content/help-glyph@2x.png
rename from browser/themes/shared/in-content/sorter.png
rename to toolkit/themes/shared/in-content/sorter.png
rename from browser/themes/shared/in-content/sorter@2x.png
rename to toolkit/themes/shared/in-content/sorter@2x.png
rename from browser/themes/windows/in-content/common.css
rename to toolkit/themes/windows/global/in-content/common.css
--- a/browser/themes/windows/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -1,13 +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/. */
 
-%include ../../shared/in-content/common.inc.css
+%include ../../../shared/in-content/common.inc.css
 
 xul|caption {
   background-color: transparent;
 }
 
 xul|button,
 xul|colorpicker[type="button"],
 xul|menulist {
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -164,16 +164,27 @@ toolkit.jar:
         skin/classic/global/media/throbber.png                   (media/throbber.png)
         skin/classic/global/media/stalled.png                    (media/stalled.png)
         skin/classic/global/media/volume-empty.png               (media/volume-empty.png)
         skin/classic/global/media/volume-full.png                (media/volume-full.png)
         skin/classic/global/media/error.png                      (media/error.png)
         skin/classic/global/media/clicktoplay-bgtexture.png      (media/clicktoplay-bgtexture.png)
         skin/classic/global/media/videoClickToPlayButton.svg     (media/videoClickToPlayButton.svg)
         skin/classic/global/menu/shared-menu-check.png           (../../shared/menu-check.png)
+*       skin/classic/global/in-content/common.css                (in-content/common.css)
+        skin/classic/global/in-content/check.png                 (../../shared/in-content/check.png)
+        skin/classic/global/in-content/check@2x.png              (../../shared/in-content/check@2x.png)
+        skin/classic/global/in-content/dropdown.png              (../../shared/in-content/dropdown.png)
+        skin/classic/global/in-content/dropdown@2x.png           (../../shared/in-content/dropdown@2x.png)
+        skin/classic/global/in-content/dropdown-disabled.png     (../../shared/in-content/dropdown-disabled.png)
+        skin/classic/global/in-content/dropdown-disabled@2x.png  (../../shared/in-content/dropdown-disabled@2x.png)
+        skin/classic/global/in-content/help-glyph.png            (../../shared/in-content/help-glyph.png)
+        skin/classic/global/in-content/help-glyph@2x.png         (../../shared/in-content/help-glyph@2x.png)
+        skin/classic/global/in-content/sorter.png                (../../shared/in-content/sorter.png)
+        skin/classic/global/in-content/sorter@2x.png             (../../shared/in-content/sorter@2x.png)
         skin/classic/global/printpreview/arrow-left.png          (printpreview/arrow-left.png)
         skin/classic/global/printpreview/arrow-left-end.png      (printpreview/arrow-left-end.png)
         skin/classic/global/printpreview/arrow-right.png         (printpreview/arrow-right.png)
         skin/classic/global/printpreview/arrow-right-end.png     (printpreview/arrow-right-end.png)
         skin/classic/global/radio/radio-check.gif                (radio/radio-check.gif)
         skin/classic/global/radio/radio-check-dis.gif            (radio/radio-check-dis.gif)
         skin/classic/global/scrollbar/slider.gif                 (scrollbar/slider.gif)
         skin/classic/global/splitter/grip-bottom.gif             (splitter/grip-bottom.gif)
@@ -347,16 +358,27 @@ toolkit.jar:
         skin/classic/aero/global/media/throbber.png                      (media/throbber.png)
         skin/classic/aero/global/media/stalled.png                       (media/stalled.png)
         skin/classic/aero/global/media/volume-empty.png                  (media/volume-empty.png)
         skin/classic/aero/global/media/volume-full.png                   (media/volume-full.png)
         skin/classic/aero/global/media/error.png                         (media/error.png)
         skin/classic/aero/global/media/clicktoplay-bgtexture.png         (media/clicktoplay-bgtexture.png)
         skin/classic/aero/global/media/videoClickToPlayButton.svg        (media/videoClickToPlayButton.svg)
         skin/classic/aero/global/menu/shared-menu-check.png              (../../shared/menu-check.png)
+*       skin/classic/aero/global/in-content/common.css                   (in-content/common.css)
+        skin/classic/aero/global/in-content/check.png                    (../../shared/in-content/check.png)
+        skin/classic/aero/global/in-content/check@2x.png                 (../../shared/in-content/check@2x.png)
+        skin/classic/aero/global/in-content/dropdown.png                 (../../shared/in-content/dropdown.png)
+        skin/classic/aero/global/in-content/dropdown@2x.png              (../../shared/in-content/dropdown@2x.png)
+        skin/classic/aero/global/in-content/dropdown-disabled.png        (../../shared/in-content/dropdown-disabled.png)
+        skin/classic/aero/global/in-content/dropdown-disabled@2x.png     (../../shared/in-content/dropdown-disabled@2x.png)
+        skin/classic/aero/global/in-content/help-glyph.png               (../../shared/in-content/help-glyph.png)
+        skin/classic/aero/global/in-content/help-glyph@2x.png            (../../shared/in-content/help-glyph@2x.png)
+        skin/classic/aero/global/in-content/sorter.png                   (../../shared/in-content/sorter.png)
+        skin/classic/aero/global/in-content/sorter@2x.png                (../../shared/in-content/sorter@2x.png)
         skin/classic/aero/global/printpreview/arrow-left.png             (printpreview/arrow-left-aero.png)
         skin/classic/aero/global/printpreview/arrow-left-end.png         (printpreview/arrow-left-end-aero.png)
         skin/classic/aero/global/printpreview/arrow-right.png            (printpreview/arrow-right-aero.png)
         skin/classic/aero/global/printpreview/arrow-right-end.png        (printpreview/arrow-right-end-aero.png)
         skin/classic/aero/global/radio/radio-check.gif                   (radio/radio-check.gif)
         skin/classic/aero/global/radio/radio-check-dis.gif               (radio/radio-check-dis.gif)
         skin/classic/aero/global/scrollbar/slider.gif                    (scrollbar/slider.gif)
         skin/classic/aero/global/splitter/grip-bottom.gif                (splitter/grip-bottom.gif)
--- a/toolkit/themes/windows/mozapps/extensions/extensions.css
+++ b/toolkit/themes/windows/mozapps/extensions/extensions.css
@@ -1119,16 +1119,20 @@ menulist { /* Fixes some styling inconsi
 
 
 /*** buttons ***/
 
 .addon-control[disabled="true"] {
   display: none;
 }
 
+.addon-control.no-auto-hide {
+  display: block;
+}
+
 button.button-link {
   -moz-appearance: none;
   background: transparent;
   border: none;
   box-shadow: none;
   text-decoration: underline;
   color: #0066CC;
   cursor: pointer;