Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Sep 2013 22:30:22 -0400
changeset 159862 23f0185620af555fa91008c4882705fbf9285fa1
parent 159861 a84f8558ca61ff44c2cad7c69474f421d40e1b22 (current diff)
parent 159827 b9029b1de41096eebd6808f169464a5ee38215a4 (diff)
child 159863 ae16eceedbbcd4e98103599b18e60c6f7cfd41c2
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound.
b2g/config/otoro/config.json
b2g/config/otoro/releng-otoro.tt
b2g/config/panda/README
b2g/config/panda/config.json
b2g/config/panda/releng-pandaboard.tt
browser/base/content/browser.js
dom/camera/AudioParameter.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
mobile/android/base/Favicons.java
mobile/android/base/tests/testWebContentContextMenu.java.in
modules/libpref/src/init/all.js
toolkit/components/jsdownloads/test/unit/test_DownloadImport.js
tools/profiler/SQLiteInterposer.cpp
tools/profiler/SQLiteInterposer.h
tools/profiler/TableTicker.cpp
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -711,16 +711,19 @@ var CustomEventManager = {
         break;
       case 'inter-app-comm-permission':
         Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
           JSON.stringify({ callerID: detail.chromeEventID,
                            keyword: detail.keyword,
                            manifestURL: detail.manifestURL,
                            selectedApps: detail.peers }));
         break;
+      case 'inputmethod-update-layouts':
+        KeyboardHelper.handleEvent(detail);
+        break;
     }
   }
 }
 
 var AlertsHelper = {
   _listeners: {},
   _count: 0,
 
@@ -1063,16 +1066,24 @@ let RemoteDebugger = {
     try {
       DebuggerServer.closeListener();
     } catch (e) {
       dump('Unable to stop debugger server: ' + e + '\n');
     }
   }
 }
 
+let KeyboardHelper = {
+  handleEvent: function keyboard_handleEvent(aMessage) {
+    let data = aMessage.data;
+
+    Keyboard.setLayouts(data.layouts);
+  }
+};
+
 // This is the backend for Gaia's screenshot feature.  Gaia requests a
 // screenshot by sending a mozContentEvent with detail.type set to
 // 'take-screenshot'.  Then we take a screenshot and send a
 // mozChromeEvent with detail.type set to 'take-screenshot-success'
 // and detail.file set to the an image/png blob
 window.addEventListener('ContentStart', function ss_onContentStart() {
   let content = shell.contentBrowser.contentWindow;
   content.addEventListener('mozContentEvent', function ss_onMozContentEvent(e) {
@@ -1200,16 +1211,25 @@ window.addEventListener('ContentStart', 
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     shell.sendChromeEvent({
       type: 'audio-channel-changed',
       channel: aData
     });
 }, "audio-channel-changed", false);
 })();
 
+(function defaultVolumeChannelChangedTracker() {
+  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+    shell.sendChromeEvent({
+      type: 'default-volume-channel-changed',
+      channel: aData
+    });
+}, "default-volume-channel-changed", false);
+})();
+
 (function visibleAudioChannelChangedTracker() {
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     shell.sendChromeEvent({
       type: 'visible-audio-channel-changed',
       channel: aData
     });
     shell.visibleNormalAudioActive = (aData == 'normal');
 }, "visible-audio-channel-changed", false);
--- a/b2g/components/Keyboard.jsm
+++ b/b2g/components/Keyboard.jsm
@@ -233,21 +233,38 @@ let Keyboard = {
     this.sendAsyncMessage('Forms:GetText', msg.data);
   },
 
   sendKey: function keyboardSendKey(msg) {
     this.sendAsyncMessage('Forms:Input:SendKey', msg.data);
   },
 
   getContext: function keyboardGetContext(msg) {
+    if (this._layouts) {
+      ppmm.broadcastAsyncMessage('Keyboard:LayoutsChange', this._layouts);
+    }
+
     this.sendAsyncMessage('Forms:GetContext', msg.data);
   },
 
   setComposition: function keyboardSetComposition(msg) {
     this.sendAsyncMessage('Forms:SetComposition', msg.data);
   },
 
   endComposition: function keyboardEndComposition(msg) {
     this.sendAsyncMessage('Forms:EndComposition', msg.data);
+  },
+
+  /**
+   * Get the number of keyboard layouts active from keyboard_manager
+   */
+  _layouts: null,
+  setLayouts: function keyboardSetLayoutCount(layouts) {
+    // The input method plugins may not have loaded yet,
+    // cache the layouts so on init we can respond immediately instead
+    // of going back and forth between keyboard_manager
+    this._layouts = layouts;
+
+    ppmm.broadcastAsyncMessage('Keyboard:LayoutsChange', layouts);
   }
 };
 
 Keyboard.init();
--- a/b2g/components/MozKeyboard.js
+++ b/b2g/components/MozKeyboard.js
@@ -202,16 +202,18 @@ MozKeyboard.prototype = {
 /**
  * ==============================================
  * InputMethodManager
  * ==============================================
  */
 function MozInputMethodManager() { }
 
 MozInputMethodManager.prototype = {
+  _supportsSwitching: false,
+
   classID: Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIInputMethodManager
   ]),
 
   classInfo: XPCOMUtils.generateCI({
     "classID": Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
@@ -225,33 +227,34 @@ MozInputMethodManager.prototype = {
     cpmm.sendAsyncMessage('Keyboard:ShowInputMethodPicker', {});
   },
 
   next: function() {
     cpmm.sendAsyncMessage('Keyboard:SwitchToNextInputMethod', {});
   },
 
   supportsSwitching: function() {
-    return true;
+    return this._supportsSwitching;
   },
 
   hide: function() {
     cpmm.sendAsyncMessage('Keyboard:RemoveFocus', {});
   }
 };
 
 /**
  * ==============================================
  * InputMethod
  * ==============================================
  */
 function MozInputMethod() { }
 
 MozInputMethod.prototype = {
   _inputcontext: null,
+  _layouts: {},
 
   classID: Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIInputMethod,
     Ci.nsIDOMGlobalPropertyInitializer,
     Ci.nsIObserver
   ]),
@@ -279,28 +282,30 @@ MozInputMethod.prototype = {
     this.innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindowUtils)
                             .currentInnerWindowID;
 
     Services.obs.addObserver(this, "inner-window-destroyed", false);
     cpmm.addMessageListener('Keyboard:FocusChange', this);
     cpmm.addMessageListener('Keyboard:SelectionChange', this);
     cpmm.addMessageListener('Keyboard:GetContext:Result:OK', this);
+    cpmm.addMessageListener('Keyboard:LayoutsChange', this);
 
     // If there already is an active context, then this will trigger
     // a GetContext:Result:OK event, and we can initialize ourselves.
     // Otherwise silently ignored.
     cpmm.sendAsyncMessage("Keyboard:GetContext", {});
   },
 
   uninit: function mozInputMethodUninit() {
     Services.obs.removeObserver(this, "inner-window-destroyed");
     cpmm.removeMessageListener('Keyboard:FocusChange', this);
     cpmm.removeMessageListener('Keyboard:SelectionChange', this);
     cpmm.removeMessageListener('Keyboard:GetContext:Result:OK', this);
+    cpmm.removeMessageListener('Keyboard:LayoutsChange', this);
 
     this._window = null;
     this._mgmt = null;
   },
 
   receiveMessage: function mozInputMethodReceiveMsg(msg) {
     let json = msg.json;
 
@@ -315,16 +320,19 @@ MozInputMethod.prototype = {
         }
         break;
       case 'Keyboard:SelectionChange':
         this._inputcontext.updateSelectionContext(json);
         break;
       case 'Keyboard:GetContext:Result:OK':
         this.setInputContext(json);
         break;
+      case 'Keyboard:LayoutsChange':
+        this._layouts = json;
+        break;
     }
   },
 
   observe: function mozInputMethodObserve(subject, topic, data) {
     let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (wId == this.innerWindowID)
       this.uninit();
   },
@@ -344,19 +352,24 @@ MozInputMethod.prototype = {
   get oninputcontextchange() {
     return this.__DOM_IMPL__.getEventHandler("oninputcontextchange");
   },
 
   setInputContext: function mozKeyboardContextChange(data) {
     if (this._inputcontext) {
       this._inputcontext.destroy();
       this._inputcontext = null;
+      this._mgmt._supportsSwitching = false;
     }
 
     if (data) {
+      this._mgmt._supportsSwitching = this._layouts[data.type] ?
+        this._layouts[data.type] > 1 :
+        false;
+
       this._inputcontext = new MozInputContext(data);
       this._inputcontext.init(this._window);
     }
 
     let event = new this._window.Event("inputcontextchange",
                                        ObjectWrapper.wrap({}, this._window));
     this.__DOM_IMPL__.dispatchEvent(event);
   }
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "af6b88994605d8833693cde83e43fe3d9e93d181", 
+    "revision": "44b5d39b37b1e0e07e09953f52d176f687f15018", 
     "repo_path": "/integration/gaia-central"
 }
deleted file mode 100644
--- a/b2g/config/otoro/config.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-    "config_version": 1,
-    "tooltool_manifest": "releng-otoro.tt",
-    "mock_target": "mozilla-centos6-i386",
-    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "java-1.6.0-openjdk-devel"],
-    "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
-    "build_targets": [],
-    "upload_files": [
-        "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
-        "{workdir}/sources.xml"
-    ],
-    "zip_files": [
-        ["{workdir}/out/target/product/otoro/*.img", "out/target/product/otoro/"],
-        ["{workdir}/boot.img", "out/target/product/otoro/"],
-        "{workdir}/flash.sh",
-        "{workdir}/load-config.sh",
-        "{workdir}/.config",
-        "{workdir}/sources.xml"
-    ],
-    "env": {
-        "VARIANT": "user",
-        "MOZILLA_OFFICIAL": "1",
-        "B2GUPDATER": "1"
-    },
-    "gecko_l10n_root": "http://hg.mozilla.org/l10n-central",
-    "gaia": {
-        "l10n": {
-            "vcs": "hgtool",
-            "root": "http://hg.mozilla.org/gaia-l10n"
-        }
-    }
-}
deleted file mode 100644
--- a/b2g/config/otoro/releng-otoro.tt
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-{
-"size": 896371048,
-"digest": "73e89a23226726739c60b2e5be00da9d24d7ab8a39d60cf4c84a0ebf1d16a0cb1ff92440e72a6bdeef44cc7252a13d0bdd8a08770c858a5abce3f0bcea7a17ce",
-"algorithm": "sha512",
-"filename": "gonk.tar.xz"
-},
-{
-"size": 4139008,
-"digest": "6f65553e882316582b944e46c659915a1b907c4a326104cb31d81356330dddacba757e3eafbd282063da0e670c3c5d6b9a0905ab88da84b47848d810c37571cb",
-"algorithm": "sha512",
-"filename": "boot.img"
-}
-]
deleted file mode 100644
--- a/b2g/config/panda/README
+++ /dev/null
@@ -1,229 +0,0 @@
-All the parts of B2G written by Mozilla are free software, under the MPL 2 and
-other licenses.
-
-However, this directory contains system images for use with the PandaBoard, and
-these images require proprietary drivers to function. The full license terms
-for those drivers are included below. The "Authorized Android Enabled Device"
-is the PandaBoard. Your attention is particularly drawn to the fact that the
-license only gives permission for Imagination Technologies' software (the
-drivers) to be used on certain devices, and only non-commercially (section 2a).
-Alternative terms may be available from Imagination Technologies.
-
-THIS DEVELOPER SOFTWARE LICENSE AGREEMENT (THE "AGREEMENT") IS A LEGALLY
-BINDING AGREEMENT BETWEEN IMAGINATION TECHNOLOGIES LTD. ("LICENSOR") AND
-YOU OR THE LEGAL ENTITY YOU REPRESENT ("You" or its possessive, "Your"). BY
-TYPING "I ACCEPT" WHERE INDICATED YOU ACKNOWLEDGE THAT YOU HAVE READ THIS
-AGREEMENT, UNDERSTAND IT AND AGREE TO BE BOUND BY ITS TERMS AND CONDITIONS. IF
-YOU DO NOT AGREE TO THESE TERMS YOU MUST DISCONTINUE THE INSTALLATION PROCESS
-AND YOU SHALL NOT USE THE SOFTWARE OR RETAIN ANY COPIES OF THE SOFTWARE OR
-DOCUMENTATION. ANY USE OR POSSESSION OF THE SOFTWARE BY YOU IS SUBJECT TO THE
-TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT. IF THE SOFTWARE IS INSTALLED
-ON A COMPUTER OWNED BY A CORPORATION OR OTHER LEGAL ENTITY, THEN YOU REPRESENT
-AND WARRANT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY TO THE TERMS AND
-CONDITIONS OF THIS AGREEMENT.
-
-   1.  Special Definitions
-
-      a.  The term "Android" means the open source mobile platform, software
-          stack, operating system, middleware, application programming
-          interfaces and mobile applications under the trade-name "Android"
-          distributed at Android.com.
-
-      b.  The term "Android Applications" means a software application or
-          open-source contribution developed by You, designed to operate with
-          Android that does not contain or incorporate any of the Software.
-
-      c.  The term "Authorized Android Enabled Device" means only the device
-          identified on the site from which You downloaded the Software.
-          The term "Software" means the Licensor's proprietary software and
-          libraries in object code form, designed for use on the Authorized
-          Android Enabled Device.
-
-      d.  The term "Authorized Android Enabled Device Software" means a
-          packaged build for Authorized Android Enabled Devices, consisting of
-          files suitable for installation on an Authorized Android Enabled
-          Device using a mechanism such as fastboot mode or recovery mode.
-
-   2.  License Grant
-
-      a.  Subject to the terms of this Agreement, Licensor hereby grants to
-          You, free of charge, a non-exclusive, non-sublicensable,
-          non-transferable, limited license, during the term of this Agreement,
-          to download, install and use the Software internally in
-          machine-readable (i.e., object code) form and the Documentation for
-          non-commercial use on an Authorized Android Enabled Device and
-          non-commercial redistribution of the Authorized Android Enabled
-          Device Software (the "Limited Purpose"). You may grant your end users
-          the right to use the Software for the Limited Purpose. The license to
-          the Software granted to You hereunder is solely for the Limited
-          Purpose set forth in this section, and the Software shall not be used
-          for any other purpose.
-
-   3.  Restrictions
-
-      a.  Retention of Rights. The entire right, title and interest in the
-          Software shall remain with Licensor and, unless specified in writing
-          hereunder, no rights are granted to any of the Software. Except for
-          the right to use the Software for the Limited Purpose, the delivery
-          of the Software to You does not convey to You any intellectual
-          property rights in the Software, including, but not limited to any
-          rights under any patent, trademark, copyright, or trade secret.
-          Neither the delivery of the Software to You nor any terms set forth
-          herein shall be construed to grant to You, either expressly, by
-          implication or by way of estoppel, any license under any patents or
-          other intellectual property rights covering or relating to any other
-          product or invention or any combination of the Software with any
-          other product. Any rights not expressly granted to You herein are
-          reserved by Licensor.
-
-      b.  No Commercialization or Distribution of the Software and
-          Documentation. Except as expressly provided in Section 2 of this
-          Agreement, You shall have no right to (i) copy, disclose, distribute,
-          publically perform, publically display, transfer, alter, modify,
-          translate, disassemble, decompile, reverse engineer, or adapt the
-          Software and Documentation, or any portion thereof, or create any
-          derivative works based thereon; (ii) rent, lease, assign, sublicense,
-          resell, disclose or otherwise transfer the Software and Documentation
-          in whole or in part to any third party (iii) use the Software and
-          Documentation except for the Limited Purpose, (iv) remove or alter
-          any of the copyright or proprietary notices contained in any of the
-          Software and Documentation. For the purposes of clarity, nothing in
-          this Agreement prohibits You from making and distributing Android
-          Applications under commercial or non-commercial terms, provided that
-          You shall not contain, incorporate, and/or compile the Software or
-          any of its derivative works, in whole or in part, into Your Android
-          Applications and/or any software/devices created by You or by third
-          parties acting on Your behalf. You and any such third party shall
-          comply with all of the terms and conditions of this Agreement.
-
-      c.  No Reverse Engineering. Except for any portions of the Software
-          provided to You in source code format and except for any third party
-          code distributed with the Software that is licensed under contrary
-          terms, You will not reverse engineer, disassemble,
-          decompile, or translate the Software, or otherwise attempt to derive
-          the source code version of the Software, except if and to the extent
-          expressly permitted under any applicable law.
-
-      d.  Third Party Software. You agree that Android may contain third party
-          software. You agree that you may not distribute such third party
-          software for any purpose without appropriate licenses from the
-          applicable third party or parties.
-
-      e.  No Transfer or Assignment. You shall not assign any of its rights or
-          obligations under this Agreement. Any attempted assignment in
-          contravention of this Section shall be void.
-
-   4.  Indemnity
-
-      a.  You agree to indemnify and hold harmless Licensor and its officers,
-          directors, customers, employees and successors and assigns (each an
-          "Indemnified Party") against any and all claims, demands, causes of
-          action, losses, liabilities, damages, costs and expenses, incurred by
-          the Indemnified Party (including but not limited to costs of defense,
-          investigation and reasonable attorney's fees) arising out of,
-          resulting from or related to (i) any software, products,
-          documentation, content, materials or derivative works created or
-          developed by You using the Software which causes an infringement of
-          any patent, copyright, trademark, trade secret, or other property,
-          publicity or privacy rights of any third parties arising in any
-          jurisdiction anywhere in the world, (ii) the download, distribution,
-          installation, storage, execution, use or transfer of such software,
-          products, documentation, content, materials or derivative works by
-          any person or entity, and/or (iii) any breach of this Agreement by
-          You. If requested by an Indemnified Party, You agree to defend such
-          Indemnified Party in connection with any third party claims, demands,
-          or causes of action resulting from, arising out of or in connection
-          with any of the foregoing.
-
-   5.  Limitation of Liability
-
-      a.  TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAWS, UNDER NO
-          CIRCUMSTANCES, INCLUDING WITHOUT LIMITATION NEGLIGENCE, SHALL
-          LICENSOR, ITS AFFILIATES AND/OR ITS DIRECTORS, OFFICERS, EMPLOYEES OR
-          AGENTS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE OR
-          CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS
-          OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
-          INFORMATION AND THE LIKE) ARISING OUT OF OR IN CONNECTION WITH THE
-          SOFTWARE OR ANY DOWNLOAD, INSTALLATION OR USE OF, OR INABILITY TO
-          USE, THE SOFTWARE, EVEN IF LICENSOR HAS BEEN ADVISED OF THE
-          POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW THE
-          LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL
-          DAMAGES SO THE ABOVE LIMITATION OR EXCLUSION MAY NOT APPLY OR MAY BE
-          LIMITED. IN NO EVENT SHALL LICENSOR'S TOTAL AGGREGATE LIABILITY TO
-          YOU FOR ANY AND ALL DAMAGES, LOSSES, CLAIMS AND CAUSES OF ACTIONS
-          (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, INDEMNIFICATION OR
-          OTHERWISE) EXCEED ONE HUNDRED U.S. DOLLARS (US00). THE LIMITATIONS
-          SET FORTH IN THIS PARAGRAPH SHALL BE DEEMED TO APPLY TO THE MAXIMUM
-          EXTENT PERMITTED BY APPLICABLE LAW. THE PARTIES HAVE FULLY CONSIDERED
-          THE FOREGOING ALLOCATION OF RISK AND FIND IT REASONABLE, AND THAT THE
-          FOREGOING LIMITATIONS IN THIS PARAGRAPH ARE AN ESSENTIAL BASIS OF
-          THE BARGAIN BETWEEN THE PARTIES.
-
-   6.  No Warranty
-
-      a.  LICENSOR MAKES NO WARRANTIES, EXPRESS OR IMPLIED, WITH RESPECT TO THE
-          SOFTWARE AND DOCUMENTATION PROVIDED UNDER THIS AGREEMENT, INCLUDING
-          BUT NOT LIMITED TO ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A
-          PARTICULAR PURPOSE OR AGAINST INFRINGEMENT, OR ANY EXPRESS OR IMPLIED
-          WARRANTY ARISING OUT OF TRADE USAGE OR OUT OF A COURSE OF DEALING OR
-          COURSE OF PERFORMANCE. NOTHING CONTAINED IN THIS AGREEMENT SHALL BE
-          CONSTRUED AS A WARRANTY OR REPRESENTATION BY LICENSOR (I) AS TO THE
-          VALIDITY OR SCOPE OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL
-          PROPERTY RIGHT AND (II) THAT ANY MANUFACTURE OR USE WILL BE FREE FROM
-          INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL PROPERTY
-          RIGHTS OF OTHERS, AND IT SHALL BE THE SOLE RESPONSIBILITY OF YOU TO
-          MAKE SUCH DETERMINATION AS IS NECESSARY WITH RESPECT TO THE
-          ACQUISITION OF LICENSES UNDER PATENTS AND OTHER INTELLECTUAL PROPERTY
-          OF THIRD PARTIES. LICENSOR SHALL NOT HAVE ANY OBLIGATION TO PROVIDE
-          ANY TECHNICAL SUPPORT OF THE SOFTWARE UNDER THIS AGREEMENT.
-
-   7.  Term and Termination
-
-      a.  This Agreement shall be effective on the date You accept this
-          Agreement and shall remain in effect until terminated as provided
-          herein. You may terminate the Agreement at any time by deleting and
-          destroying all copies of the Software and all related information in
-          Your possession or control. This Agreement terminates immediately and
-          automatically, with or without notice, if You fail to comply with any
-          provision hereof.  Additionally, Licensor may at any time terminate
-          this Agreement, without cause, upon notice to You. Upon termination
-          You must delete or destroy all copies of the Software in Your
-          possession, and the license granted to You in this Agreement shall
-          terminate. Sections 3, 4, 5, 6 and 8 shall survive the termination of
-          this Agreement.
-
-   8.  Miscellaneous
-
-      a.  Governing Law. This Agreement is governed and interpreted in
-          accordance with the laws of the State of California without giving
-          effect to its conflict of laws provisions. The United Nations
-          Convention on Contracts for the International Sale of Goods is
-          expressly disclaimed and shall not apply. Any claim arising out of or
-          related to this Agreement must be brought exclusively in a federal or
-          state court located in Santa Clara County, California and You consent
-          to the jurisdiction and venue of such courts.
-
-      b.  Waiver and Severability. The failure of either party to require
-          performance by the other party of any provision of this Agreement
-          shall not affect the full right to require such performance at any
-          time thereafter; nor shall the waiver by either party of a breach of
-          any provision of this Agreement be taken or held to be a waiver of
-          the provision itself. Severability. If any provision of this
-          Agreement is unenforceable or invalid under any applicable law or is
-          so held by applicable court decision, such unenforceability or
-          invalidity shall not render this Agreement unenforceable or invalid
-          as a whole, and such provision shall be changed and interpreted so as
-          to best accomplish the objectives of such unenforceable or invalid
-          provision within the limits of applicable law or applicable court
-          decisions.
-
-      c.  Amendment and Modification. This Agreement and any of its terms and
-          provisions may only be amended, modified, supplemented or waived in a
-          writing signed by both parties hereto.
-
-      d.  Compliance with Laws. You shall comply with all applicable laws,
-          rules, and regulations in connection with its activities under this
-          Agreement.
-
-      e.  Entire Agreement. This Agreement completely and exclusively states
-          the agreement between You and Licensor regarding this subject matter.
deleted file mode 100644
--- a/b2g/config/panda/config.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "config_version": 2,
-    "tooltool_manifest": "releng-pandaboard.tt",
-    "mock_target": "mozilla-centos6-i386",
-    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "git"],
-    "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
-    "build_targets": ["boottarball", "systemtarball", "userdatatarball", "package-tests"],
-    "upload_files": [
-        "{workdir}/out/target/product/panda/*.tar.bz2",
-        "{workdir}/out/target/product/panda/tests/*.zip",
-        "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
-        "{srcdir}/b2g/config/panda/README",
-        "{workdir}/sources.xml"
-    ],
-    "b2g_manifest": "pandaboard.xml",
-    "gecko_l10n_root": "http://hg.mozilla.org/l10n-central",
-    "gaia": {
-        "l10n": {
-            "vcs": "hgtool",
-            "root": "http://hg.mozilla.org/gaia-l10n"
-        }
-    }
-}
deleted file mode 100644
--- a/b2g/config/panda/releng-pandaboard.tt
+++ /dev/null
@@ -1,8 +0,0 @@
-[
-{
-"size": 2116507,
-"digest": "be67a012963a5c162834f9fcb989bcebd2d047dcb4e17ee23031b694dcf7cdfd6d7a6545d7a1f5e7293b6d24415403972f4ea1ab8c6c78fefcabfaf3f6875214",
-"algorithm": "sha512",
-"filename": "download-panda.tar.bz2"
-}
-]
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -347,16 +347,20 @@ pref("browser.download.animateNotificati
 
 // This records whether or not the panel has been shown at least once.
 pref("browser.download.panel.shown", false);
 
 // This records whether or not at least one session with the Downloads Panel
 // enabled has been completed already.
 pref("browser.download.panel.firstSessionCompleted", false);
 
+#ifndef XP_MACOSX
+pref("browser.helperApps.deleteTempFileOnExit", true);
+#endif
+
 // search engines URL
 pref("browser.search.searchEnginesURL",      "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
 
 // pointer to the default engine name
 pref("browser.search.defaultenginename",      "chrome://browser-region/locale/region.properties");
 
 // disable logging for the search service by default
 pref("browser.search.log", false);
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -97,17 +97,17 @@ let gBrowserThumbnails = {
                                                    aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
       this._delayedCapture(aBrowser);
   },
 
   _capture: function Thumbnails_capture(aBrowser) {
     if (this._shouldCapture(aBrowser))
-      PageThumbs.captureAndStore(aBrowser);
+      PageThumbs.captureAndStoreIfStale(aBrowser);
   },
 
   _delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
     if (this._timeouts.has(aBrowser))
       clearTimeout(this._timeouts.get(aBrowser));
     else
       aBrowser.addEventListener("scroll", this, true);
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -539,17 +539,16 @@ browser[tabmodalPromptShowing] {
 }
 
 /* Status panel */
 
 statuspanel {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel");
   position: fixed;
   margin-top: -3em;
-  left: 0;
   max-width: calc(100% - 5px);
   pointer-events: none;
 }
 
 statuspanel:-moz-locale-dir(ltr)[mirror],
 statuspanel:-moz-locale-dir(rtl):not([mirror]) {
   left: auto;
   right: 0;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -170,16 +170,20 @@ let gInitialPages = [
 #endif
 
 #ifdef MOZ_SERVICES_SYNC
 #include browser-syncui.js
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
 #ifdef XP_WIN
+  // Bug 666808 - AeroPeek support for e10s
+  if (gMultiProcessBrowser)
+    return null;
+
   const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
   if (WINTASKBAR_CONTRACTID in Cc &&
       Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
     let AeroPeek = Cu.import("resource:///modules/WindowsPreviewPerTab.jsm", {}).AeroPeek;
     return {
       onOpenWindow: function () {
         AeroPeek.onOpenWindow(window);
       },
@@ -1140,21 +1144,18 @@ var gBrowserInit = {
     placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
     placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
 #endif
 
     gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
     gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
     gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
 
-    // Bug 666808 - AeroPeek support for e10s
-    if (!gMultiProcessBrowser) {
-      if (Win7Features)
-        Win7Features.onOpenWindow();
-    }
+    if (Win7Features)
+      Win7Features.onOpenWindow();
 
    // called when we go into full screen, even if initiated by a web page script
     window.addEventListener("fullscreen", onFullScreen, true);
 
     // Called when we enter DOM full-screen mode. Note we can already be in browser
     // full-screen mode when we enter DOM full-screen mode.
     window.addEventListener("MozEnteredDomFullscreen", onMozEnteredDomFullscreen, true);
 
@@ -3666,18 +3667,17 @@ var XULBrowserWindow = {
     delete this.stopCommand;
     return this.stopCommand = document.getElementById("Browser:Stop");
   },
   get reloadCommand () {
     delete this.reloadCommand;
     return this.reloadCommand = document.getElementById("Browser:Reload");
   },
   get statusTextField () {
-    delete this.statusTextField;
-    return this.statusTextField = document.getElementById("statusbar-display");
+    return gBrowser.getStatusPanel();
   },
   get isImage () {
     delete this.isImage;
     return this.isImage = document.getElementById("isImage");
   },
 
   init: function () {
     this.throbberElement = document.getElementById("navigator-throbber");
@@ -3687,17 +3687,16 @@ var XULBrowserWindow = {
     this.onSecurityChange(null, null, securityUI.state);
   },
 
   destroy: function () {
     // XXXjag to avoid leaks :-/, see bug 60729
     delete this.throbberElement;
     delete this.stopCommand;
     delete this.reloadCommand;
-    delete this.statusTextField;
     delete this.statusText;
   },
 
   setJSStatus: function () {
     // unsupported
   },
 
   setDefaultStatus: function (status) {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1021,17 +1021,16 @@
     <vbox id="appcontent" flex="1">
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   selectpopup="ContentSelectDropdown"/>
       <chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
-      <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
     <splitter id="social-sidebar-splitter"
               class="chromeclass-extrachrome sidebar-splitter"
               observes="socialSidebarBroadcaster"/>
     <vbox id="social-sidebar-box"
           class="chromeclass-extrachrome"
           observes="socialSidebarBroadcaster"
           persist="width">
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -129,17 +129,17 @@ Site.prototype = {
     link.setAttribute("href", url);
     this._querySelector(".newtab-title").textContent = title;
 
     if (this.isPinned())
       this._updateAttributes(true);
 #ifndef RELEASE_BUILD
     // request a staleness check for the thumbnail, which will cause page.js
     // to be notified and call our refreshThumbnail() method.
-    BackgroundPageThumbs.captureIfStale(this.url);
+    BackgroundPageThumbs.captureIfMissing(this.url);
     // but still display whatever thumbnail might be available now.
 #endif
     this.refreshThumbnail();
   },
 
   /**
    * Refreshes the thumbnail for the site.
    */
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -317,21 +317,18 @@ Sanitizer.prototype = {
               // Convert microseconds back to milliseconds for date comparisons.
               let rangeBeginMs = this.range[0] / 1000;
               let rangeEndMs = this.range[1] / 1000;
               filterByTime = download => download.startTime >= rangeBeginMs &&
                                          download.startTime <= rangeEndMs;
             }
 
             // Clear all completed/cancelled downloads
-            let publicList = yield Downloads.getPublicDownloadList();
-            publicList.removeFinished(filterByTime);
-
-            let privateList = yield Downloads.getPrivateDownloadList();
-            privateList.removeFinished(filterByTime);
+            let list = yield Downloads.getList(Downloads.ALL);
+            list.removeFinished(filterByTime);
           }.bind(this)).then(null, Components.utils.reportError);
         }
         else {
           var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                                 .getService(Components.interfaces.nsIDownloadManager);
 
           if (this.range) {
             // First, remove the completed/cancelled downloads
--- a/browser/base/content/socialmarks.xml
+++ b/browser/base/content/socialmarks.xml
@@ -193,21 +193,22 @@
       <handler event="popupshown"><![CDATA[
         // because the panel may be preloaded, we need to size the panel when
         // showing as well as after load
         let sizeSocialPanelToContent = Cu.import("resource:///modules/Social.jsm", {}).sizeSocialPanelToContent;
         if (!this._loading && this.contentDocument.readyState == "complete") {
           this.dispatchPanelEvent("socialFrameShow");
           sizeSocialPanelToContent(this.panel, this.content);
         } else {
-          this.content.addEventListener("load", function panelBrowserOnload(e) {
+          let panelBrowserOnload = () => {
             this.content.removeEventListener("load", panelBrowserOnload, true);
             this.dispatchPanelEvent("socialFrameShow");
             sizeSocialPanelToContent(this.panel, this.content);
-          }.bind(this), true);
+          };
+          this.content.addEventListener("load", panelBrowserOnload, true);
         }
       ]]></handler>
       <handler event="popuphidden"><![CDATA[
         this.dispatchPanelEvent("socialFrameHide");
       ]]></handler>
       <handler event="command"><![CDATA[
         this.markCurrentPage();
       ]]></handler>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -177,16 +177,37 @@
           let event = document.createEvent("Events");
           event.initEvent("TabFindInitialized", true, false);
           aTab.dispatchEvent(event);
 
           return findBar;
         ]]></body>
       </method>
 
+      <method name="getStatusPanel">
+        <body><![CDATA[
+          if (!this._statusPanel) {
+            this._statusPanel = document.createElementNS(this.namespaceURI, "statuspanel");
+            this._statusPanel.setAttribute("inactive", "true");
+            this._appendStatusPanel();
+          }
+          return this._statusPanel;
+        ]]></body>
+      </method>
+
+      <method name="_appendStatusPanel">
+        <body><![CDATA[
+          if (this._statusPanel) {
+            let browser = this.selectedBrowser;
+            let browserContainer = this.getBrowserContainer(browser);
+            browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling);
+          }
+        ]]></body>
+      </method>
+
       <method name="updateWindowResizers">
         <body><![CDATA[
           if (!window.gShowPageResizers)
             return;
 
           var show = document.getElementById("addon-bar").collapsed &&
                      window.windowState == window.STATE_NORMAL;
           for (let i = 0; i < this.browsers.length; i++) {
@@ -983,16 +1004,18 @@
             if (backForwardContainer) {
               backForwardContainer.setAttribute("switchingtabs", "true");
               window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
                 window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
                 backForwardContainer.removeAttribute("switchingtabs");
               });
             }
 
+            this._appendStatusPanel();
+
             if (updatePageReport)
               this.mCurrentBrowser.updatePageReport();
 
             // Update the URL bar.
             var loc = this.mCurrentBrowser.currentURI;
 
             // Bug 666809 - SecurityUI support for e10s
             var webProgress = this.mCurrentBrowser.webProgress;
--- a/browser/base/content/test/browser_sanitize-timespans.js
+++ b/browser/base/content/test/browser_sanitize-timespans.js
@@ -91,17 +91,17 @@ function onHistoryReady() {
   itemPrefs.setBoolPref("cache", false);
   itemPrefs.setBoolPref("cookies", false);
   itemPrefs.setBoolPref("formdata", true);
   itemPrefs.setBoolPref("offlineApps", false);
   itemPrefs.setBoolPref("passwords", false);
   itemPrefs.setBoolPref("sessions", false);
   itemPrefs.setBoolPref("siteSettings", false);
 
-  let publicList = yield Downloads.getPublicDownloadList();
+  let publicList = yield Downloads.getList(Downloads.PUBLIC);
   let downloadPromise = promiseDownloadRemoved(publicList);
 
   // Clear 10 minutes ago
   s.range = [now_uSec - 10*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
   yield promiseFormHistoryRemoved();
@@ -607,17 +607,17 @@ function setupFormHistory() {
   yield countEntries("4hour10minutes", "Checking for 4hour10minutes form history entry creation", checkOne);
   yield countEntries("today", "Checking for today form history entry creation", checkOne);
   yield countEntries("b4today", "Checking for b4today form history entry creation", checkOne);
   is(checks, 9, "9 checks made");
 }
 
 function setupDownloads() {
 
-  let publicList = yield Downloads.getPublicDownloadList();
+  let publicList = yield Downloads.getList(Downloads.PUBLIC);
 
   let download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target: "fakefile-10-minutes"
   });
   download.startTime = new Date(now_mSec - 10 * kMsecPerMin), // 10 minutes ago
   download.canceled = true;
   publicList.add(download);
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -907,17 +907,17 @@ WindowHelper.prototype = {
 
 /**
  * Adds a download to history.
  *
  * @param aMinutesAgo
  *        The download will be downloaded this many minutes ago
  */
 function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) {
-  let publicList = yield Downloads.getPublicDownloadList();
+  let publicList = yield Downloads.getList(Downloads.PUBLIC);
 
   let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
   let download = yield Downloads.createDownload({
     source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
     target: name
   });
   download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin));
   download.canceled = true;
@@ -979,17 +979,17 @@ function formNameExists(name)
 function blankSlate() {
   PlacesUtils.bhistory.removeAllPages();
 
   // The promise is resolved only when removing both downloads and form history are done.
   let deferred = Promise.defer();
   let formHistoryDone = false, downloadsDone = false;
 
   Task.spawn(function deleteAllDownloads() {
-    let publicList = yield Downloads.getPublicDownloadList();
+    let publicList = yield Downloads.getList(Downloads.PUBLIC);
     let downloads = yield publicList.getAll();
     for (let download of downloads) {
       publicList.remove(download);
       yield download.finalize(true);
     }
     downloadsDone = true;
     if (formHistoryDone) {
       deferred.resolve();
@@ -1032,17 +1032,17 @@ function boolPrefIs(aPrefName, aExpected
  *
  * @param  aPath
  *         The path of the download to check
  * @return True if the download exists, false otherwise
  */
 function downloadExists(aPath)
 {
   return Task.spawn(function() {
-    let publicList = yield Downloads.getPublicDownloadList();
+    let publicList = yield Downloads.getList(Downloads.PUBLIC);
     let listArray = yield publicList.getAll();
     throw new Task.Result(listArray.some(i => i.target.path == aPath));
   });
 }
 
 /**
  * Runs the next test in the gAllTests array.  If all tests have been run,
  * finishes the entire suite.
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -635,18 +635,18 @@ DownloadsDataCtor.prototype = {
    *        called, and we must ensure to register our listeners before the
    *        getService call for the Download Manager returns.
    */
   initializeDataLink: function DD_initializeDataLink(aDownloadManagerService)
   {
     // Start receiving real-time events.
     if (DownloadsCommon.useJSTransfer) {
       if (!this._dataLinkInitialized) {
-        let promiseList = this._isPrivate ? Downloads.getPrivateDownloadList()
-                                          : Downloads.getPublicDownloadList();
+        let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE
+                                                            : Downloads.PUBLIC);
         promiseList.then(list => list.addView(this)).then(null, Cu.reportError);
         this._dataLinkInitialized = true;
       }
     } else {
       aDownloadManagerService.addPrivacyAwareListener(this);
       Services.obs.addObserver(this, "download-manager-remove-download-guid",
                                false);
     }
@@ -692,18 +692,18 @@ DownloadsDataCtor.prototype = {
   },
 
   /**
    * Asks the back-end to remove finished downloads from the list.
    */
   removeFinished: function DD_removeFinished()
   {
     if (DownloadsCommon.useJSTransfer) {
-      let promiseList = this._isPrivate ? Downloads.getPrivateDownloadList()
-                                        : Downloads.getPublicDownloadList();
+      let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE
+                                                          : Downloads.PUBLIC);
       promiseList.then(list => list.removeFinished())
                  .then(null, Cu.reportError);
     } else {
       if (this._isPrivate) {
         Services.downloads.cleanUpPrivate();
       } else {
         Services.downloads.cleanUp();
       }
@@ -1710,22 +1710,20 @@ DownloadsDataItem.prototype = {
     }.bind(this));
   },
 
   /**
    * Remove the download.
    */
   remove: function DDI_remove() {
     if (DownloadsCommon.useJSTransfer) {
-      let promiseList = this._download.source.isPrivate
-                          ? Downloads.getPrivateDownloadList()
-                          : Downloads.getPublicDownloadList();
-      promiseList.then(list => list.remove(this._download))
-                 .then(() => this._download.finalize(true))
-                 .then(null, Cu.reportError);
+      Downloads.getList(Downloads.ALL)
+               .then(list => list.remove(this._download))
+               .then(() => this._download.finalize(true))
+               .then(null, Cu.reportError);
       return;
     }
 
     this.getDownload(function (aDownload) {
       if (this.inProgress) {
         aDownload.cancel();
         this._ensureLocalFileRemoved();
       }
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -67,33 +67,33 @@ function promisePanelOpened()
   };
 
   return deferred.promise;
 }
 
 function task_resetState()
 {
   // Remove all downloads.
-  let publicList = yield Downloads.getPublicDownloadList();
+  let publicList = yield Downloads.getList(Downloads.PUBLIC);
   let downloads = yield publicList.getAll();
   for (let download of downloads) {
     publicList.remove(download);
     yield download.finalize(true);
   }
 
   DownloadsPanel.hidePanel();
 
   yield promiseFocus();
 }
 
 function task_addDownloads(aItems)
 {
   let startTimeMs = Date.now();
 
-  let publicList = yield Downloads.getPublicDownloadList();
+  let publicList = yield Downloads.getList(Downloads.PUBLIC);
   for (let item of aItems) {
     publicList.add(yield Downloads.createDownload({
       source: "http://www.example.com/test-download.txt",
       target: gTestTargetFile,
       succeeded: item.state == nsIDM.DOWNLOAD_FINISHED,
       canceled: item.state == nsIDM.DOWNLOAD_CANCELED ||
                 item.state == nsIDM.DOWNLOAD_PAUSED,
       error: item.state == nsIDM.DOWNLOAD_FAILED ? new Error("Failed.") : null,
--- a/browser/devtools/app-manager/content/index.xul
+++ b/browser/devtools/app-manager/content/index.xul
@@ -18,18 +18,18 @@
         fullscreenbutton="true"
         screenX="4" screenY="4"
         width="800" height="600"
         persist="screenX screenY width height sizemode">
 
   <vbox flex="1">
     <hbox id="content" flex="1">
       <vbox id="tabs">
-        <button class="button projects-button" onclick="selectTab('projects')">&index.projects;</button>
-        <button class="button device-button" onclick="selectTab('device')">&index.device;</button>
+        <button class="button projects-button" onclick="selectTab('projects')">&index.projects2;</button>
+        <button class="button device-button" onclick="selectTab('device')">&index.device2;</button>
       </vbox>
       <hbox id="tab-panels" flex="1">
         <iframe flex="1" class="panel projects-panel" src="chrome://browser/content/devtools/app-manager/projects.xhtml"/>
         <iframe flex="1" class="panel device-panel" src="chrome://browser/content/devtools/app-manager/device.xhtml"/>
       </hbox>
     </hbox>
     <iframe id="connection-footer" src="chrome://browser/content/devtools/app-manager/connection-footer.xhtml"></iframe>
   </vbox>
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -663,17 +663,17 @@ let StackFrameUtils = {
    * to display in the stackframes container.
    *
    * @param object aFrame
    *        The stack frame to label.
    */
   getFrameTitle: function(aFrame) {
     if (aFrame.type == "call") {
       let c = aFrame.callee;
-      return (c.name || c.userDisplayName || c.displayName || "(anonymous)");
+      return (c.userDisplayName || c.displayName || c.name || "(anonymous)");
     }
     return "(" + aFrame.type + ")";
   },
 
   /**
    * Constructs a scope label based on its environment.
    *
    * @param object aEnv
@@ -697,17 +697,17 @@ let StackFrameUtils = {
     switch (aEnv.type) {
       case "with":
       case "object":
         label += " [" + aEnv.object.class + "]";
         break;
       case "function":
         let f = aEnv.function;
         label += " [" +
-          (f.name || f.userDisplayName || f.displayName || "(anonymous)") +
+          (f.userDisplayName || f.displayName || f.name || "(anonymous)") +
         "]";
         break;
     }
     return label;
   }
 };
 
 /**
--- a/browser/devtools/inspector/test/browser_inspector_scrolling.js
+++ b/browser/devtools/inspector/test/browser_inspector_scrolling.js
@@ -38,16 +38,18 @@ function inspectNode(aInspector)
     inspector.highlighter.unlock();
     inspector.selection.setNode(div, "");
   });
 }
 
 function performScrollingTest()
 {
   executeSoon(function() {
+    // FIXME: this will fail on retina displays. EventUtils will only scroll
+    // 25px down instead of 50.
     EventUtils.synthesizeWheel(div, 10, 10,
       { deltaY: 50.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL },
       iframe.contentWindow);
   });
 
   gBrowser.selectedBrowser.addEventListener("scroll", function() {
     gBrowser.selectedBrowser.removeEventListener("scroll", arguments.callee,
       false);
--- a/browser/devtools/markupview/markup-view.css
+++ b/browser/devtools/markupview/markup-view.css
@@ -1,47 +1,84 @@
 /* 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/. */
 
-ul {
+#root-wrapper {
+  overflow: hidden;
+  min-width: 250px;
+}
+
+.children {
   list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.child {
+  margin-left: -1000em;
+  padding-left: 1001em;
 }
 
-ul.children:not([expanded]) {
+.tag-line {
+  min-height: 1.4em;
+  line-height: 1.4em;
+  position: relative;
+}
+
+/* Children are indented thanks to their parent's left padding, that means they
+ * are not stretching from edge to edge, which is what we want.
+ * So we insert a pseudo-element and make sure it covers the whole "line" */
+.tag-line .highlighter {
+    content: "";
+    position: absolute;
+    left: -1000em;
+    right: 0;
+    height: 100%;
+    z-index: -1;
+}
+
+.expander {
+  display: inline-block;
+  margin-left: -14px;
+  vertical-align: middle;
+}
+
+.child.collapsed .child {
   display: none;
 }
 
-.codebox {
-  display: inline-block;
+.child > .tag-line:first-child .close {
+  display: none;
+}
+
+.child.collapsed > .tag-line:first-child .close {
+  display: inline;
+}
+
+.child.collapsed > .tag-line ~ .tag-line {
+  display: none;
+}
+
+.child.collapsed .close {
+  display: inline;
 }
 
 .newattr {
   display: inline-block;
   width: 1em;
   height: 1ex;
   margin-right: -1em;
+  padding: 1px 0;
 }
 
 .newattr:focus {
   margin-right: 0;
 }
 
-.closing-bracket {
-  pointer-events: none;
-}
-
-.summary {
-  cursor: pointer;
-}
-
-.summary[expanded] {
-  display: none;
-}
-
 /* Preview */
 
 #previewbar {
   position: fixed;
   top: 0;
   right: 0;
   width: 90px;
   background: black;
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -3,29 +3,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {Cc, Cu, Ci} = require("chrome");
 
 // Page size for pageup/pagedown
 const PAGE_SIZE = 10;
-
 const PREVIEW_AREA = 700;
 const DEFAULT_MAX_CHILDREN = 100;
 
-let {UndoStack} = require("devtools/shared/undo");
-let EventEmitter = require("devtools/shared/event-emitter");
-let {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
-let promise = require("sdk/core/promise");
+const {UndoStack} = require("devtools/shared/undo");
+const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
+const promise = require("sdk/core/promise");
 
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyGetter(this, "DOMParser", function() {
  return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
 });
 loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
 
 /**
  * Vocabulary for the purposes of this file:
@@ -187,17 +184,18 @@ MarkupView.prototype = {
         } else {
           let parent = this._selectionWalker().parentNode();
           if (parent) {
             this.navigate(parent.container);
           }
         }
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT:
-        if (!this._selectedContainer.expanded) {
+        if (!this._selectedContainer.expanded &&
+            this._selectedContainer.hasChildren) {
           this._expandContainer(this._selectedContainer);
         } else {
           let next = this._selectionWalker().nextNode();
           if (next) {
             this.navigate(next.container);
           }
         }
         break;
@@ -360,17 +358,17 @@ MarkupView.prototype = {
   {
     for (let mutation of aMutations) {
       let type = mutation.type;
       let target = mutation.target;
 
       if (mutation.type === "documentUnload") {
         // Treat this as a childList change of the child (maybe the protocol
         // should do this).
-        type = "childList"
+        type = "childList";
         target = mutation.targetParent;
         if (!target) {
           continue;
         }
       }
 
       let container = this._containers.get(target);
       if (!container) {
@@ -385,17 +383,16 @@ MarkupView.prototype = {
         this._updateChildren(container);
       }
     }
     this._waitForChildren().then(() => {
       this._inspector.emit("markupmutation");
     });
   },
 
-
   /**
    * Make sure the given node's parents are expanded and the
    * node is scrolled on to screen.
    */
   showNode: function MT_showNode(aNode, centered)
   {
     let container = this.importNode(aNode);
     let parent = aNode;
@@ -414,17 +411,17 @@ MarkupView.prototype = {
 
   /**
    * Expand the container's children.
    */
   _expandContainer: function MT__expandContainer(aContainer)
   {
     return this._updateChildren(aContainer, true).then(() => {
       aContainer.expanded = true;
-    })
+    });
   },
 
   /**
    * Expand the node's children.
    */
   expandNode: function MT_expandNode(aNode)
   {
     let container = this._containers.get(aNode);
@@ -802,90 +799,83 @@ MarkupView.prototype = {
     let win = this._frame.contentWindow;
     this._previewBar.classList.add("hide");
     win.clearTimeout(this._resizePreviewTimeout);
 
     win.setTimeout(function() {
       this._updatePreview();
       this._previewBar.classList.remove("hide");
     }.bind(this), 1000);
-  },
-
+  }
 };
 
 
 /**
  * The main structure for storing a document node in the markup
  * tree.  Manages creation of the editor for the node and
  * a <ul> for placing child elements, and expansion/collapsing
  * of the element.
- *
+ * 
  * @param MarkupView aMarkupView
  *        The markup view that owns this container.
  * @param DOMNode aNode
  *        The node to display.
  */
-function MarkupContainer(aMarkupView, aNode)
-{
+function MarkupContainer(aMarkupView, aNode) {
   this.markup = aMarkupView;
   this.doc = this.markup.doc;
   this.undo = this.markup.undo;
   this.node = aNode;
 
   if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
     this.editor = new TextEditor(this, aNode, "text");
   } else if (aNode.nodeType == Ci.nsIDOMNode.COMMENT_NODE) {
     this.editor = new TextEditor(this, aNode, "comment");
   } else if (aNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
     this.editor = new ElementEditor(this, aNode);
   } else if (aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
     this.editor = new DoctypeEditor(this, aNode);
   } else {
-    this.editor = new GenericEditor(this.markup, aNode);
+    this.editor = new GenericEditor(this, aNode);
   }
 
   // The template will fill the following properties
   this.elt = null;
   this.expander = null;
-  this.codeBox = null;
+  this.highlighter = null;
+  this.tagLine = null;
   this.children = null;
   this.markup.template("container", this);
   this.elt.container = this;
   this.children.container = this;
 
-  this.expander.addEventListener("click", function() {
-    this.markup.navigate(this);
-
-    this.markup.setNodeExpanded(this.node, !this.expanded);
-  }.bind(this));
-
-  this.codeBox.insertBefore(this.editor.elt, this.children);
-
-  this.editor.elt.addEventListener("mousedown", function(evt) {
-    this.markup.navigate(this);
-  }.bind(this), false);
+  // Expanding/collapsing the node on dblclick of the whole tag-line element
+  this._onToggle = this._onToggle.bind(this);
+  this.elt.addEventListener("dblclick", this._onToggle, false);
+  this.expander.addEventListener("click", this._onToggle, false);
 
-  if (this.editor.summaryElt) {
-    this.editor.summaryElt.addEventListener("click", function(evt) {
-      this.markup.navigate(this);
-      this.markup.expandNode(this.node);
-    }.bind(this), false);
-    this.codeBox.appendChild(this.editor.summaryElt);
-  }
+  // Dealing with the highlighting of the row via javascript rather than :hover
+  // This is to allow highlighting the closing tag-line as well as reusing the
+  // theme css classes (which wouldn't have been possible with a :hover pseudo)
+  this._onMouseOver = this._onMouseOver.bind(this);
+  this.elt.addEventListener("mouseover", this._onMouseOver, false);
 
-  if (this.editor.closeElt) {
-    this.editor.closeElt.addEventListener("mousedown", function(evt) {
-      this.markup.navigate(this);
-    }.bind(this), false);
-    this.codeBox.appendChild(this.editor.closeElt);
-  }
+  this._onMouseOut = this._onMouseOut.bind(this);
+  this.elt.addEventListener("mouseout", this._onMouseOut, false);
+
+  // Appending the editor element and attaching event listeners
+  this.tagLine.appendChild(this.editor.elt);
+
+  this.elt.addEventListener("mousedown", this._onMouseDown.bind(this), false);
 }
 
 MarkupContainer.prototype = {
-  toString: function() { return "[MarkupContainer for " + this.node + "]" },
+  toString: function() {
+    return "[MarkupContainer for " + this.node + "]";
+  },
 
   /**
    * True if the current node has children.  The MarkupView
    * will set this attribute for the MarkupContainer.
    */
   _hasChildren: false,
 
   get hasChildren() {
@@ -904,104 +894,170 @@ MarkupContainer.prototype = {
   parentContainer: function() {
     return this.elt.parentNode ? this.elt.parentNode.container : null;
   },
 
   /**
    * True if the node has been visually expanded in the tree.
    */
   get expanded() {
-    return this.children.hasAttribute("expanded");
+    return !this.elt.classList.contains("collapsed");
   },
 
   set expanded(aValue) {
-    if (aValue) {
+    if (aValue && this.elt.classList.contains("collapsed")) {
+      // Expanding a node means cloning its "inline" closing tag into a new
+      // tag-line that the user can interact with and showing the children.
+      if (this.editor instanceof ElementEditor) {
+        let closingTag = this.elt.querySelector(".close");
+        if (closingTag) {
+          if (!this.closeTagLine) {
+            let line = this.markup.doc.createElement("div");
+            line.classList.add("tag-line");
+
+            let highlighter = this.markup.doc.createElement("div");
+            highlighter.classList.add("highlighter");
+            line.appendChild(highlighter);
+
+            line.appendChild(closingTag.cloneNode(true));
+            line.addEventListener("mouseover", this._onMouseOver, false);
+            line.addEventListener("mouseout", this._onMouseOut, false);
+
+            this.closeTagLine = line;
+          }
+          this.elt.appendChild(this.closeTagLine);
+        }
+      }
+      this.elt.classList.remove("collapsed");
       this.expander.setAttribute("open", "");
-      this.children.setAttribute("expanded", "");
-      if (this.editor.summaryElt) {
-        this.editor.summaryElt.setAttribute("expanded", "");
+      this.highlighted = false;
+    } else if (!aValue) {
+      if (this.editor instanceof ElementEditor && this.closeTagLine) {
+        this.elt.removeChild(this.closeTagLine);
+      }
+      this.elt.classList.add("collapsed");
+      this.expander.removeAttribute("open");
+    }
+  },
+
+  _onToggle: function(event) {
+    this.markup.navigate(this);
+    if(this.hasChildren) {
+      this.markup.setNodeExpanded(this.node, !this.expanded);
+    }
+    event.stopPropagation();
+  },
+
+  _onMouseOver: function(event) {
+    this.highlighted = true;
+    event.stopPropagation();
+  },
+
+  _onMouseOut: function(event) {
+    this.highlighted = false;
+    event.stopPropagation();
+  },
+
+  _onMouseDown: function(event) {
+    this.highlighted = false;
+    this.markup.navigate(this);
+    event.stopPropagation();
+  },
+
+  _highlighted: false,
+
+  /**
+   * Highlight the currently hovered tag + its closing tag if necessary
+   * (that is if the tag is expanded)
+   */
+  set highlighted(aValue) {
+    this._highlighted = aValue;
+    if (aValue) {
+      if (!this.selected) {
+        this.highlighter.classList.add("theme-bg-darker");
+      }
+      if (this.closeTagLine) {
+        this.closeTagLine.querySelector(".highlighter").classList.add("theme-bg-darker");
       }
     } else {
-      this.expander.removeAttribute("open");
-      this.children.removeAttribute("expanded");
-      if (this.editor.summaryElt) {
-        this.editor.summaryElt.removeAttribute("expanded");
+      this.highlighter.classList.remove("theme-bg-darker");
+      if (this.closeTagLine) {
+        this.closeTagLine.querySelector(".highlighter").classList.remove("theme-bg-darker");
       }
     }
   },
 
   /**
    * True if the container is visible in the markup tree.
    */
-  get visible()
-  {
+  get visible() {
     return this.elt.getBoundingClientRect().height > 0;
   },
 
   /**
    * True if the container is currently selected.
    */
   _selected: false,
 
   get selected() {
     return this._selected;
   },
 
   set selected(aValue) {
     this._selected = aValue;
     this.editor.selected = aValue;
     if (this._selected) {
-      this.editor.elt.classList.add("theme-selected");
-      if (this.editor.closeElt) {
-        this.editor.closeElt.classList.add("theme-selected");
-      }
+      this.tagLine.setAttribute("selected", "");
+      this.highlighter.classList.add("theme-selected");
     } else {
-      this.editor.elt.classList.remove("theme-selected");
-      if (this.editor.closeElt) {
-        this.editor.closeElt.classList.remove("theme-selected");
-      }
+      this.tagLine.removeAttribute("selected");
+      this.highlighter.classList.remove("theme-selected");
     }
   },
 
   /**
    * Update the container's editor to the current state of the
    * viewed node.
    */
-  update: function MC_update()
-  {
+  update: function() {
     if (this.editor.update) {
       this.editor.update();
     }
   },
 
   /**
    * Try to put keyboard focus on the current editor.
    */
-  focus: function MC_focus()
-  {
+  focus: function() {
     let focusable = this.editor.elt.querySelector("[tabindex]");
     if (focusable) {
       focusable.focus();
     }
-  },
-}
+  }
+};
+
 
 /**
  * Dummy container node used for the root document element.
  */
-function RootContainer(aMarkupView, aNode)
-{
+function RootContainer(aMarkupView, aNode) {
   this.doc = aMarkupView.doc;
   this.elt = this.doc.createElement("ul");
   this.elt.container = this;
   this.children = this.elt;
   this.node = aNode;
   this.toString = function() { return "[root container]"}
 }
 
+RootContainer.prototype = {
+  hasChildren: true,
+  expanded: true,
+  update: function() {}
+};
+
 /**
  * Creates an editor for simple nodes.
  */
 function GenericEditor(aContainer, aNode)
 {
   this.elt = aContainer.doc.createElement("span");
   this.elt.className = "editor";
   this.elt.textContent = aNode.nodeName;
@@ -1112,37 +1168,29 @@ function ElementEditor(aContainer, aNode
 {
   this.doc = aContainer.doc;
   this.undo = aContainer.undo;
   this.template = aContainer.markup.template.bind(aContainer.markup);
   this.container = aContainer;
   this.markup = this.container.markup;
   this.node = aNode;
 
-  this.attrs = { };
+  this.attrs = {};
 
   // The templates will fill the following properties
   this.elt = null;
   this.tag = null;
+  this.closeTag = null;
   this.attrList = null;
   this.newAttr = null;
-  this.summaryElt = null;
   this.closeElt = null;
 
   // Create the main editor
   this.template("element", this);
 
-  if (this.node.hasChildren) {
-    // Create the summary placeholder
-    this.template("elementContentSummary", this);
-  }
-
-  // Create the closing tag
-  this.template("elementClose", this);
-
   this.rawNode = aNode.rawNode();
 
   // Make the tag name editable (unless this is a remote node or
   // a document element)
   if (this.rawNode && !aNode.isDocumentElement) {
     this.tag.setAttribute("tabindex", "0");
     editableField({
       element: this.tag,
@@ -1333,17 +1381,16 @@ ElementEditor.prototype = {
    *        user put them.
    */
   _applyAttributes: function EE__applyAttributes(aValue, aAttrNode, aDoMods, aUndoMods)
   {
     let attrs = parseAttributeValues(aValue, this.doc);
     for (let attr of attrs) {
       // Create an attribute editor next to the current attribute if needed.
       this._createAttribute(attr, aAttrNode ? aAttrNode.nextSibling : null);
-
       this._saveAttribute(attr.name, aUndoMods);
       aDoMods.setAttribute(attr.name, attr.value);
     }
   },
 
   /**
    * Saves the current state of the given attribute into an attribute
    * modification list.
@@ -1409,24 +1456,16 @@ ElementEditor.prototype = {
         swapNodes(newElt, this.rawNode);
         this.markup.setNodeExpanded(this.node, newContainer.expanded);
         if (newContainer.selected) {
           this.markup.navigate(this.container);
         }
       });
     }).then(null, console.error);
   }
-}
-
-
-
-RootContainer.prototype = {
-  hasChildren: true,
-  expanded: true,
-  update: function RC_update() {}
 };
 
 function nodeDocument(node) {
   return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
 }
 
 /**
  * Parse attribute names and values from a string.
@@ -1434,17 +1473,16 @@ function nodeDocument(node) {
  * @param  {String} attr
  *         The input string for which names/values are to be parsed.
  * @param  {HTMLDocument} doc
  *         A document that can be used to test valid attributes.
  * @return {Array}
  *         An array of attribute names and their values.
  */
 function parseAttributeValues(attr, doc) {
-
   attr = attr.trim();
 
   // Handle bad user inputs by appending a " or ' if it fails to parse without them.
   let el = DOMParser.parseFromString("<div " + attr + "></div>", "text/html").body.childNodes[0] ||
            DOMParser.parseFromString("<div " + attr + "\"></div>", "text/html").body.childNodes[0] ||
            DOMParser.parseFromString("<div " + attr + "'></div>", "text/html").body.childNodes[0];
   let div = doc.createElement("div");
 
@@ -1461,24 +1499,11 @@ function parseAttributeValues(attr, doc)
     }
     catch(e) { }
   }
 
   // Attributes return from DOMParser in reverse order from how they are entered.
   return attributes.reverse();
 }
 
-/**
- * A tree walker filter for avoiding empty whitespace text nodes.
- */
-function whitespaceTextFilter(aNode)
-{
-    if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
-        !/[^\s]/.exec(aNode.nodeValue)) {
-      return Ci.nsIDOMNodeFilter.FILTER_SKIP;
-    } else {
-      return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
-    }
-}
-
 loader.lazyGetter(MarkupView.prototype, "strings", () => Services.strings.createBundle(
   "chrome://browser/locale/devtools/inspector.properties"
 ));
--- a/browser/devtools/markupview/markup-view.xhtml
+++ b/browser/devtools/markupview/markup-view.xhtml
@@ -10,38 +10,41 @@
   <link rel="stylesheet" href="chrome://browser/content/devtools/markup-view.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/markup-view.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
 
   <script type="application/javascript;version=1.8" src="theme-switching.js"/>
 
 </head>
 <body class="theme-body devtools-monospace" role="application">
-  <div id="root"></div>
+  <div id="root-wrapper">
+    <div id="root"></div>
+  </div>
   <div id="templates" style="display:none">
-    <ul>
-      <li id="template-container" save="${elt}" class="container"><span save="${codeBox}" class="codebox"><span save="${expander}" class="theme-twisty expander"></span><ul save="${children}" class="children"></ul></span></li>
+
+    <ul class="children">
+      <li id="template-container" save="${elt}" class="child collapsed">
+        <div save="${tagLine}" class="tag-line"><span save="${highlighter}" class="highlighter"></span><span save="${expander}" class="theme-twisty expander"></span></div>
+        <ul save="${children}" class="children"></ul>
+      </li>
 
       <li id="template-more-nodes" class="more-nodes devtools-class-comment" save="${elt}"><span>${showing}</span> <button href="#" onclick="${allButtonClick}">${showAll}</button></li>
     </ul>
 
-    <span id="template-element" save="${elt}" class="editor"><span>&lt;</span><span save="${tag}" class="tagname theme-fg-color3"></span><span save="${attrList}"></span><span save="${newAttr}" class="newattr" tabindex="0"></span><span class="closing-bracket">&gt;</span></span>
+    <span id="template-element" save="${elt}" class="editor"><span class="open">&lt;<span save="${tag}" class="tag theme-fg-color3" tabindex="0"></span><span save="${attrList}"></span><span save="${newAttr}" class="newattr" tabindex="0"></span>&gt;</span><span class="close">&lt;/<span save="${closeTag}" class="tag theme-fg-color3"></span>&gt;</span></span>
 
-    <span id="template-attribute" save="${attr}" data-attr="${attrName}" class="attreditor" style="display:none"> <span class="editable" save="${inner}" tabindex="0"><span save="${name}" class="attrname theme-fg-color2"></span>=&quot;<span save="${val}" class="attrvalue theme-fg-color6"></span>&quot;</span></span>
+    <span id="template-attribute" save="${attr}" data-attr="${attrName}" class="attreditor" style="display:none"> <span class="editable" save="${inner}" tabindex="0"><span save="${name}" class="attr-name theme-fg-color2"></span>=&quot;<span save="${val}" class="attr-value theme-fg-color6"></span>&quot;</span></span>
 
     <span id="template-text" save="${elt}" class="editor text">
       <pre save="${value}" style="display:inline-block;" tabindex="0"></pre>
     </span>
 
-    <span id="template-comment" save="${elt}" class="editor comment theme-comment">
-      <span>&lt;!--</span><pre save="${value}" style="display:inline-block;" tabindex="0"></pre><span>--&gt;</span>
-    </span>
+    <span id="template-comment" save="${elt}" class="editor comment theme-comment"><span>&lt;!--</span><pre save="${value}" style="display:inline-block;" tabindex="0"></pre><span>--&gt;</span></span>
 
-    <span id="template-elementContentSummary" save="${summaryElt}" class="summary"> … </span>
+    <!-- span id="template-elementClose" save="${closeElt}">&lt;/<span save="${closeTag}" class="tagname theme-fg-color3"></span>&gt;</span -->
 
-    <span id="template-elementClose" save="${closeElt}">&lt;/<span save="${closeTag}" class="tagname theme-fg-color3"></span>&gt;</span>
-   </div>
-   <div id="previewbar" class="disabled">
+  </div>
+  <div id="previewbar" class="disabled">
      <div id="preview"/>
      <div id="viewbox"/>
-   </div>
+  </div>
 </body>
 </html>
--- a/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
@@ -29,17 +29,16 @@ function test() {
     ["down" , "node7"],
     ["right", "node7"],
     ["down", "*text*"],
     ["down", "node8"],
     ["left", "node7"],
     ["left", "node7"],
     ["right", "node7"],
     ["right", "*text*"],
-    ["right", "*text*"],
     ["down", "node8"],
     ["right", "node8"],
     ["left", "node8"],
     ["down", "node9"],
     ["down", "node10"],
     ["down", "node11"],
     ["down", "node12"],
     ["right", "node12"],
--- a/browser/devtools/shared/inplace-editor.js
+++ b/browser/devtools/shared/inplace-editor.js
@@ -200,20 +200,19 @@ function InplaceEditor(aOptions, aEvent)
 
   this.input.addEventListener("blur", this._onBlur, false);
   this.input.addEventListener("keypress", this._onKeyPress, false);
   this.input.addEventListener("input", this._onInput, false);
   this.input.addEventListener("mousedown", function(aEvt) {
                                              aEvt.stopPropagation();
                                            }, false);
 
-  this.warning = aOptions.warning;
   this.validate = aOptions.validate;
 
-  if (this.warning && this.validate) {
+  if (this.validate) {
     this.input.addEventListener("keyup", this._onKeyup, false);
   }
 
   if (aOptions.start) {
     aOptions.start(this, aEvent);
   }
 
   EventEmitter.decorate(this);
@@ -249,25 +248,25 @@ InplaceEditor.prototype = {
     this.input.removeEventListener("keypress", this._onKeyPress, false);
     this.input.removeEventListener("keyup", this._onKeyup, false);
     this.input.removeEventListener("oninput", this._onInput, false);
     this._stopAutosize();
 
     this.elt.style.display = this.originalDisplay;
     this.elt.focus();
 
-    if (this.destroy) {
-      this.destroy();
-    }
-
     this.elt.parentNode.removeChild(this.input);
     this.input = null;
 
     delete this.elt.inplaceEditor;
     delete this.elt;
+
+    if (this.destroy) {
+      this.destroy();
+    }
   },
 
   /**
    * Keeps the editor close to the size of its input string.  This is pretty
    * crappy, suggestions for improvement welcome.
    */
   _autosize: function InplaceEditor_autosize()
   {
@@ -347,17 +346,17 @@ InplaceEditor.prototype = {
                                            selectionEnd);
 
     if (!newValue) {
       return false;
     }
 
     this.input.value = newValue.value;
     this.input.setSelectionRange(newValue.start, newValue.end);
-    this.warning.hidden = this.validate(this.input.value);
+    this._doValidation();
 
     return true;
   },
 
   /**
    * Increment the property value based on the property type.
    *
    * @param {string} value
@@ -763,18 +762,20 @@ InplaceEditor.prototype = {
       let input = this.input;
       let pre = input.value.slice(0, input.selectionStart);
       let post = input.value.slice(input.selectionEnd, input.value.length);
       let item = this.popup.selectedItem;
       let toComplete = item.label.slice(item.preLabel.length);
       input.value = pre + toComplete + post;
       input.setSelectionRange(pre.length, pre.length + toComplete.length);
       this._updateSize();
+
       // This emit is mainly for the purpose of making the test flow simpler.
       this.emit("after-suggest");
+      this._doValidation();
     }
 
     if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_BACK_SPACE ||
         aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_DELETE ||
         aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_LEFT ||
         aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RIGHT) {
       if (this.popup && this.popup.isOpen) {
         this.popup.hidePopup();
@@ -850,43 +851,49 @@ InplaceEditor.prototype = {
       aEvent.preventDefault();
     }
   },
 
   /**
    * Handle the input field's keyup event.
    */
   _onKeyup: function(aEvent) {
-    // Validate the entered value.
-    this.warning.hidden = this.validate(this.input.value);
     this._applied = false;
   },
 
   /**
    * Handle changes to the input text.
    */
   _onInput: function InplaceEditor_onInput(aEvent)
   {
     // Validate the entered value.
-    if (this.warning && this.validate) {
-      this.warning.hidden = this.validate(this.input.value);
-    }
+    this._doValidation();
 
     // Update size if we're autosizing.
     if (this._measurement) {
       this._updateSize();
     }
 
     // Call the user's change handler if available.
     if (this.change) {
       this.change(this.input.value.trim());
     }
   },
 
   /**
+   * Fire validation callback with current input
+   */
+  _doValidation: function()
+  {
+    if (this.validate && this.input) {
+      this.validate(this.input.value);
+    }
+  },
+
+  /**
    * Handles displaying suggestions based on the current input.
    */
   _maybeSuggestCompletion: function() {
     // Since we are calling this method from a keypress event handler, the
     // |input.value| does not include currently typed character. Thus we perform
     // this method async.
     this.doc.defaultView.setTimeout(() => {
       if (this._preventSuggestions) {
@@ -975,16 +982,17 @@ InplaceEditor.prototype = {
       if (finalList.length > 1) {
         this.popup.setItems(finalList);
         this.popup.openPopup(this.input);
       } else {
         this.popup.hidePopup();
       }
       // This emit is mainly for the purpose of making the test flow simpler.
       this.emit("after-suggest");
+      this._doValidation();
     }, 0);
   }
 };
 
 /**
  * Copy text-related styles from one element to another.
  */
 function copyTextStyles(aFrom, aTo)
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -520,31 +520,36 @@ Rule.prototype = {
    *
    * @param {string} [aName]
    *        A text property name (such as "background" or "border-top") used
    *        when calling from setPropertyValue & setPropertyName to signify
    *        that the property should be saved in store.userProperties.
    */
   applyProperties: function Rule_applyProperties(aModifications, aName)
   {
+    this.elementStyle.markOverriddenAll();
+
     if (!aModifications) {
       aModifications = this.style.startModifyingProperties();
     }
     let disabledProps = [];
     let store = this.elementStyle.store;
 
     for (let prop of this.textProps) {
       if (!prop.enabled) {
         disabledProps.push({
           name: prop.name,
           value: prop.value,
           priority: prop.priority
         });
         continue;
       }
+      if (prop.value.trim() === "") {
+        continue;
+      }
 
       aModifications.setProperty(prop.name, prop.value, prop.priority);
 
       prop.updateComputed();
     }
 
     // Store disabled properties in the disabled store.
     let disabled = this.elementStyle.store.disabled;
@@ -624,16 +629,17 @@ Rule.prototype = {
    * @param {string} aPriority
    *        The property's priority (either "important" or an empty string).
    */
   setPropertyValue: function Rule_setPropertyValue(aProperty, aValue, aPriority)
   {
     if (aValue === aProperty.value && aPriority === aProperty.priority) {
       return;
     }
+
     aProperty.value = aValue;
     aProperty.priority = aPriority;
     this.applyProperties(null, aProperty.name);
   },
 
   /**
    * Disables or enables given TextProperty.
    */
@@ -841,16 +847,55 @@ Rule.prototype = {
     // value.
     if (match.prop) {
       match.prop.set(aNewProp);
       return true;
     }
 
     return false;
   },
+
+  /**
+   * Jump between editable properties in the UI.  Will begin editing the next
+   * name, if possible.  If this is the last element in the set, then begin
+   * editing the previous value.  If this is the *only* element in the set,
+   * then settle for focusing the new property editor.
+   *
+   * @param {TextProperty} aTextProperty
+   *        The text property that will be left to focus on a sibling.
+   *
+   */
+  editClosestTextProperty: function Rule__editClosestTextProperty(aTextProperty)
+  {
+    let index = this.textProps.indexOf(aTextProperty);
+    let previous = false;
+
+    // If this is the last element, move to the previous instead of next
+    if (index === this.textProps.length - 1) {
+      index = index - 1;
+      previous = true;
+    }
+    else {
+      index = index + 1;
+    }
+
+    let nextProp = this.textProps[index];
+
+    // If possible, begin editing the next name or previous value.
+    // Otherwise, settle for focusing the new property element.
+    if (nextProp) {
+      if (previous) {
+        nextProp.editor.valueSpan.click();
+      } else {
+        nextProp.editor.nameSpan.click();
+      }
+    } else {
+      aTextProperty.rule.editor.closeBrace.focus();
+    }
+  }
 };
 
 /**
  * A single property in a rule's cssText.
  *
  * @param {Rule} aRule
  *        The rule this TextProperty came from.
  * @param {string} aName
@@ -1268,17 +1313,17 @@ CssRuleView.prototype = {
           this.togglePseudoElementVisibility(!this.showPseudoElements);
         }, false);
 
         div.insertBefore(twisty, div.firstChild);
         this.element.appendChild(div);
       }
 
       if (!rule.editor) {
-        new RuleEditor(this, rule);
+        rule.editor = new RuleEditor(this, rule);
       }
 
       this.element.appendChild(rule.editor.element);
     }
 
     this.togglePseudoElementVisibility(this.showPseudoElements);
   },
 
@@ -1328,17 +1373,16 @@ CssRuleView.prototype = {
  *        The Rule object we're editing.
  * @constructor
  */
 function RuleEditor(aRuleView, aRule)
 {
   this.ruleView = aRuleView;
   this.doc = this.ruleView.doc;
   this.rule = aRule;
-  this.rule.editor = this;
 
   this._onNewProperty = this._onNewProperty.bind(this);
   this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
 
   this._create();
 }
 
 RuleEditor.prototype = {
@@ -1392,27 +1436,16 @@ RuleEditor.prototype = {
       let selection = this.doc.defaultView.getSelection();
       if (selection.isCollapsed) {
         this.newProperty();
       }
     }.bind(this), false);
 
     this.element.addEventListener("mousedown", function() {
       this.doc.defaultView.focus();
-
-      let editorNodes =
-        this.doc.querySelectorAll(".styleinspector-propertyeditor");
-
-      if (editorNodes) {
-        for (let node of editorNodes) {
-          if (node.inplaceEditor) {
-            node.inplaceEditor._clear();
-          }
-        }
-      }
     }.bind(this), false);
 
     this.propertyList = createChild(code, "ul", {
       class: "ruleview-propertylist"
     });
 
     this.populate();
 
@@ -1461,18 +1494,18 @@ RuleEditor.prototype = {
           class: cls,
           textContent: selector
         });
       });
     }
 
     for (let prop of this.rule.textProps) {
       if (!prop.editor) {
-        new TextPropertyEditor(this, prop);
-        this.propertyList.appendChild(prop.editor.element);
+        let editor = new TextPropertyEditor(this, prop);
+        this.propertyList.appendChild(editor.element);
       }
     }
   },
 
   /**
    * Programatically add a new property to the rule.
    *
    * @param {string} aName
@@ -1576,34 +1609,39 @@ RuleEditor.prototype = {
 function TextPropertyEditor(aRuleEditor, aProperty)
 {
   this.ruleEditor = aRuleEditor;
   this.doc = this.ruleEditor.doc;
   this.popup = this.ruleEditor.ruleView.popup;
   this.prop = aProperty;
   this.prop.editor = this;
   this.browserWindow = this.doc.defaultView.top;
+  this.removeOnRevert = this.prop.value === "";
 
   let sheet = this.prop.rule.sheet;
   let href = sheet ? (sheet.href || sheet.nodeHref) : null;
   if (href) {
     this.sheetURI = IOService.newURI(href, null, null);
   }
 
   this._onEnableClicked = this._onEnableClicked.bind(this);
   this._onExpandClicked = this._onExpandClicked.bind(this);
   this._onStartEditing = this._onStartEditing.bind(this);
   this._onNameDone = this._onNameDone.bind(this);
   this._onValueDone = this._onValueDone.bind(this);
+  this._onValidate = throttle(this._livePreview, 10, this, this.browserWindow);
 
   this._create();
   this.update();
 }
 
 TextPropertyEditor.prototype = {
+  /**
+   * Boolean indicating if the name or value is being currently edited.
+   */
   get editing() {
     return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor);
   },
 
   /**
    * Create the property editor's DOM.
    */
   _create: function TextPropertyEditor_create()
@@ -1622,55 +1660,56 @@ TextPropertyEditor.prototype = {
     this.expander = createChild(this.element, "span", {
       class: "ruleview-expander theme-twisty"
     });
     this.expander.addEventListener("click", this._onExpandClicked, true);
 
     this.nameContainer = createChild(this.element, "span", {
       class: "ruleview-namecontainer"
     });
-    this.nameContainer.addEventListener("click", function(aEvent) {
+    this.nameContainer.addEventListener("click", (aEvent) => {
       // Clicks within the name shouldn't propagate any further.
       aEvent.stopPropagation();
       if (aEvent.target === propertyContainer) {
         this.nameSpan.click();
       }
-    }.bind(this), false);
+    }, false);
 
     // Property name, editable when focused.  Property name
     // is committed when the editor is unfocused.
     this.nameSpan = createChild(this.nameContainer, "span", {
       class: "ruleview-propertyname theme-fg-color5",
       tabindex: "0",
     });
 
     editableField({
       start: this._onStartEditing,
       element: this.nameSpan,
       done: this._onNameDone,
+      destroy: this.update.bind(this),
       advanceChars: ':',
       contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY,
       popup: this.popup
     });
 
     appendText(this.nameContainer, ": ");
 
     // Create a span that will hold the property and semicolon.
     // Use this span to create a slightly larger click target
     // for the value.
     let propertyContainer = createChild(this.element, "span", {
       class: "ruleview-propertycontainer"
     });
-    propertyContainer.addEventListener("click", function(aEvent) {
+    propertyContainer.addEventListener("click", (aEvent) => {
       // Clicks within the value shouldn't propagate any further.
       aEvent.stopPropagation();
       if (aEvent.target === propertyContainer) {
         this.valueSpan.click();
       }
-    }.bind(this), false);
+    }, false);
 
     // Property value, editable when focused.  Changes to the
     // property value are applied as they are typed, and reverted
     // if the user presses escape.
     this.valueSpan = createChild(propertyContainer, "span", {
       class: "ruleview-propertyvalue theme-fg-color1",
       tabindex: "0",
     });
@@ -1679,33 +1718,33 @@ TextPropertyEditor.prototype = {
     // for restoring after pressing escape.
     this.committed = { name: this.prop.name,
                        value: this.prop.value,
                        priority: this.prop.priority };
 
     appendText(propertyContainer, ";");
 
     this.warning = createChild(this.element, "div", {
+      class: "ruleview-warning",
       hidden: "",
-      class: "ruleview-warning",
       title: CssLogic.l10n("rule.warning.title"),
     });
 
     // Holds the viewers for the computed properties.
     // will be populated in |_updateComputed|.
     this.computed = createChild(this.element, "ul", {
       class: "ruleview-computedlist",
     });
 
     editableField({
       start: this._onStartEditing,
       element: this.valueSpan,
       done: this._onValueDone,
-      validate: this._validate.bind(this),
-      warning: this.warning,
+      destroy: this.update.bind(this),
+      validate: this._onValidate,
       advanceChars: ';',
       contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
       property: this.prop,
       popup: this.popup
     });
   },
 
   /**
@@ -1746,32 +1785,33 @@ TextPropertyEditor.prototype = {
     if (this.prop.enabled) {
       this.enable.style.removeProperty("visibility");
       this.enable.setAttribute("checked", "");
     } else {
       this.enable.style.visibility = "visible";
       this.enable.removeAttribute("checked");
     }
 
-    if (this.prop.overridden && !this.editing) {
+    this.warning.hidden = this.editing || this.isValid();
+
+    if ((this.prop.overridden || !this.prop.enabled) && !this.editing) {
       this.element.classList.add("ruleview-overridden");
     } else {
       this.element.classList.remove("ruleview-overridden");
     }
 
     let name = this.prop.name;
     this.nameSpan.textContent = name;
 
     // Combine the property's value and priority into one string for
     // the value.
     let val = this.prop.value;
     if (this.prop.priority) {
       val += " !" + this.prop.priority;
     }
-
     // Treat URLs differently than other properties.
     // Allow the user to click a link to the resource and open it.
     let resourceURI = this.getResourceURI();
     if (resourceURI) {
       this.valueSpan.textContent = "";
 
       appendText(this.valueSpan, val.split(resourceURI)[0]);
 
@@ -1792,33 +1832,32 @@ TextPropertyEditor.prototype = {
 
       }, false);
 
       appendText(this.valueSpan, val.split(resourceURI)[1]);
     } else {
       this.valueSpan.textContent = val;
     }
 
-    this.warning.hidden = this._validate();
-
     let store = this.prop.rule.elementStyle.store;
     let propDirty = store.userProperties.contains(this.prop.rule.style, name);
     if (propDirty) {
       this.element.setAttribute("dirty", "");
     } else {
       this.element.removeAttribute("dirty");
     }
 
     // Populate the computed styles.
     this._updateComputed();
   },
 
   _onStartEditing: function TextPropertyEditor_onStartEditing()
   {
     this.element.classList.remove("ruleview-overridden");
+    this._livePreview(this.prop.value);
   },
 
   /**
    * Populate the list of computed styles.
    */
   _updateComputed: function TextPropertyEditor_updateComputed()
   {
     // Clear out existing viewers.
@@ -1901,29 +1940,23 @@ TextPropertyEditor.prototype = {
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {boolean} aCommit
    *        True if the change should be applied.
    */
   _onNameDone: function TextPropertyEditor_onNameDone(aValue, aCommit)
   {
-    if (!aCommit) {
-      if (this.prop.overridden) {
-        this.element.classList.add("ruleview-overridden");
+    if (aCommit) {
+      if (aValue.trim() === "") {
+        this.remove();
+      } else {
+        this.prop.setName(aValue);
       }
-
-      return;
     }
-    if (!aValue) {
-      this.prop.remove();
-      this.element.parentNode.removeChild(this.element);
-      return;
-    }
-    this.prop.setName(aValue);
   },
 
   /**
    * Pull priority (!important) out of the value provided by a
    * value editor.
    *
    * @param {string} aValue
    *        The value from the text editor.
@@ -1934,70 +1967,110 @@ TextPropertyEditor.prototype = {
     let pieces = aValue.split("!", 2);
     return {
       value: pieces[0].trim(),
       priority: (pieces.length > 1 ? pieces[1].trim() : "")
     };
   },
 
   /**
+   * Remove property from style and the editors from DOM.
+   * Begin editing next available property.
+   */
+  remove: function TextPropertyEditor_remove()
+  {
+    this.element.parentNode.removeChild(this.element);
+    this.ruleEditor.rule.editClosestTextProperty(this.prop);
+    this.prop.remove();
+  },
+
+  /**
    * Called when a value editor closes.  If the user pressed escape,
    * revert to the value this property had before editing.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {bool} aCommit
    *        True if the change should be applied.
    */
    _onValueDone: function PropertyEditor_onValueDone(aValue, aCommit)
   {
     if (aCommit) {
       let val = this._parseValue(aValue);
-      this.prop.setValue(val.value, val.priority);
-      this.committed.value = this.prop.value;
-      this.committed.priority = this.prop.priority;
-      if (this.prop.overridden) {
-        this.element.classList.add("ruleview-overridden");
+      // Any property should be removed if has an empty value.
+      if (val.value.trim() === "") {
+        this.remove();
+      } else {
+        this.prop.setValue(val.value, val.priority);
+        this.removeOnRevert = false;
+        this.committed.value = this.prop.value;
+        this.committed.priority = this.prop.priority;
       }
     } else {
-      this.prop.setValue(this.committed.value, this.committed.priority);
+      // A new property should be removed when escape is pressed.
+      if (this.removeOnRevert) {
+        this.remove();
+      } else {
+        this.prop.setValue(this.committed.value, this.committed.priority);
+      }
     }
   },
 
   /**
-   * Validate this property.
+   * Live preview this property, without committing changes.
    *
    * @param {string} [aValue]
-   *        Override the actual property value used for validation without
-   *        applying property values e.g. validate as you type.
+   *        The value to set the current property to.
+   */
+  _livePreview: function TextPropertyEditor_livePreview(aValue)
+  {
+    // Since function call is throttled, we need to make sure we are still editing
+    if (!this.editing) {
+      return;
+    }
+
+    let val = this._parseValue(aValue);
+
+    // Live previewing the change without committing just yet, that'll be done in _onValueDone
+    // If it was not a valid value, apply an empty string to reset the live preview
+    this.ruleEditor.rule.setPropertyValue(this.prop, val.value, val.priority);
+  },
+
+  /**
+   * Validate this property. Does it make sense for this value to be assigned
+   * to this property name? This does not apply the property value
+   *
+   * @param {string} [aValue]
+   *        The property value used for validation.
+   *        Defaults to the current value for this.prop
    *
    * @return {bool} true if the property value is valid, false otherwise.
    */
-  _validate: function TextPropertyEditor_validate(aValue)
+  isValid: function TextPropertyEditor_isValid(aValue)
   {
     let name = this.prop.name;
     let value = typeof aValue == "undefined" ? this.prop.value : aValue;
     let val = this._parseValue(value);
 
     let style = this.doc.createElementNS(HTML_NS, "div").style;
     let prefs = Services.prefs;
 
     // We toggle output of errors whilst the user is typing a property value.
-    let prefVal = Services.prefs.getBoolPref("layout.css.report_errors");
+    let prefVal = prefs.getBoolPref("layout.css.report_errors");
     prefs.setBoolPref("layout.css.report_errors", false);
 
+    let validValue = false;
     try {
       style.setProperty(name, val.value, val.priority);
-      // Live previewing the change without committing yet just yet, that'll be done in _onValueDone
-      this.ruleEditor.rule.setPropertyValue(this.prop, val.value, val.priority);
+      validValue = style.getPropertyValue(name) !== "" || val.value === "";
     } finally {
       prefs.setBoolPref("layout.css.report_errors", prefVal);
     }
-    return !!style.getPropertyValue(name);
-  },
+    return validValue;
+  }
 };
 
 /**
  * Store of CSSStyleDeclarations mapped to properties that have been changed by
  * the user.
  */
 function UserProperties()
 {
@@ -2112,16 +2185,32 @@ function createMenuItem(aMenu, aAttribut
   item.setAttribute("accesskey", _strings.GetStringFromName(aAttributes.accesskey));
   item.addEventListener("command", aAttributes.command);
 
   aMenu.appendChild(item);
 
   return item;
 }
 
+
+function throttle(func, wait, scope, window) {
+  var timer = null;
+  return function() {
+    if(timer) {
+      window.clearTimeout(timer);
+    }
+    var args = arguments;
+    timer = window.setTimeout(function() {
+      timer = null;
+      func.apply(scope, args);
+    }, wait);
+  };
+}
+
+
 /**
  * Append a text node to an element.
  */
 function appendText(aParent, aText)
 {
   aParent.appendChild(aParent.ownerDocument.createTextNode(aText));
 }
 
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
@@ -65,48 +65,86 @@ function testCreateNew()
         is(elementRuleEditor.rule.textProps.length,  1, "Should have created a new text property.");
         is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
         let textProp = elementRuleEditor.rule.textProps[0];
         is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
         aEditor.input.value = "#XYZ";
         waitForEditorBlur(aEditor, function() {
           promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
             is(textProp.value, "#XYZ", "Text prop should have been changed.");
-            is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
-            testEditProperty();
+            is(textProp.editor.isValid(), false, "#XYZ should not be a valid entry");
+            testCreateNewEscape();
           }));
         });
         aEditor.input.blur();
       }));
     });
     EventUtils.synthesizeKey("VK_RETURN", {}, ruleWindow);
   });
 
   EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
                              { },
                              ruleWindow);
 }
 
+function testCreateNewEscape()
+{
+  // Create a new property.
+  let elementRuleEditor = ruleView.element.children[0]._ruleEditor;
+  waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
+    is(inplaceEditor(elementRuleEditor.newPropSpan), aEditor, "Next focused editor should be the new property editor.");
+    let input = aEditor.input;
+    input.value = "color";
+
+    waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
+      promiseDone(expectRuleChange(elementRuleEditor.rule).then(() => {
+        is(elementRuleEditor.rule.textProps.length,  2, "Should have created a new text property.");
+        is(elementRuleEditor.propertyList.children.length, 2, "Should have created a property editor.");
+        let textProp = elementRuleEditor.rule.textProps[1];
+        is(aEditor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
+        aEditor.input.value = "red";
+        EventUtils.synthesizeKey("VK_ESCAPE", {}, ruleWindow);
+
+        // Make sure previous input is focused.
+        let focusedElement = inplaceEditor(elementRuleEditor.rule.textProps[0].editor.valueSpan).input;
+        is(focusedElement, focusedElement.ownerDocument.activeElement, "Correct element has focus");
+
+        EventUtils.synthesizeKey("VK_ESCAPE", {}, ruleWindow);
+
+        is(elementRuleEditor.rule.textProps.length,  1, "Should have removed the new text property.");
+        is(elementRuleEditor.propertyList.children.length, 1, "Should have removed the property editor.");
+
+        testEditProperty();
+      }));
+    });
+    EventUtils.synthesizeKey("VK_RETURN", {}, ruleWindow);
+  });
+
+  EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
+                             { },
+                             ruleWindow);
+}
+
 function testEditProperty()
 {
   let idRuleEditor = ruleView.element.children[1]._ruleEditor;
   let propEditor = idRuleEditor.rule.textProps[0].editor;
   waitForEditorFocus(propEditor.element, function onNewElement(aEditor) {
     is(inplaceEditor(propEditor.nameSpan), aEditor, "Next focused editor should be the name editor.");
     let input = aEditor.input;
     waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
       promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
         input = aEditor.input;
         is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
 
         waitForEditorBlur(aEditor, function() {
           promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
             let value = idRuleEditor.rule.domRule._rawStyle().getPropertyValue("border-color");
             is(value, "red", "border-color should have been set.");
-            is(propEditor._validate(), true, "red should be a valid entry");
+            is(propEditor.isValid(), true, "red should be a valid entry");
             finishTest();
           }));
         });
 
         for (let ch of "red;") {
           EventUtils.sendChar(ch, ruleWindow);
         }
       }));
--- a/browser/devtools/styleinspector/test/browser_ruleview_livepreview.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_livepreview.js
@@ -12,17 +12,24 @@ let inspector;
 
 // Format
 // {
 //   value : what to type in the field
 //   expected : expected computed style on the targeted element
 // }
 let testData = [
   {value: "inline", expected: "inline"},
-  {value: "something", expected: "inline"}
+  {value: "inline-block", expected: "inline-block"},
+
+  // Invalid property values should not apply, and should fall back to default
+  {value: "red", expected: "block"},
+  {value: "something", expected: "block"},
+
+  {escape: true, value: "inline", expected: "block"},
+  {escape: true, value: "block", expected: "block"}
 ];
 
 function startTest()
 {
   let style = '#testid {display:block;}';
 
   let styleNode = addStyle(doc, style);
   doc.body.innerHTML = '<div id="testid">Styled Node</div><span>inline element</span>';
@@ -44,28 +51,34 @@ function loopTestData(index)
     return;
   }
 
   let idRuleEditor = ruleView.element.children[1]._ruleEditor;
   let propEditor = idRuleEditor.rule.textProps[0].editor;
   waitForEditorFocus(propEditor.element, function(aEditor) {
     is(inplaceEditor(propEditor.valueSpan), aEditor, "Focused editor should be the value.");
 
+    let thisTest = testData[index];
+
     // Entering a correct value for the property
-    for (let ch of testData[index].value) {
+    for (let ch of thisTest.value) {
       EventUtils.sendChar(ch, ruleWindow);
     }
+    if (thisTest.escape) {
+      EventUtils.synthesizeKey("VK_ESCAPE", {});
+    } else {
+      EventUtils.synthesizeKey("VK_RETURN", {});
+    }
 
     // While the editor is still focused in, the display should have changed already
     executeSoon(() => {
       is(content.getComputedStyle(testElement).display,
         testData[index].expected,
         "Element should be previewed as " + testData[index].expected);
 
-      EventUtils.synthesizeKey("VK_RETURN", {});
       loopTestData(index + 1);
     });
   });
 
   EventUtils.synthesizeMouse(propEditor.valueSpan, 1, 1, {}, ruleWindow);
 }
 
 function finishTest()
--- a/browser/devtools/styleinspector/test/browser_ruleview_ui.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_ui.js
@@ -147,17 +147,17 @@ function testEditProperty()
                 "props[" + i + "] marked dirty as appropriate");
             }
             testDisableProperty();
           }));
         });
 
         for (let ch of "red;") {
           EventUtils.sendChar(ch, ruleWindow);
-          is(propEditor.warning.hidden, ch == "d" || ch == ";",
+          is(propEditor.warning.hidden, true,
             "warning triangle is hidden or shown as appropriate");
         }
       }));
     });
     for (let ch of "border-color:") {
       EventUtils.sendChar(ch, ruleWindow);
     }
   });
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.8.478
+Current extension version is: 0.8.505
 
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -15,18 +15,18 @@
  * limitations under the License.
  */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '0.8.478';
-PDFJS.build = '8a4a6f4';
+PDFJS.version = '0.8.505';
+PDFJS.build = 'da1c944';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
@@ -3666,17 +3666,16 @@ var TextAnnotation = (function TextAnnot
       if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) {
         rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
       }
 
       var container = this.getEmptyContainer('section', rect);
       container.className = 'annotText';
 
       var image = document.createElement('img');
-      image.style.width = container.style.width;
       image.style.height = container.style.height;
       var iconName = item.name;
       image.src = PDFJS.imageResourcesPath + 'annotation-' +
         iconName.toLowerCase() + '.svg';
       image.alt = '[{{type}} Annotation]';
       image.dataset.l10nId = 'text_annotation_type';
       image.dataset.l10nArgs = JSON.stringify({type: iconName});
       var content = document.createElement('div');
@@ -3694,25 +3693,33 @@ var TextAnnotation = (function TextAnnot
         var lines = item.content.split(/(?:\r\n?|\n)/);
         for (var i = 0, ii = lines.length; i < ii; ++i) {
           var line = lines[i];
           e.appendChild(document.createTextNode(line));
           if (i < (ii - 1))
             e.appendChild(document.createElement('br'));
         }
         text.appendChild(e);
-        image.addEventListener('mouseover', function annotationImageOver() {
+
+        var showAnnotation = function showAnnotation() {
           container.style.zIndex += 1;
           content.removeAttribute('hidden');
-        }, false);
-
-        image.addEventListener('mouseout', function annotationImageOut() {
-          container.style.zIndex -= 1;
-          content.setAttribute('hidden', true);
-        }, false);
+        };
+
+        var hideAnnotation = function hideAnnotation(e) {
+          if (e.toElement || e.relatedTarget) { // No context menu is used
+            container.style.zIndex -= 1;
+            content.setAttribute('hidden', true);
+          }
+        };
+
+        content.addEventListener('mouseover', showAnnotation, false);
+        content.addEventListener('mouseout', hideAnnotation, false);
+        image.addEventListener('mouseover', showAnnotation, false);
+        image.addEventListener('mouseout', hideAnnotation, false);
       }
 
       content.appendChild(title);
       content.appendChild(text);
       container.appendChild(image);
       container.appendChild(content);
 
       return container;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -15,18 +15,18 @@
  * limitations under the License.
  */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '0.8.478';
-PDFJS.build = '8a4a6f4';
+PDFJS.version = '0.8.505';
+PDFJS.build = 'da1c944';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
@@ -2916,17 +2916,16 @@ var TextAnnotation = (function TextAnnot
       if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) {
         rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
       }
 
       var container = this.getEmptyContainer('section', rect);
       container.className = 'annotText';
 
       var image = document.createElement('img');
-      image.style.width = container.style.width;
       image.style.height = container.style.height;
       var iconName = item.name;
       image.src = PDFJS.imageResourcesPath + 'annotation-' +
         iconName.toLowerCase() + '.svg';
       image.alt = '[{{type}} Annotation]';
       image.dataset.l10nId = 'text_annotation_type';
       image.dataset.l10nArgs = JSON.stringify({type: iconName});
       var content = document.createElement('div');
@@ -2944,25 +2943,33 @@ var TextAnnotation = (function TextAnnot
         var lines = item.content.split(/(?:\r\n?|\n)/);
         for (var i = 0, ii = lines.length; i < ii; ++i) {
           var line = lines[i];
           e.appendChild(document.createTextNode(line));
           if (i < (ii - 1))
             e.appendChild(document.createElement('br'));
         }
         text.appendChild(e);
-        image.addEventListener('mouseover', function annotationImageOver() {
+
+        var showAnnotation = function showAnnotation() {
           container.style.zIndex += 1;
           content.removeAttribute('hidden');
-        }, false);
-
-        image.addEventListener('mouseout', function annotationImageOut() {
-          container.style.zIndex -= 1;
-          content.setAttribute('hidden', true);
-        }, false);
+        };
+
+        var hideAnnotation = function hideAnnotation(e) {
+          if (e.toElement || e.relatedTarget) { // No context menu is used
+            container.style.zIndex -= 1;
+            content.setAttribute('hidden', true);
+          }
+        };
+
+        content.addEventListener('mouseover', showAnnotation, false);
+        content.addEventListener('mouseout', hideAnnotation, false);
+        image.addEventListener('mouseover', showAnnotation, false);
+        image.addEventListener('mouseout', hideAnnotation, false);
       }
 
       content.appendChild(title);
       content.appendChild(text);
       container.appendChild(image);
       container.appendChild(content);
 
       return container;
--- a/browser/extensions/pdfjs/content/web/images/annotation-check.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-check.svg
@@ -1,10 +1,11 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="40"
-   height="40">
+   height="40"
+   viewBox="0 0 40 40">
   <path
      d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
      id="path4"
      style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
 </svg>
--- a/browser/extensions/pdfjs/content/web/images/annotation-comment.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-comment.svg
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    height="40"
-   width="40">
+   width="40"
+   viewBox="0 0 40 40">
   <rect
      style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
      width="33.76017"
      height="33.76017"
      x="3.119915"
      y="3.119915" />
   <path
      d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"
--- a/browser/extensions/pdfjs/content/web/images/annotation-help.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-help.svg
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="40"
-   height="40">
+   height="40"
+   viewBox="0 0 40 40">
   <g
      transform="translate(0,-60)"
      id="layer1">
     <rect
        width="36.460953"
        height="34.805603"
        x="1.7695236"
        y="62.597198"
--- a/browser/extensions/pdfjs/content/web/images/annotation-insert.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-insert.svg
@@ -1,9 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="64"
-   height="64">
+   height="64"
+   viewBox="0 0 64 64">
   <path
      d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"
      style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
 </svg>
--- a/browser/extensions/pdfjs/content/web/images/annotation-key.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-key.svg
@@ -1,10 +1,11 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="64"
-   height="64">
+   height="64"
+   viewBox="0 0 64 64">
   <path
      d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"
      id="path604"
      style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.72665179;stroke-opacity:1" />
 </svg>
--- a/browser/extensions/pdfjs/content/web/images/annotation-newparagraph.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-newparagraph.svg
@@ -1,10 +1,11 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="64"
-   height="64">
+   height="64"
+   viewBox="0 0 64 64">
   <path
      d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"
      id="path2985"
      style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:0.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
 </svg>
--- a/browser/extensions/pdfjs/content/web/images/annotation-note.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-note.svg
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="40"
-   height="40">
+   height="40"
+   viewBox="0 0 40 40">
   <rect
      width="36.075428"
      height="31.096582"
      x="1.962286"
      y="4.4517088"
      id="rect4"
      style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.23004246;stroke-opacity:1" />
   <rect
--- a/browser/extensions/pdfjs/content/web/images/annotation-paragraph.svg
+++ b/browser/extensions/pdfjs/content/web/images/annotation-paragraph.svg
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
    xmlns="http://www.w3.org/2000/svg"
    width="40"
-   height="40">
+   height="40"
+   viewBox="0 0 40 40">
   <rect
      width="33.76017"
      height="33.76017"
      x="3.119915"
      y="3.119915"
      style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
   <path
      d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z"
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..59b3d345f055b098a42a8a7dcabc82328559df29
GIT binary patch
literal 1291
zc$}S7Z)n_P7|->X&b7tCKFqEKv&@3B-sO_^(q7uwOD?(gpx1TRgPj&ybC>seX`8%h
zl6u#IaBid6IYF5s7RG83{j}jBL$vCIUZD<A5FBgi2iZP2HeDH3hwaMwrt4LaQNcHV
z-uHR)`#sO|K7TUR)7??M@6bLB!>S{lVhqjuooDZ3=>7C>B^gaMwv@1YVcO2hCct<F
zCIKGN<UtSvvNAsU4QRtKcUp}n>_oJSO+n2oI~Z?HGcaegwdV{uH4JP#2?kZ2BUb0G
z5V)#v#0h`Y7d1j4qjpZ1pm(A>o|+g=1r(zFdAu#hA_W?-Wjv>i=oXvfh%LP=T07e$
zfp0<VVUBp{R3h4ghoA{?zqiqo@=+AdFkY(3&oER2PWvdD^r4&aP))3#Vd-Xkdl1N)
zsifJM7~ZypB#y|~w!xBQHk<Wk8@<pRB&k3k;OL-f4?=jXaov`49^E=p)*u2aWvYg)
zLLGNxISI#Xjv&fM(2QtwN3m{g2MUFZ%*h5xd3~g&k!7@H$3S{_v=tvW02u=o95YiW
zAL%3iFtt0?0U~eMW2TCdB9DkLHKqaGj))wAzIYW?VMTu!UCk212)sm76wfm)f*=JX
zzaUaV$XC|!C~lL$&^+&#1fFT8D2Z;7T0}8G2`C#aj70NgZbY|iSx<p7SN)qS?#g9D
zCXj7t#vvRjS3pk&+R(~C0}u5w__HTeU4dEaIVU~ao&r;S87N^BYWP-sS@jY2|NGnz
zxyt{E8Ht=B|5YsIEmVQda;Nl>u+uz1M;&9L)`+u(dl=?wjfi|ax14-XDz&@o^HZ-1
z!Gh~-@Y_8_I@s`J@r!3}eOXaFmdF=Bec_Yz!t&Bm%_|#!yz$mk=Np2?jpEcfRaGzJ
z<ZH)&a#L?s_Pa(sUB$)4y<)*t_~qKgz5~HJ2v_$6Z&UkAZ1!&5hg6WnEq?l)cRhXA
zD$m~V;8n>}#ToPZ+RRY@=eFA1b8`akuU@#rCvQ#WvBv52(!%QpKMHqr9Npa9xPSlq
z3+?Zf9{f4KSS+sf4Gau)N|H3Xx&C`LfBUP7djpF5>*<4@7vHX{tv&j<Qo@GT=dWH}
zuRP6<%v^^Y$MFDw`kBL7dD1uEL?%AQ4|~1chd!toVwo!Qi7T_Svp-B$*WNu<<yvT6
z{qB70ft8gNJNSN=vR3$Y`SP7Jb8{ONXL^a7`Jcmi%#9sCck*=Rvmfi7-%~{D7B2}e
Go%{=$J;0^_
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3967cc619606b37c53d2a62478143374fe950949
GIT binary patch
literal 1188
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3m8Da#=fE;F*!T6L?J0P
zJu}Z%>HY5gN(z}Nwo2iqz6QPp&Z!xh9#uuD!Bu`C$yM3OmMKd1b_zBXRzL%CQ%e#R
zDspr3imfVamB8j&0ofp7eI*63l9Fs&C5WRUd;=7m^NUgyO!bU)lMM_F70k@^3{6bU
z%nWrDj0_Bo^bLT>OxMuF%GAut$Xo#mlz_GsrKDK}xwt{?0`hE?GD=Dctn~HE%ggo3
zjrH=2()A53EiFN27#ZmTRp=I1=9MH?=;jqG!%T2VElw`VEGWs$&r<;L6O-~wOKg>t
zU|z^AfE$}v3=Jk=fazBx7U&!58R#P^^!3HBG&dKny0|1LH4VS&;*iRMRQ;gT;{4L0
zWMIUlDT6c@SUDG^CYIzEh2-bwz(O$~BfliSI3vG6!8zDWK_fgfFD1XcSQD(?*VoE3
zuec;JFFDoI#a0O@qL-POVrArJVs7Z>YUt!>W^QQcYUF5a?q*@?=;-EV<Y;PX=nT{A
zlAm0fo0?Yw)0=|OYlKrTC@Da6rxs<FrKY$Q<>xAZy=;|<(=9G;xZMKLn}XXd1~~QV
z10AD}7SS*vVCn%e;mH=rfhYXbJYZ@s0w(JWf#^yG21W@_7srr_TS<TZ|F>sWZRl)t
za@fe&<8X%ch>^n@=caw<9sUS(%-_Jk`E0Ir`L5kNcQ*3;a9zTfv%Bo=u1D|Q?PGY-
zI^W^gwpMQOeN3Aw8)nR9c*eMr^(EuWR5qdd#viZSc%|QIYiqB5_4;+U)81n}ti?KU
zdwzJ<)cmPY_-%ON=$QcZM&8K%b$??;6)s%9Ed2lXcm2=j=i6KU`}6bgF&4%*-qUm{
z1;oY8*_TPKzTXg0`SQ}zy9d5(l<Nq0{#Z3Z!h>%WTRlUgh+g!zHR3A@xjC9|>@YEY
zWav`%`r2Afg}&|Ezca<0W0O=c%f7ZIhMSwactQPt0pa$Tn3(_kvQ{N}y1KbW!WTN%
opYUx}*ekN!<rw4nUIs=shPl^k8~E4xfXW~SPgg&ebxsLQ0Kkoy4gdfE
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ffe62936c4a8000c0154cf5616eab93e1739694d
GIT binary patch
literal 3061
zc$@+C3kvj!P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU
z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag
z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V
z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H
zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T
zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j
zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p
z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i
z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i
z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf
z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G
zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u
zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm
z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v
zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW
zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo
z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X
zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t
z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl
zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4
z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_
zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l
znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U
zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0
zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O
zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p
z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya?
z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y
zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB
zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt
z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc=
zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C
z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB
zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe
zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0
z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ
zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$
z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4
z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu
zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu
z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E
ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw
zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX
z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i&
z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01
z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R
z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw
zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD
zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3|
zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy
zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z
zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h
z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}
z0003XNkl<Zc-o}X%PNIo6vpx2M(pHFC}lu0Fc%Z%BwPenAQKrGaS5168A{4n3>@x2
zi2)(y7!uJoSzBLUJF%_F`wpJ<?^&lzE|>G4D2sOvt2jmz2GEB#<Zy`t?BFH|$b^G6
zTw@zU0XfHW=&I3<do1EG4Ol@ImAJrWh;BpI7>>qKkA*y>BoD$T-tdm2M6&pAVIFsw
z442=4ENW1J9?YP%K+le`fjSH(OXx&tGz`jdiq`^=M$F;^Q~4>J{9$spctS_AgrcJu
zKm)39l?JS1<QIVc*onO~-~prP{vo*wV|c`F8n7JprZ61cPIi76)1g_+uXHALO#PTZ
zEza-~qpb?t4_J!3PzV6cu_V1{iT$|3{!hnLbbJi}?q*PZlw*0P00000NkvXXu0mjf
D7gnmq
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1fe2a8ffefc523d7366d80c5869ae440f5ac7600
GIT binary patch
literal 3066
zc$@+H3kCFvP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU
z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag
z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V
z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H
zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T
zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j
zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p
z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i
z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i
z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf
z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G
zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u
zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm
z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v
zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW
zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo
z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X
zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t
z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl
zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4
z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_
zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l
znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U
zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0
zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O
zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p
z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya?
z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y
zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB
zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt
z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc=
zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C
z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB
zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe
zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0
z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ
zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$
z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4
z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu
zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu
z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E
ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw
zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX
z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i&
z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01
z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R
z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw
zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD
zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3|
zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy
zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z
zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h
z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}
z0003cNkl<Zc-o}X%PNIo6b9hu*Di{*opLB<GH?O5(ar@pjFq8%2TbGwl-z*af=rk*
zK;}v*1Cq#TM^3fdCTr{O@0YUQ(bu=W^{)3@>s?8`Ue^o?Id{D%ql6(OxW+LKaF0<e
zU^U4om_P+xxWr4`cVIY@_b`Qi%oj4r%Xq?8RD6h7PsC@ih&OydOG<VHk66QQDp{P*
z@P$v*;%h$yWfZZ4^PIj6U<Ko7MJL+QO`OD4$j(C*8=*i{ucZkFagmXo#yd{)0xg<E
zgWx7c)%{<BLySZ({=zi2vjhjxsJU?2Zy3WYp3w(sCzh}jUf9VoQYi}bU_DgJTDcTX
zD~6R{V`S~|#VR&&)JRBjwz08HlQG^JZgGNr+@&8U&93wJ0E#$Gv!F{p{Qv*}07*qo
IM6N<$g5|-pU;qFB
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ddd8a8710be0004a0b5354278b995bc71da06542
GIT binary patch
literal 302
zc$@()0nz@6P)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e0002>Nkl<ZcmZRe
z0$^njV^CwzVNhh?Mq$GN*SW_3cR`TVf<X+4&5ba@zJ102t3Y^@!=Awt#^$tQ;6W&{
zD4+lTJP=+HaA0sQTL57T+8~s$Fc@Xb_<sTj&q_Np*umH`_6)p?P$kR^x?veBHGwQP
z%j^>jVQCPyT{?>`SpF~r!)yL042nSJkH5?eFJSCH%nX)52(&_~HTVDa{~P~r<gH@R
zg0ssR1R;);=_~%f;s2`tt2indv|(({G6sH#wEVR4|11A5{=b-|l0g^FE@j|@2GpSp
zpupV!bD2sR#F5y%h)^(R$Y97}NMYbcVN(qN0FGsWNDIB@Hvj+t07*qoM6N<$g0G;2
ASpWb4
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -31,17 +31,17 @@ body {
 body,
 input,
 button,
 select {
   font: message-box;
 }
 
 .hidden {
-  display: none;
+  display: none !important;
 }
 [hidden] {
   display: none !important;
 }
 
 #viewerContainer:-moz-full-screen {
   top: 0px;
   border-top: 2px solid transparent;
@@ -59,17 +59,16 @@ select {
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
   overflow: hidden;
   cursor: none;
 }
 
-
 :-moz-full-screen .page {
   margin-bottom: 100%;
 }
 
 :fullscreen .page {
   margin-bottom: 100%;
 }
 
@@ -209,17 +208,17 @@ html[dir='rtl'] #sidebarContent {
                     linear-gradient(hsla(0,0%,30%,.99), hsla(0,0%,25%,.95));
   box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25),
 
               inset 0 -1px 0 hsla(0,0%,100%,.05),
               0 1px 0 hsla(0,0%,0%,.15),
               0 0 1px hsla(0,0%,0%,.1);
 }
 
-#toolbarContainer, .findbar {
+#toolbarContainer, .findbar, .secondaryToolbar {
   position: relative;
   height: 32px;
   background-color: #474747; /* fallback */
   background-image: url(images/texture.png),
                     linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08),
               inset 0 1px 1px hsla(0,0%,0%,.15),
               inset 0 -1px 0 hsla(0,0%,100%,.05),
@@ -270,17 +269,17 @@ html[dir='rtl'] #sidebarContent {
 
   background-image: linear-gradient(to right, #999 0%, #fff 50%, #999 100%);
   background-size: 100% 100%;
   background-repeat: no-repeat;
 
   animation: progressIndeterminate 2s linear infinite;
 }
 
-.findbar {
+.findbar, .secondaryToolbar {
   top: 32px;
   position: absolute;
   z-index: 10000;
   height: 32px;
 
   min-width: 16px;
   padding: 0px 6px 0px 6px;
   margin: 4px 2px 4px 2px;
@@ -304,55 +303,81 @@ html[dir='rtl'] .findbar {
 }
 
 #findInput[data-status="pending"] {
   background-image: url(images/loading-small.png);
   background-repeat: no-repeat;
   background-position: right;
 }
 
-.doorHanger {
+.secondaryToolbar {
+  padding: 6px;
+  height: auto;
+  z-index: 30000;
+}
+html[dir='ltr'] .secondaryToolbar {
+  right: 4px;
+}
+html[dir='rtl'] .secondaryToolbar {
+  left: 4px;
+}
+
+#secondaryToolbarButtonContainer {
+  max-width: 200px;
+  max-height: 400px;
+  overflow-y: auto;
+}
+
+.doorHanger,
+.doorHangerRight {
   border: 1px solid hsla(0,0%,0%,.5);
   border-radius: 2px;
   box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
 }
-.doorHanger:after, .doorHanger:before {
+.doorHanger:after, .doorHanger:before,
+.doorHangerRight:after, .doorHangerRight:before {
   bottom: 100%;
   border: solid transparent;
   content: " ";
   height: 0;
   width: 0;
   position: absolute;
   pointer-events: none;
 }
-.doorHanger:after {
+.doorHanger:after,
+.doorHangerRight:after {
   border-bottom-color: hsla(0,0%,32%,.99);
   border-width: 8px;
 }
-.doorHanger:before {
+.doorHanger:before,
+.doorHangerRight:before {
   border-bottom-color: hsla(0,0%,0%,.5);
   border-width: 9px;
 }
 
-html[dir='ltr'] .doorHanger:after {
+html[dir='ltr'] .doorHanger:after,
+html[dir='rtl'] .doorHangerRight:after {
   left: 13px;
   margin-left: -8px;
 }
 
-html[dir='ltr'] .doorHanger:before {
+html[dir='ltr'] .doorHanger:before,
+html[dir='rtl'] .doorHangerRight:before {
   left: 13px;
   margin-left: -9px;
 }
 
-html[dir='rtl'] .doorHanger:after {
+html[dir='rtl'] .doorHanger:after,
+html[dir='ltr'] .doorHangerRight:after {
   right: 13px;
   margin-right: -8px;
 }
 
-html[dir='rtl'] .doorHanger:before {
+html[dir='rtl'] .doorHanger:before,
+html[dir='ltr'] .doorHangerRight:before {
   right: 13px;
   margin-right: -9px;
 }
 
 #findMsg {
   font-style: italic;
   color: #A6B7D0;
 }
@@ -408,31 +433,33 @@ html[dir='ltr'] .splitToolbarButton > .t
   border-radius: 0;
   float: left;
 }
 html[dir='rtl'] .splitToolbarButton > .toolbarButton {
   border-radius: 0;
   float: right;
 }
 
-.toolbarButton {
+.toolbarButton,
+.secondaryToolbarButton {
   border: 0 none;
   background-color: rgba(0, 0, 0, 0);
   width: 32px;
   height: 25px;
 }
 
 .toolbarButton > span {
   display: inline-block;
   width: 0;
   height: 0;
   overflow: hidden;
 }
 
-.toolbarButton[disabled] {
+.toolbarButton[disabled],
+.secondaryToolbarButton[disabled] {
   opacity: .5;
 }
 
 .toolbarButton.group {
   margin-right: 0;
 }
 
 .splitToolbarButton.toggled .toolbarButton {
@@ -483,17 +510,17 @@ html[dir='rtl'] .splitToolbarButton > .t
   margin-left: -1px;
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
   border-left-color: transparent;
 }
 .splitToolbarButtonSeparator {
   padding: 8px 0;
   width: 1px;
-  background-color: hsla(0,0%,00%,.5);
+  background-color: hsla(0,0%,0%,.5);
   z-index: 99;
   box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
   display: inline-block;
   margin: 5px 0;
 }
 html[dir='ltr'] .splitToolbarButtonSeparator {
   float: left;
 }
@@ -506,22 +533,23 @@ html[dir='rtl'] .splitToolbarButtonSepar
   margin: 1px 0;
   box-shadow: 0 0 0 1px hsla(0,0%,100%,.03);
   transition-property: padding;
   transition-duration: 10ms;
   transition-timing-function: ease;
 }
 
 .toolbarButton,
-.dropdownToolbarButton {
+.dropdownToolbarButton,
+.secondaryToolbarButton {
   min-width: 16px;
   padding: 2px 6px 0;
   border: 1px solid transparent;
   border-radius: 2px;
-  color: hsl(0,0%,95%);
+  color: hsla(0,0%,100%,.8);
   font-size: 12px;
   line-height: 14px;
   -moz-user-select: none;
   /* Opera does not support user-select, use <... unselectable="on"> instead */
   cursor: default;
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
   transition-timing-function: ease;
@@ -533,55 +561,60 @@ html[dir='ltr'] .dropdownToolbarButton {
 }
 html[dir='rtl'] .toolbarButton,
 html[dir='rtl'] .dropdownToolbarButton {
   margin: 3px 0 4px 2px;
 }
 
 .toolbarButton:hover,
 .toolbarButton:focus,
-.dropdownToolbarButton {
+.dropdownToolbarButton,
+.secondaryToolbarButton:hover,
+.secondaryToolbarButton:focus {
   background-color: hsla(0,0%,0%,.12);
   background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
   border: 1px solid hsla(0,0%,0%,.35);
   border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
               0 0 1px hsla(0,0%,100%,.15) inset,
               0 1px 0 hsla(0,0%,100%,.05);
 }
 
 .toolbarButton:hover:active,
-.dropdownToolbarButton:hover:active {
+.dropdownToolbarButton:hover:active,
+.secondaryToolbarButton:hover:active {
   background-color: hsla(0,0%,0%,.2);
   background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.4) hsla(0,0%,0%,.45);
   box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset,
               0 0 1px hsla(0,0%,0%,.2) inset,
               0 1px 0 hsla(0,0%,100%,.05);
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 10ms;
   transition-timing-function: linear;
 }
 
 .toolbarButton.toggled,
-.splitToolbarButton.toggled > .toolbarButton.toggled {
+.splitToolbarButton.toggled > .toolbarButton.toggled,
+.secondaryToolbarButton.toggled {
   background-color: hsla(0,0%,0%,.3);
   background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.45) hsla(0,0%,0%,.5);
   box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset,
               0 0 1px hsla(0,0%,0%,.2) inset,
               0 1px 0 hsla(0,0%,100%,.05);
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 10ms;
   transition-timing-function: linear;
 }
 
 .toolbarButton.toggled:hover:active,
-.splitToolbarButton.toggled > .toolbarButton.toggled:hover:active {
+.splitToolbarButton.toggled > .toolbarButton.toggled:hover:active,
+.secondaryToolbarButton.toggled:hover:active {
   background-color: hsla(0,0%,0%,.4);
   border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.5) hsla(0,0%,0%,.55);
   box-shadow: 0 1px 1px hsla(0,0%,0%,.2) inset,
               0 0 1px hsla(0,0%,0%,.3) inset,
               0 1px 0 hsla(0,0%,100%,.05);
 }
 
 .dropdownToolbarButton {
@@ -640,111 +673,119 @@ html[dir='rtl'] .toolbarButton:first-chi
   height: 1px;
 }
 
 .toolbarButtonFlexibleSpacer {
   -moz-box-flex: 1;
   min-width: 30px;
 }
 
-.toolbarButton#sidebarToggle::before {
-  display: inline-block;
-  content: url(images/toolbarButton-sidebarToggle.png);
-}
-
 html[dir='ltr'] #findPrevious {
   margin-left: 3px;
 }
 html[dir='ltr'] #findNext {
   margin-right: 3px;
 }
 
 html[dir='rtl'] #findPrevious {
   margin-right: 3px;
 }
 html[dir='rtl'] #findNext {
   margin-left: 3px;
 }
-.toolbarButton::before {
+
+.toolbarButton::before,
+.secondaryToolbarButton::before {
   /* All matching images have a size of 16x16
    * (except for the print button: 18x16)
    * All relevant containers have a size of 32x25 */
   position: absolute;
+  display: inline-block;
   top: 4px;
   left: 7px;
 }
+html[dir="ltr"] .secondaryToolbarButton::before {
+  left: 4px;
+}
+html[dir="rtl"] .secondaryToolbarButton::before {
+  right: 4px;
+}
+
+.toolbarButton#sidebarToggle::before {
+  content: url(images/toolbarButton-sidebarToggle.png);
+}
+
+.toolbarButton#secondaryToolbarToggle::before {
+  content: url(images/toolbarButton-secondaryToolbarToggle.png);
+}
 
 html[dir='ltr'] .toolbarButton.findPrevious::before {
-  display: inline-block;
   content: url(images/findbarButton-previous.png);
 }
 
 html[dir='rtl'] .toolbarButton.findPrevious::before {
-  display: inline-block;
   content: url(images/findbarButton-previous-rtl.png);
 }
 
 html[dir='ltr'] .toolbarButton.findNext::before {
-  display: inline-block;
   content: url(images/findbarButton-next.png);
 }
 
 html[dir='rtl'] .toolbarButton.findNext::before {
-  display: inline-block;
   content: url(images/findbarButton-next-rtl.png);
 }
 
 html[dir='ltr'] .toolbarButton.pageUp::before {
-  display: inline-block;
   content: url(images/toolbarButton-pageUp.png);
 }
 
 html[dir='rtl'] .toolbarButton.pageUp::before {
-  display: inline-block;
   content: url(images/toolbarButton-pageUp-rtl.png);
 }
 
 html[dir='ltr'] .toolbarButton.pageDown::before {
-  display: inline-block;
   content: url(images/toolbarButton-pageDown.png);
 }
 
 html[dir='rtl'] .toolbarButton.pageDown::before {
-  display: inline-block;
   content: url(images/toolbarButton-pageDown-rtl.png);
 }
 
 .toolbarButton.zoomOut::before {
-  display: inline-block;
   content: url(images/toolbarButton-zoomOut.png);
 }
 
 .toolbarButton.zoomIn::before {
-  display: inline-block;
   content: url(images/toolbarButton-zoomIn.png);
 }
 
-.toolbarButton.presentationMode::before {
-  display: inline-block;
+.toolbarButton.presentationMode::before,
+.secondaryToolbarButton.presentationMode::before {
   content: url(images/toolbarButton-presentationMode.png);
 }
 
-.toolbarButton.print::before {
-  display: inline-block;
+.toolbarButton.print::before,
+.secondaryToolbarButton.print::before {
   content: url(images/toolbarButton-print.png);
   left: 6px;
 }
+html[dir="ltr"] .secondaryToolbarButton.print::before {
+  left: 3px;
+}
+html[dir="rtl"] .secondaryToolbarButton.print::before {
+  right: 3px;
+}
 
-.toolbarButton.openFile::before {
-  display: inline-block;
+.toolbarButton.openFile::before,
+.secondaryToolbarButton.openFile::before {
   content: url(images/toolbarButton-openFile.png);
 }
 
-.toolbarButton.download::before {
-  display: inline-block;
+.toolbarButton.download::before,
+.secondaryToolbarButton.download::before {
   content: url(images/toolbarButton-download.png);
 }
 
 .toolbarButton.bookmark {
   -moz-box-sizing: border-box;
   box-sizing: border-box;
   margin-top: 3px;
   padding-top: 4px;
@@ -755,30 +796,96 @@ html[dir='rtl'] .toolbarButton.pageDown:
   pointer-events: none;
 }
 
 .toolbarButton.bookmark::before {
   content: url(images/toolbarButton-bookmark.png);
 }
 
 #viewThumbnail.toolbarButton::before {
-  display: inline-block;
   content: url(images/toolbarButton-viewThumbnail.png);
 }
 
 #viewOutline.toolbarButton::before {
-  display: inline-block;
   content: url(images/toolbarButton-viewOutline.png);
 }
 
 #viewFind.toolbarButton::before {
-  display: inline-block;
   content: url(images/toolbarButton-search.png);
 }
 
+.secondaryToolbarButton {
+  position: relative;
+  margin: 0 0 4px 0;
+  padding: 3px 0 1px 0;
+  height: auto;
+  min-height: 25px;
+  width: auto;
+  min-width: 100%;
+  white-space: normal;
+}
+html[dir="ltr"] .secondaryToolbarButton {
+  padding-left: 24px;
+  text-align: left;
+}
+html[dir="rtl"] .secondaryToolbarButton {
+  padding-right: 24px;
+  text-align: right;
+}
+
+#secondaryToolbarButtonContainer :last-child {
+  margin-bottom: 0;
+}
+
+html[dir="ltr"] .secondaryToolbarButton > span {
+  padding-right: 4px;
+}
+html[dir="rtl"] .secondaryToolbarButton > span {
+  padding-left: 4px;
+}
+
+.secondaryToolbarButton.firstPage::before {
+  content: url(images/secondaryToolbarButton-firstPage.png);
+}
+
+.secondaryToolbarButton.lastPage::before {
+  content: url(images/secondaryToolbarButton-lastPage.png);
+}
+
+.secondaryToolbarButton.rotateCcw::before {
+  content: url(images/secondaryToolbarButton-rotateCcw.png);
+}
+
+.secondaryToolbarButton.rotateCw::before {
+  content: url(images/secondaryToolbarButton-rotateCw.png);
+}
+
+.verticalToolbarSeparator {
+  display: block;
+  padding: 8px 0;
+  margin: 8px 4px;
+  width: 1px;
+  background-color: hsla(0,0%,0%,.5);
+  box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
+}
+html[dir='ltr'] .verticalToolbarSeparator {
+  margin-left: 2px;
+}
+html[dir='rtl'] .verticalToolbarSeparator {
+  margin-right: 2px;
+}
+
+.horizontalToolbarSeparator {
+  display: block; 
+  margin: 0 0 4px 0;
+  height: 1px;
+  width: 100%;
+  background-color: hsla(0,0%,0%,.5);
+  box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
+}
 
 .toolbarField {
   padding: 3px 6px;
   margin: 4px 0 4px 0;
   border: 1px solid transparent;
   border-radius: 2px;
   background-color: hsla(0,0%,100%,.09);
   background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
@@ -994,22 +1101,24 @@ canvas {
   position: relative;
   overflow: visible;
   border: 9px solid transparent;
   background-clip: content-box;
   border-image: url(images/shadow.png) 9 9 repeat;
   background-color: white;
 }
 
-.page > a {
+.page > a,
+.annotationLayer > a {
   display: block;
   position: absolute;
 }
 
-.page > a:hover {
+.page > a:hover,
+.annotationLayer > a:hover {
   opacity: 0.2;
   background: #ff0;
   box-shadow: 0px 2px 10px #ff0;
 }
 
 .loadingIcon {
   position: absolute;
   display: block;
@@ -1080,17 +1189,16 @@ canvas {
 }
 
 .annotText > img {
   position: absolute;
   opacity: 0.6;
 }
 
 .annotText > img:hover {
-  cursor: pointer;
   opacity: 1;
 }
 
 .annotText > div > h1 {
   font-size: 1.2em;
   border-bottom: 1px solid #000000;
   margin: 0px;
 }
@@ -1204,17 +1312,17 @@ canvas {
 
 @media print {
   /* General rules for printing. */
   body {
     background: transparent none;
   }
 
   /* Rules for browsers that don't support mozPrintCallback. */
-  #sidebarContainer, .toolbar, #loadingBox, #errorWrapper, .textLayer {
+  #sidebarContainer, #secondaryToolbar, .toolbar, #loadingBox, #errorWrapper, .textLayer {
     display: none;
   }
   #viewerContainer {
     overflow: visible;
   }
 
   #mainContainer, #viewerContainer, .page, .page canvas {
     position: static;
@@ -1242,26 +1350,50 @@ canvas {
   }
   #printContainer canvas {
     position: relative;
     top: 0;
     left: 0;
   }
 }
 
-@media all and (max-width: 950px) {
+.visibleLargeView,
+.visibleMediumView,
+.visibleSmallView {
+  display: none;
+}
+
+@media all and (max-width: 960px) {
   html[dir='ltr'] #outerContainer.sidebarMoving .outerCenter,
   html[dir='ltr'] #outerContainer.sidebarOpen .outerCenter {
     float: left;
-    left: 180px;
+    left: 185px;
   }
   html[dir='rtl'] #outerContainer.sidebarMoving .outerCenter,
   html[dir='rtl'] #outerContainer.sidebarOpen .outerCenter {
     float: right;
-    right: 180px;
+    right: 185px;
+  }
+}
+
+@media all and (max-width: 900px) {
+  .sidebarOpen .hiddenLargeView {
+    display: none;
+  }
+  .sidebarOpen .visibleLargeView {
+    display: inherit;
+  }
+}
+
+@media all and (max-width: 860px) {
+  .sidebarOpen .hiddenMediumView {
+    display: none;
+  }
+  .sidebarOpen .visibleMediumView {
+    display: inherit;
   }
 }
 
 @media all and (max-width: 770px) {
   #sidebarContainer {
     top: 32px;
     z-index: 100;
   }
@@ -1277,37 +1409,71 @@ canvas {
     left: 0px;
   }
   html[dir='rtl'] #outerContainer.sidebarOpen > #mainContainer {
     right: 0px;
   }
 
   html[dir='ltr'] .outerCenter {
     float: left;
-    left: 180px;
+    left: 185px;
   }
   html[dir='rtl'] .outerCenter {
     float: right;
-    right: 180px;
+    right: 185px;
+  }
+
+  #outerContainer .hiddenLargeView,
+  #outerContainer .hiddenMediumView {
+    display: inherit;
+  }
+  #outerContainer .visibleLargeView,
+  #outerContainer .visibleMediumView {
+    display: none;
+  }
+}
+
+@media all and (max-width: 700px) {
+  #outerContainer .hiddenLargeView {
+    display: none;
+  }
+  #outerContainer .visibleLargeView {
+    display: inherit;
+  }
+}
+
+@media all and (max-width: 660px) {
+  #outerContainer .hiddenMediumView {
+    display: none;
+  }
+  #outerContainer .visibleMediumView {
+    display: inherit;
   }
 }
 
 @media all and (max-width: 600px) {
   .hiddenSmallView {
     display: none;
   }
+  .visibleSmallView {
+    display: inherit;
+  }
+  html[dir='ltr'] #outerContainer.sidebarMoving .outerCenter,
+  html[dir='ltr'] #outerContainer.sidebarOpen .outerCenter,
   html[dir='ltr'] .outerCenter {
     left: 156px;
   }
-  html[dir='rtr'] .outerCenter {
+  html[dir='rtl'] #outerContainer.sidebarMoving .outerCenter,
+  html[dir='rtl'] #outerContainer.sidebarOpen .outerCenter,
+  html[dir='rtl'] .outerCenter {
     right: 156px;
   }
   .toolbarButtonSpacer {
     width: 0;
   }
 }
 
-@media all and (max-width: 500px) {
+@media all and (max-width: 510px) {
   #scaleSelectContainer, #pageNumberLabel {
     display: none;
   }
 }
 
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -58,32 +58,71 @@ limitations under the License.
           <div id="outlineView" class="hidden">
           </div>
         </div>
       </div>  <!-- sidebarContainer -->
 
       <div id="mainContainer">
         <div class="findbar hidden doorHanger hiddenSmallView" id="findbar">
           <label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
-          <input id="findInput" class="toolbarField" tabindex="21">
+          <input id="findInput" class="toolbarField" tabindex="41">
           <div class="splitToolbarButton">
-            <button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="22" data-l10n-id="find_previous">
+            <button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="42" data-l10n-id="find_previous">
               <span data-l10n-id="find_previous_label">Previous</span>
             </button>
             <div class="splitToolbarButtonSeparator"></div>
-            <button class="toolbarButton findNext" title="" id="findNext" tabindex="23" data-l10n-id="find_next">
+            <button class="toolbarButton findNext" title="" id="findNext" tabindex="43" data-l10n-id="find_next">
               <span data-l10n-id="find_next_label">Next</span>
             </button>
           </div>
           <input type="checkbox" id="findHighlightAll" class="toolbarField">
-          <label for="findHighlightAll" class="toolbarLabel" tabindex="24" data-l10n-id="find_highlight">Highlight all</label>
+          <label for="findHighlightAll" class="toolbarLabel" tabindex="44" data-l10n-id="find_highlight">Highlight all</label>
           <input type="checkbox" id="findMatchCase" class="toolbarField">
-          <label for="findMatchCase" class="toolbarLabel" tabindex="25" data-l10n-id="find_match_case_label">Match case</label>
+          <label for="findMatchCase" class="toolbarLabel" tabindex="45" data-l10n-id="find_match_case_label">Match case</label>
           <span id="findMsg" class="toolbarLabel"></span>
-        </div>
+        </div>  <!-- findbar -->
+
+        <div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
+          <div id="secondaryToolbarButtonContainer">
+            <button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="18" data-l10n-id="presentation_mode">
+              <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
+            </button>
+
+            <button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="19" data-l10n-id="open_file">
+              <span data-l10n-id="open_file_label">Open</span>
+            </button>
+
+            <button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="20" data-l10n-id="print">
+              <span data-l10n-id="print_label">Print</span>
+            </button>
+
+            <button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="21" data-l10n-id="download">
+              <span data-l10n-id="download_label">Download</span>
+            </button>
+
+            <div class="horizontalToolbarSeparator visibleLargeView"></div>
+
+            <button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="22" data-l10n-id="first_page">
+              <span data-l10n-id="first_page_label">Go to First Page</span>
+            </button>
+            <button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="23" data-l10n-id="last_page">
+              <span data-l10n-id="last_page_label">Go to Last Page</span>
+            </button>
+
+            <div class="horizontalToolbarSeparator"></div>
+
+            <button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="24" data-l10n-id="page_rotate_cw">
+              <span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
+            </button>
+            <button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="25" data-l10n-id="page_rotate_ccw">
+              <span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
+            </button>
+          </div>
+        </div>  <!-- secondaryToolbar -->
+
         <div class="toolbar">
           <div id="toolbarContainer">
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
                 <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_sidebar">
                   <span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
                 </button>
                 <div class="toolbarButtonSpacer"></div>
@@ -100,33 +139,39 @@ limitations under the License.
                   </button>
                 </div>
                 <label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
                 <input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="8">
                 </input>
                 <span id="numPages" class="toolbarLabel"></span>
               </div>
               <div id="toolbarViewerRight">
-                <button id="presentationMode" class="toolbarButton presentationMode hiddenSmallView" title="Switch to Presentation Mode" tabindex="12" data-l10n-id="presentation_mode">
+                <button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="12" data-l10n-id="presentation_mode">
                   <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
                 </button>
 
-                <button id="openFile" class="toolbarButton openFile hiddenSmallView" title="Open File" tabindex="13" data-l10n-id="open_file">
-                   <span data-l10n-id="open_file_label">Open</span>
+                <button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="13" data-l10n-id="open_file">
+                  <span data-l10n-id="open_file_label">Open</span>
                 </button>
 
-                <button id="print" class="toolbarButton print" title="Print" tabindex="14" data-l10n-id="print">
+                <button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="14" data-l10n-id="print">
                   <span data-l10n-id="print_label">Print</span>
                 </button>
 
-                <button id="download" class="toolbarButton download" title="Download" tabindex="15" data-l10n-id="download">
+                <button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="15" data-l10n-id="download">
                   <span data-l10n-id="download_label">Download</span>
                 </button>
                 <!-- <div class="toolbarButtonSpacer"></div> -->
                 <a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="16" data-l10n-id="bookmark"><span data-l10n-id="bookmark_label">Current View</span></a>
+
+                <div class="verticalToolbarSeparator hiddenSmallView"></div>
+                
+                <button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="17" data-l10n-id="tools">
+                  <span data-l10n-id="tools_label">Tools</span>
+                </button> 
               </div>
               <div class="outerCenter">
                 <div class="innerCenter" id="toolbarViewerMiddle">
                   <div class="splitToolbarButton">
                     <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="9" data-l10n-id="zoom_out">
                       <span data-l10n-id="zoom_out_label">Zoom Out</span>
                     </button>
                     <div class="splitToolbarButtonSeparator"></div>
@@ -157,28 +202,28 @@ limitations under the License.
                 <div class="glimmer">
                 </div>
               </div>
             </div>
           </div>
         </div>
 
         <menu type="context" id="viewerContextMenu">
-          <menuitem id="firstPage" label="First Page"
-                    data-l10n-id="first_page" ></menuitem>
-          <menuitem id="lastPage" label="Last Page"
-                    data-l10n-id="last_page" ></menuitem>
-          <menuitem id="pageRotateCcw" label="Rotate Counter-Clockwise"
-                    data-l10n-id="page_rotate_ccw" ></menuitem>
-          <menuitem id="pageRotateCw" label="Rotate Clockwise"
-                    data-l10n-id="page_rotate_cw" ></menuitem>
+          <menuitem id="contextFirstPage" label="First Page"
+                    data-l10n-id="first_page"></menuitem>
+          <menuitem id="contextLastPage" label="Last Page"
+                    data-l10n-id="last_page"></menuitem>
+          <menuitem id="contextPageRotateCw" label="Rotate Clockwise"
+                    data-l10n-id="page_rotate_cw"></menuitem>
+          <menuitem id="contextPageRotateCcw" label="Rotate Counter-Clockwise"
+                    data-l10n-id="page_rotate_ccw"></menuitem>
         </menu>
 
       <div id="viewerContainer">   
-          <div id="viewer" contextmenu="viewerContextMenu"></div>
+          <div id="viewer"></div>
         </div>
 
         <div id="errorWrapper" hidden='true'>
           <div id="errorMessageLeft">
             <span id="errorMessage"></span>
             <button id="errorShowMore" data-l10n-id="error_more_info">
               More Information
             </button>
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -12,17 +12,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
            PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
            getFileName, getOutputScale, scrollIntoView, getPDFFileNameFromURL,
-           PDFHistory, ThumbnailView, noContextMenuHandler */
+           PDFHistory, ThumbnailView, noContextMenuHandler, SecondaryToolbar */
 
 'use strict';
 
 var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
 var DEFAULT_SCALE = 'auto';
 var DEFAULT_SCALE_DELTA = 1.1;
 var UNKNOWN_SCALE = 0;
 var CACHE_SIZE = 20;
@@ -425,18 +425,16 @@ var Settings = (function SettingsClosure
   };
 
   return Settings;
 })();
 
 var cache = new Cache(CACHE_SIZE);
 var currentPageNumber = 1;
 
-// TODO: Enable the FindBar *AFTER* the pagesPromise in the load function
-// got resolved
 
 /* globals PDFFindController, FindStates, mozL10n */
 
 /**
  * Creates a "search bar" given set of DOM elements
  * that act as controls for searching, or for setting
  * search preferences in the UI. This object also sets
  * up the appropriate events for the controls. Actual
@@ -634,16 +632,18 @@ var PDFFindController = {
   dirtyMatch: false,
 
   findTimeout: null,
 
   pdfPageSource: null,
 
   integratedFind: false,
 
+  firstPagePromise: new PDFJS.Promise(),
+
   initialize: function(options) {
     if(typeof PDFFindBar === 'undefined' || PDFFindBar === null) {
         throw 'PDFFindController cannot be initialized ' +
             'without a PDFFindController instance';
     }
 
     this.pdfPageSource = options.pdfPageSource;
     this.integratedFind = options.integratedFind;
@@ -742,25 +742,27 @@ var PDFFindController = {
 
   handleEvent: function(e) {
     if (this.state === null || e.type !== 'findagain') {
       this.dirtyMatch = true;
     }
     this.state = e.detail;
     this.updateUIState(FindStates.FIND_PENDING);
 
-    this.extractText();
-
-    clearTimeout(this.findTimeout);
-    if (e.type === 'find') {
-      // Only trigger the find action after 250ms of silence.
-      this.findTimeout = setTimeout(this.nextMatch.bind(this), 250);
-    } else {
-      this.nextMatch();
-    }
+    this.firstPagePromise.then(function() {
+      this.extractText();
+
+      clearTimeout(this.findTimeout);
+      if (e.type === 'find') {
+        // Only trigger the find action after 250ms of silence.
+        this.findTimeout = setTimeout(this.nextMatch.bind(this), 250);
+      } else {
+        this.nextMatch();
+      }
+    }.bind(this));
   },
 
   updatePage: function(idx) {
     var page = this.pdfPageSource.pages[idx];
 
     if (this.selected.pageIdx === idx) {
       // If the page is selected, scroll the page into view, which triggers
       // rendering the page, which adds the textLayer. Once the textLayer is
@@ -1136,16 +1138,32 @@ var PDFHistory = {
       this._pushToHistory(params, true);
     }
   },
 
   _getPreviousParams: function pdfHistory_getPreviousParams(onlyCheckPage,
                                                             beforeUnload) {
     if (!(this.currentBookmark && this.currentPage)) {
       return null;
+    } else if (this.updatePreviousBookmark) {
+      this.updatePreviousBookmark = false;
+    }
+    if (this.uid > 0 && !(this.previousBookmark && this.previousPage)) {
+      // Prevent the history from getting stuck in the current state,
+      // effectively preventing the user from going back/forward in the history.
+      //
+      // This happens if the current position in the document didn't change when
+      // the history was previously updated. The reasons for this are either:
+      // 1. The current zoom value is such that the document does not need to,
+      //    or cannot, be scrolled to display the destination.
+      // 2. The previous destination is broken, and doesn't actally point to a
+      //    position within the document.
+      //    (This is either due to a bad PDF generator, or the user making a
+      //     mistake when entering a destination in the hash parameters.)
+      return null;
     }
     if ((!this.current.dest && !onlyCheckPage) || beforeUnload) {
       if (this.previousBookmark === this.currentBookmark) {
         return null;
       }
     } else if (this.current.page || onlyCheckPage) {
       if (this.previousPage === this.currentPage) {
         return null;
@@ -1170,17 +1188,18 @@ var PDFHistory = {
       return;
     }
     if (!params.hash && params.page) {
       params.hash = ('page=' + params.page);
     }
     if (addPrevious && !overwrite) {
       var previousParams = this._getPreviousParams();
       if (previousParams) {
-        var replacePrevious = (this.current.hash !== this.previousHash);
+        var replacePrevious = (!this.current.dest &&
+                               this.current.hash !== this.previousHash);
         this._pushToHistory(previousParams, false, replacePrevious);
       }
     }
     if (overwrite || this.uid === 0) {
       window.history.replaceState(this._stateObj(params), '');
     } else {
       window.history.pushState(this._stateObj(params), '');
     }
@@ -1243,16 +1262,137 @@ var PDFHistory = {
       } else if (direction === 1 && state && state.uid < (this.uid - 1)) {
         window.history.forward();
       }
     }
   }
 };
 
 
+var SecondaryToolbar = {
+  opened: false,
+  previousContainerHeight: null,
+  newContainerHeight: null,
+
+  initialize: function secondaryToolbarInitialize(options) {
+    this.toolbar = options.toolbar;
+    this.toggleButton = options.toggleButton;
+
+    this.buttonContainer = this.toolbar.firstElementChild;
+
+    // Define the toolbar buttons.
+    this.presentationMode = options.presentationMode;
+    this.openFile = options.openFile;
+    this.print = options.print;
+    this.download = options.download;
+    this.firstPage = options.firstPage;
+    this.lastPage = options.lastPage;
+    this.pageRotateCw = options.pageRotateCw;
+    this.pageRotateCcw = options.pageRotateCcw;
+
+    // Attach the event listeners.
+    this.toggleButton.addEventListener('click', this.toggle.bind(this));
+
+    this.presentationMode.addEventListener('click',
+      this.presentationModeClick.bind(this));
+    this.openFile.addEventListener('click', this.openFileClick.bind(this));
+    this.print.addEventListener('click', this.printClick.bind(this));
+    this.download.addEventListener('click', this.downloadClick.bind(this));
+
+    this.firstPage.addEventListener('click', this.firstPageClick.bind(this));
+    this.lastPage.addEventListener('click', this.lastPageClick.bind(this));
+
+    this.pageRotateCw.addEventListener('click',
+      this.pageRotateCwClick.bind(this));
+    this.pageRotateCcw.addEventListener('click',
+      this.pageRotateCcwClick.bind(this));
+  },
+
+  // Event handling functions.
+  presentationModeClick: function secondaryToolbarPresentationModeClick(evt) {
+    PDFView.presentationMode();
+    this.close();
+  },
+
+  openFileClick: function secondaryToolbarOpenFileClick(evt) {
+    document.getElementById('fileInput').click();
+    this.close(evt.target);
+  },
+
+  printClick: function secondaryToolbarPrintClick(evt) {
+    window.print();
+    this.close(evt.target);
+  },
+
+  downloadClick: function secondaryToolbarDownloadClick(evt) {
+    PDFView.download();
+    this.close(evt.target);
+  },
+
+  firstPageClick: function secondaryToolbarFirstPageClick(evt) {
+    PDFView.page = 1;
+  },
+
+  lastPageClick: function secondaryToolbarLastPageClick(evt) {
+    PDFView.page = PDFView.pdfDocument.numPages;
+  },
+
+  pageRotateCwClick: function secondaryToolbarPageRotateCwClick(evt) {
+    PDFView.rotatePages(90);
+  },
+
+  pageRotateCcwClick: function secondaryToolbarPageRotateCcwClick(evt) {
+    PDFView.rotatePages(-90);
+  },
+
+  // Misc. functions for interacting with the toolbar.
+  setMaxHeight: function secondaryToolbarSetMaxHeight(container) {
+    this.newContainerHeight = container.clientHeight;
+    if (this.previousContainerHeight === this.newContainerHeight) {
+      return;
+    }
+    this.buttonContainer.setAttribute('style',
+      'max-height: ' + (this.newContainerHeight - SCROLLBAR_PADDING) + 'px;');
+    this.previousContainerHeight = this.newContainerHeight;
+  },
+
+  open: function secondaryToolbarOpen() {
+    if (this.opened) {
+      return;
+    }
+    this.opened = true;
+    this.toggleButton.classList.add('toggled');
+    this.toolbar.classList.remove('hidden');
+  },
+
+  close: function secondaryToolbarClose(target) {
+    if (!this.opened) {
+      return;
+    } else if (target && !this.toolbar.contains(target)) {
+      return;
+    }
+    this.opened = false;
+    this.toolbar.classList.add('hidden');
+    this.toggleButton.classList.remove('toggled');
+  },
+
+  toggle: function secondaryToolbarToggle() {
+    if (this.opened) {
+      this.close();
+    } else {
+      this.open();
+    }
+  },
+
+  get isOpen() {
+    return this.opened;
+  }
+};
+
+
 var PDFView = {
   pages: [],
   thumbnails: [],
   currentScale: UNKNOWN_SCALE,
   currentScaleValue: null,
   initialBookmark: document.location.hash.substring(1),
   container: null,
   thumbnailContainer: null,
@@ -1279,31 +1419,44 @@ var PDFView = {
     this.watchScroll(container, this.pageViewScroll, updateViewarea);
 
     var thumbnailContainer = this.thumbnailContainer =
                              document.getElementById('thumbnailView');
     this.thumbnailViewScroll = {};
     this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
                      this.renderHighestPriority.bind(this));
 
+    SecondaryToolbar.initialize({
+      toolbar: document.getElementById('secondaryToolbar'),
+      toggleButton: document.getElementById('secondaryToolbarToggle'),
+      presentationMode: document.getElementById('secondaryPresentationMode'),
+      openFile: document.getElementById('secondaryOpenFile'),
+      print: document.getElementById('secondaryPrint'),
+      download: document.getElementById('secondaryDownload'),
+      firstPage: document.getElementById('firstPage'),
+      lastPage: document.getElementById('lastPage'),
+      pageRotateCw: document.getElementById('pageRotateCw'),
+      pageRotateCcw: document.getElementById('pageRotateCcw')
+    });
+
     PDFFindBar.initialize({
-        bar: document.getElementById('findbar'),
-        toggleButton: document.getElementById('viewFind'),
-        findField: document.getElementById('findInput'),
-        highlightAllCheckbox: document.getElementById('findHighlightAll'),
-        caseSensitiveCheckbox: document.getElementById('findMatchCase'),
-        findMsg: document.getElementById('findMsg'),
-        findStatusIcon: document.getElementById('findStatusIcon'),
-        findPreviousButton: document.getElementById('findPrevious'),
-        findNextButton: document.getElementById('findNext')
+      bar: document.getElementById('findbar'),
+      toggleButton: document.getElementById('viewFind'),
+      findField: document.getElementById('findInput'),
+      highlightAllCheckbox: document.getElementById('findHighlightAll'),
+      caseSensitiveCheckbox: document.getElementById('findMatchCase'),
+      findMsg: document.getElementById('findMsg'),
+      findStatusIcon: document.getElementById('findStatusIcon'),
+      findPreviousButton: document.getElementById('findPrevious'),
+      findNextButton: document.getElementById('findNext')
     });
 
     PDFFindController.initialize({
-        pdfPageSource: this,
-        integratedFind: this.supportsIntegratedFind
+      pdfPageSource: this,
+      integratedFind: this.supportsIntegratedFind
     });
 
     this.initialized = true;
     container.addEventListener('scroll', function() {
       self.lastScroll = Date.now();
     }, false);
   },
 
@@ -1386,27 +1539,33 @@ var PDFView = {
         scale = Math.min(1.0, pageWidthScale);
         break;
     }
     this.setScale(scale, resetAutoSettings, noScroll);
 
     selectScaleOption(value);
   },
 
-  zoomIn: function pdfViewZoomIn() {
-    var newScale = (this.currentScale * DEFAULT_SCALE_DELTA).toFixed(2);
-    newScale = Math.ceil(newScale * 10) / 10;
-    newScale = Math.min(MAX_SCALE, newScale);
+  zoomIn: function pdfViewZoomIn(ticks) {
+    var newScale = this.currentScale;
+    do {
+      newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
+      newScale = Math.ceil(newScale * 10) / 10;
+      newScale = Math.min(MAX_SCALE, newScale);
+    } while (--ticks && newScale < MAX_SCALE);
     this.parseScale(newScale, true);
   },
 
-  zoomOut: function pdfViewZoomOut() {
-    var newScale = (this.currentScale / DEFAULT_SCALE_DELTA).toFixed(2);
-    newScale = Math.floor(newScale * 10) / 10;
-    newScale = Math.max(MIN_SCALE, newScale);
+  zoomOut: function pdfViewZoomOut(ticks) {
+    var newScale = this.currentScale;
+    do {
+      newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
+      newScale = Math.floor(newScale * 10) / 10;
+      newScale = Math.max(MIN_SCALE, newScale);
+    } while (--ticks && newScale > MIN_SCALE);
     this.parseScale(newScale, true);
   },
 
   set page(val) {
     var pages = this.pages;
     var input = document.getElementById('pageNumber');
     var event = document.createEvent('UIEvents');
     event.initUIEvent('pagechange', false, false, window, 0);
@@ -1682,17 +1841,17 @@ var PDFView = {
 
     if (!this.pdfDocument) { // the PDF is not ready yet
       noData();
       return;
     }
 
     this.pdfDocument.getData().then(
       function getDataSuccess(data) {
-        var blob = PDFJS.createBlob(data.buffer, 'application/pdf');
+        var blob = PDFJS.createBlob(data, 'application/pdf');
         downloadManager.download(blob, url, filename);
       },
       noData // Error occurred try downloading with just the url.
     ).then(null, noData);
   },
 
   fallback: function pdfViewFallback() {
     // Only trigger the fallback once so we don't spam the user with messages
@@ -1934,16 +2093,18 @@ var PDFView = {
 
           if (self.pendingRefStr && self.pendingRefStr === refStr) {
             self.pendingRefStrLoaded.resolve();
           }
         });
         pagePromises.push(pagePromise);
       }
 
+      PDFFindController.firstPagePromise.resolve();
+
       PDFJS.Promise.all(pagePromises).then(function(pages) {
         pagesPromise.resolve(pages);
       });
     });
 
     var storePromise = store.initializedPromise;
     PDFJS.Promise.all([firstPagePromise, storePromise]).then(function() {
       var storedHash = null;
@@ -2265,26 +2426,18 @@ var PDFView = {
 
         if (outlineButton.getAttribute('disabled'))
           return;
         break;
     }
   },
 
   getVisiblePages: function pdfViewGetVisiblePages() {
-    if (!this.isPresentationMode) {
-      return this.getVisibleElements(this.container, this.pages, true);
-    } else {
-      // The algorithm in getVisibleElements is broken in presentation mode.
-      var visible = [], page = this.page;
-      var currentPage = this.pages[page - 1];
-      visible.push({ id: currentPage.id, view: currentPage });
-
-      return { first: currentPage, last: currentPage, views: visible};
-    }
+    return this.getVisibleElements(this.container, this.pages,
+                                   !this.isPresentationMode);
   },
 
   getVisibleThumbs: function pdfViewGetVisibleThumbs() {
     return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
   },
 
   // Generic helper to find out what elements are visible within a scroll pane.
   getVisibleElements: function pdfViewGetVisibleElements(
@@ -2413,26 +2566,32 @@ var PDFView = {
     return true;
   },
 
   enterPresentationMode: function pdfViewEnterPresentationMode() {
     this.isPresentationMode = true;
     this.page = this.presentationModeArgs.page;
     this.parseScale('page-fit', true);
     this.showPresentationControls();
+
+    var viewer = document.getElementById('viewer');
+    viewer.setAttribute('contextmenu', 'viewerContextMenu');
   },
 
   exitPresentationMode: function pdfViewExitPresentationMode() {
     this.isPresentationMode = false;
     this.parseScale(this.presentationModeArgs.previousScale);
     this.page = this.page;
     this.clearMouseScrollState();
     this.hidePresentationControls();
     this.presentationModeArgs = null;
 
+    var viewer = document.getElementById('viewer');
+    viewer.removeAttribute('contextmenu');
+
     // Ensure that the thumbnail of the current page is visible
     // when exiting presentation mode.
     scrollIntoView(document.getElementById('thumbnailContainer' + this.page));
   },
 
   showPresentationControls: function pdfViewShowPresentationControls() {
     var DELAY_BEFORE_HIDING_CONTROLS = 3000;
     var wrapper = document.getElementById('viewerContainer');
@@ -2563,16 +2722,18 @@ var PageView = function pageView(contain
   this.pdfPageRotate = defaultViewport.rotate;
 
   this.renderingState = RenderingStates.INITIAL;
   this.resume = null;
 
   this.textContent = null;
   this.textLayer = null;
 
+  this.annotationLayer = null;
+
   var anchor = document.createElement('a');
   anchor.name = '' + this.id;
 
   var div = this.el = document.createElement('div');
   div.id = 'pageContainer' + this.id;
   div.className = 'page';
   div.style.width = Math.floor(this.viewport.width) + 'px';
   div.style.height = Math.floor(this.viewport.height) + 'px';
@@ -2616,16 +2777,18 @@ var PageView = function pageView(contain
 
     div.style.width = Math.floor(this.viewport.width) + 'px';
     div.style.height = Math.floor(this.viewport.height) + 'px';
 
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
     div.removeAttribute('data-loaded');
 
+    this.annotationLayer = null;
+
     delete this.canvas;
 
     this.loadingIconDiv = document.createElement('div');
     this.loadingIconDiv.className = 'loadingIcon';
     div.appendChild(this.loadingIconDiv);
   };
 
   Object.defineProperty(this, 'width', {
@@ -2637,29 +2800,32 @@ var PageView = function pageView(contain
 
   Object.defineProperty(this, 'height', {
     get: function PageView_getHeight() {
       return this.viewport.height;
     },
     enumerable: true
   });
 
-  function setupAnnotations(annotationsDiv, pdfPage, viewport) {
+  var self = this;
+
+  function setupAnnotations(pageDiv, pdfPage, viewport) {
 
     function bindLink(link, dest) {
       link.href = PDFView.getDestinationHash(dest);
       link.onclick = function pageViewSetupLinksOnclick() {
         if (dest)
           PDFView.navigateTo(dest);
         return false;
       };
       link.className = 'internalLink';
     }
 
     function bindNamedAction(link, action) {
+      link.href = PDFView.getAnchorUrl('');
       link.onclick = function pageViewSetupNamedActionOnClick() {
         // See PDF reference, table 8.45 - Named action
         switch (action) {
           case 'GoToPage':
             document.getElementById('pageNumber').focus();
             break;
 
           case 'GoBack':
@@ -2696,16 +2862,22 @@ var PageView = function pageView(contain
             break; // No action according to spec
         }
         return false;
       };
       link.className = 'internalLink';
     }
 
     pdfPage.getAnnotations().then(function(annotationsData) {
+      if (self.annotationLayer) {
+        // If an annotationLayer already exists, delete it to avoid creating
+        // duplicate annotations when rapidly re-zooming the document.
+        pageDiv.removeChild(self.annotationLayer);
+        self.annotationLayer = null;
+      }
       viewport = viewport.clone({ dontFlip: true });
       for (var i = 0; i < annotationsData.length; i++) {
         var data = annotationsData[i];
         var annotation = PDFJS.Annotation.fromData(data);
         if (!annotation || !annotation.hasHtml()) {
           continue;
         }
 
@@ -2734,34 +2906,39 @@ var PageView = function pageView(contain
         if (data.subtype === 'Link' && !data.url) {
           if (data.action) {
             bindNamedAction(element, data.action);
           } else {
             bindLink(element, ('dest' in data) ? data.dest : null);
           }
         }
 
-        annotationsDiv.appendChild(element);
+        if (!self.annotationLayer) {
+          var annotationLayerDiv = document.createElement('div');
+          annotationLayerDiv.className = 'annotationLayer';
+          pageDiv.appendChild(annotationLayerDiv);
+          self.annotationLayer = annotationLayerDiv;
+        }
+        self.annotationLayer.appendChild(element);
       }
     });
   }
 
   this.getPagePoint = function pageViewGetPagePoint(x, y) {
     return this.viewport.convertToPdfPoint(x, y);
   };
 
   this.scrollIntoView = function pageViewScrollIntoView(dest) {
+      if (PDFView.isPresentationMode) { // Avoid breaking presentation mode.
+        dest = null;
+      }
       if (!dest) {
         scrollIntoView(div);
         return;
       }
-      if (PDFView.isPresentationMode) { // Avoid breaking presentation mode.
-        PDFView.page = id;
-        return;
-      }
 
       var x = 0, y = 0;
       var width = 0, height = 0, widthScale, heightScale;
       var scale = 0;
       switch (dest[1].name) {
         case 'XYZ':
           x = dest[2];
           y = dest[3];
@@ -2877,21 +3054,22 @@ var PageView = function pageView(contain
     if (!PDFJS.disableTextLayer) {
       textLayerDiv = document.createElement('div');
       textLayerDiv.className = 'textLayer';
       textLayerDiv.style.width = canvas.width + 'px';
       textLayerDiv.style.height = canvas.height + 'px';
       div.appendChild(textLayerDiv);
     }
     var textLayer = this.textLayer =
-          textLayerDiv ? new TextLayerBuilder({
-              textLayerDiv: textLayerDiv,
-              pageIndex: this.id - 1,
-              lastScrollSource: PDFView
-          }) : null;
+      textLayerDiv ? new TextLayerBuilder({
+        textLayerDiv: textLayerDiv,
+        pageIndex: this.id - 1,
+        lastScrollSource: PDFView,
+        isViewerInPresentationMode: PDFView.isPresentationMode
+      }) : null;
 
     if (outputScale.scaled) {
       var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' +
                                 (1 / outputScale.sy) + ')';
       CustomStyle.setProp('transform' , canvas, cssScale);
       CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
       if (textLayerDiv) {
         CustomStyle.setProp('transform' , textLayerDiv, cssScale);
@@ -3263,16 +3441,17 @@ var TextLayerBuilder = function textLaye
   var textLayerFrag = document.createDocumentFragment();
 
   this.textLayerDiv = options.textLayerDiv;
   this.layoutDone = false;
   this.divContentDone = false;
   this.pageIdx = options.pageIndex;
   this.matches = [];
   this.lastScrollSource = options.lastScrollSource;
+  this.isViewerInPresentationMode = options.isViewerInPresentationMode;
 
   if(typeof PDFFindController === 'undefined') {
       window.PDFFindController = null;
   }
 
   if(typeof this.lastScrollSource === 'undefined') {
       this.lastScrollSource = null;
   }
@@ -3531,18 +3710,19 @@ var TextLayerBuilder = function textLaye
 
     for (i = i0; i < i1; i++) {
       var match = matches[i];
       var begin = match.begin;
       var end = match.end;
 
       var isSelected = isSelectedPage && i === selectedMatchIdx;
       var highlightSuffix = (isSelected ? ' selected' : '');
-      if (isSelected)
-        scrollIntoView(textDivs[begin.divIdx], {top: -50});
+      if (isSelected && !this.isViewerInPresentationMode) {
+        scrollIntoView(textDivs[begin.divIdx], { top: -50 });
+      }
 
       // Match inside new div.
       if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
         // If there was a previous div, then add the text at the end
         if (prevEnd !== null) {
           appendText(prevEnd, infty);
         }
         // clears the divs and set the content until the begin point.
@@ -3655,16 +3835,17 @@ var DocumentOutlineView = function docum
 
 document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
   PDFView.initialize();
 
   var file = window.location.href.split('#')[0];
 
 
   document.getElementById('openFile').setAttribute('hidden', 'true');
+  document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
 
   // Special debugging flags in the hash section of the URL.
   var hash = document.location.hash.substring(1);
   var hashParams = PDFView.parseQueryString(hash);
 
   if ('disableWorker' in hashParams) {
     PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
   }
@@ -3708,20 +3889,23 @@ document.addEventListener('DOMContentLoa
     var pdfBug = hashParams['pdfBug'];
     var enabled = pdfBug.split(',');
     PDFBug.enable(enabled);
     PDFBug.init();
   }
 
   if (!PDFView.supportsPrinting) {
     document.getElementById('print').classList.add('hidden');
+    document.getElementById('secondaryPrint').classList.add('hidden');
   }
 
   if (!PDFView.supportsFullscreen) {
     document.getElementById('presentationMode').classList.add('hidden');
+    document.getElementById('secondaryPresentationMode').
+      classList.add('hidden');
   }
 
   if (PDFView.supportsIntegratedFind) {
     document.getElementById('viewFind').classList.add('hidden');
   }
 
   // Listen for warnings to trigger the fallback UI.  Errors should be caught
   // and call PDFView.error() so we don't need to listen for those.
@@ -3779,32 +3963,16 @@ document.addEventListener('DOMContentLoa
       PDFView.zoomIn();
     });
 
   document.getElementById('zoomOut').addEventListener('click',
     function() {
       PDFView.zoomOut();
     });
 
-  document.getElementById('presentationMode').addEventListener('click',
-    function() {
-      PDFView.presentationMode();
-    });
-
-
-  document.getElementById('print').addEventListener('click',
-    function() {
-      window.print();
-    });
-
-  document.getElementById('download').addEventListener('click',
-    function() {
-      PDFView.download();
-    });
-
   document.getElementById('pageNumber').addEventListener('click',
     function() {
       this.select();
     });
 
   document.getElementById('pageNumber').addEventListener('change',
     function() {
       // Handle the user inputting a floating point number.
@@ -3815,35 +3983,39 @@ document.addEventListener('DOMContentLoa
       }
     });
 
   document.getElementById('scaleSelect').addEventListener('change',
     function() {
       PDFView.parseScale(this.value);
     });
 
-  document.getElementById('firstPage').addEventListener('click',
-    function() {
-      PDFView.page = 1;
-    });
-
-  document.getElementById('lastPage').addEventListener('click',
-    function() {
-      PDFView.page = PDFView.pdfDocument.numPages;
-    });
-
-  document.getElementById('pageRotateCcw').addEventListener('click',
-    function() {
-      PDFView.rotatePages(-90);
-    });
-
-  document.getElementById('pageRotateCw').addEventListener('click',
-    function() {
-      PDFView.rotatePages(90);
-    });
+  document.getElementById('presentationMode').addEventListener('click',
+    SecondaryToolbar.presentationModeClick.bind(SecondaryToolbar));
+
+  document.getElementById('openFile').addEventListener('click',
+    SecondaryToolbar.openFileClick.bind(SecondaryToolbar));
+
+  document.getElementById('print').addEventListener('click',
+    SecondaryToolbar.printClick.bind(SecondaryToolbar));
+
+  document.getElementById('download').addEventListener('click',
+    SecondaryToolbar.downloadClick.bind(SecondaryToolbar));
+
+  document.getElementById('contextFirstPage').addEventListener('click',
+    SecondaryToolbar.firstPageClick.bind(SecondaryToolbar));
+
+  document.getElementById('contextLastPage').addEventListener('click',
+    SecondaryToolbar.lastPageClick.bind(SecondaryToolbar));
+
+  document.getElementById('contextPageRotateCw').addEventListener('click',
+    SecondaryToolbar.pageRotateCwClick.bind(SecondaryToolbar));
+
+  document.getElementById('contextPageRotateCcw').addEventListener('click',
+    SecondaryToolbar.pageRotateCcwClick.bind(SecondaryToolbar));
 
   PDFView.setTitleUsingUrl(file);
   PDFView.initPassiveLoading();
   return;
 
   PDFView.open(file, 0);
 }, true);
 
@@ -3874,21 +4046,19 @@ function updateViewarea() {
       break;
     }
   }
 
   if (!stillFullyVisible) {
     currentId = visiblePages[0].id;
   }
 
-  if (!PDFView.isPresentationMode) {
-    updateViewarea.inProgress = true; // used in "set page"
-    PDFView.page = currentId;
-    updateViewarea.inProgress = false;
-  }
+  updateViewarea.inProgress = true; // used in "set page"
+  PDFView.page = currentId;
+  updateViewarea.inProgress = false;
 
   var currentScale = PDFView.currentScale;
   var currentScaleValue = PDFView.currentScaleValue;
   var normalizedScaleValue = currentScaleValue == currentScale ?
     currentScale * 100 : currentScaleValue;
 
   var pageNumber = firstPage.id;
   var pdfOpenParams = '#page=' + pageNumber;
@@ -3916,16 +4086,19 @@ function updateViewarea() {
 window.addEventListener('resize', function webViewerResize(evt) {
   if (PDFView.initialized &&
       (document.getElementById('pageWidthOption').selected ||
        document.getElementById('pageFitOption').selected ||
        document.getElementById('pageAutoOption').selected)) {
     PDFView.parseScale(document.getElementById('scaleSelect').value);
   }
   updateViewarea();
+
+  // Set the 'max-height' CSS property of the secondary toolbar.
+  SecondaryToolbar.setMaxHeight(PDFView.container);
 });
 
 window.addEventListener('hashchange', function webViewerHashchange(evt) {
   if (PDFHistory.isHashChangeUnlocked) {
     PDFView.setHash(document.location.hash.substring(1));
   }
 });
 
@@ -3944,16 +4117,17 @@ window.addEventListener('change', functi
 
   var file = files[0];
   fileReader.readAsArrayBuffer(file);
   PDFView.setTitleUsingUrl(file.name);
 
   // URL does not reflect proper document location - hiding some icons.
   document.getElementById('viewBookmark').setAttribute('hidden', 'true');
   document.getElementById('download').setAttribute('hidden', 'true');
+  document.getElementById('secondaryDownload').setAttribute('hidden', 'true');
 }, true);
 
 function selectScaleOption(value) {
   var options = document.getElementById('scaleSelect').options;
   var predefinedValueFound = false;
   for (var i = 0; i < options.length; i++) {
     var option = options[i];
     if (option.value != value) {
@@ -3964,30 +4138,33 @@ function selectScaleOption(value) {
     predefinedValueFound = true;
   }
   return predefinedValueFound;
 }
 
 window.addEventListener('localized', function localized(evt) {
   document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
 
-  // Adjust the width of the zoom box to fit the content.
-  // Note: This is only done if the zoom box is actually visible,
-  // since otherwise element.clientWidth will return 0.
   PDFView.animationStartedPromise.then(function() {
+    // Adjust the width of the zoom box to fit the content.
+    // Note: This is only done if the zoom box is actually visible,
+    // since otherwise element.clientWidth will return 0.
     var container = document.getElementById('scaleSelectContainer');
     if (container.clientWidth > 0) {
       var select = document.getElementById('scaleSelect');
       select.setAttribute('style', 'min-width: inherit;');
       var width = select.clientWidth + SCALE_SELECT_CONTAINER_PADDING;
       select.setAttribute('style', 'min-width: ' +
                                    (width + SCALE_SELECT_PADDING) + 'px;');
       container.setAttribute('style', 'min-width: ' + width + 'px; ' +
                                       'max-width: ' + width + 'px;');
     }
+
+    // Set the 'max-height' CSS property of the secondary toolbar.
+    SecondaryToolbar.setMaxHeight(PDFView.container);
   });
 }, true);
 
 window.addEventListener('scalechange', function scalechange(evt) {
   document.getElementById('zoomOut').disabled = (evt.scale === MIN_SCALE);
   document.getElementById('zoomIn').disabled = (evt.scale === MAX_SCALE);
 
   var customScaleOption = document.getElementById('customScaleOption');
@@ -4037,18 +4214,17 @@ window.addEventListener('pagechange', fu
 
 // Firefox specific event, so that we can prevent browser from zooming
 window.addEventListener('DOMMouseScroll', function(evt) {
   if (evt.ctrlKey) {
     evt.preventDefault();
 
     var ticks = evt.detail;
     var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn';
-    for (var i = 0, length = Math.abs(ticks); i < length; i++)
-      PDFView[direction]();
+    PDFView[direction](Math.abs(ticks));
   } else if (PDFView.isPresentationMode) {
     var FIREFOX_DELTA_FACTOR = -40;
     PDFView.mouseScroll(evt.detail * FIREFOX_DELTA_FACTOR);
   }
 }, false);
 
 window.addEventListener('mousemove', function mousemove(evt) {
   if (PDFView.isPresentationMode) {
@@ -4068,19 +4244,23 @@ window.addEventListener('mousedown', fun
       // mode.
       evt.preventDefault();
       PDFView.page++;
     }
   }
 }, false);
 
 window.addEventListener('click', function click(evt) {
-  if (PDFView.isPresentationMode && evt.button === 0) {
+  if (!PDFView.isPresentationMode) {
+    if (SecondaryToolbar.isOpen && PDFView.container.contains(evt.target)) {
+      SecondaryToolbar.close();
+    }
+  } else if (evt.button === 0) {
     // Necessary since preventDefault() in 'mousedown' won't stop
-    // the event propagation in all circumstances.
+    // the event propagation in all circumstances in presentation mode.
     evt.preventDefault();
   }
 }, false);
 
 window.addEventListener('keydown', function keydown(evt) {
   var handled = false;
   var cmd = (evt.ctrlKey ? 1 : 0) |
             (evt.altKey ? 2 : 0) |
@@ -4129,32 +4309,37 @@ window.addEventListener('keydown', funct
     }
   }
 
   // CTRL+ALT or Option+Command
   if (cmd === 3 || cmd === 10) {
     switch (evt.keyCode) {
       case 80: // p
         PDFView.presentationMode();
+        SecondaryToolbar.close();
         handled = true;
         break;
     }
   }
 
   if (handled) {
     evt.preventDefault();
     return;
   }
 
   // Some shortcuts should not get handled if a control/input element
   // is selected.
   var curElement = document.activeElement || document.querySelector(':focus');
   if (curElement && (curElement.tagName.toUpperCase() === 'INPUT' ||
+                     curElement.tagName.toUpperCase() === 'TEXTAREA' ||
                      curElement.tagName.toUpperCase() === 'SELECT')) {
-    return;
+    // Make sure that the secondary toolbar is closed when Escape is pressed.
+    if (evt.keyCode !== 27) { // 'Esc'
+      return;
+    }
   }
   var controlsElement = document.getElementById('toolbar');
   while (curElement) {
     if (curElement === controlsElement && !PDFView.isPresentationMode)
       return; // ignoring if the 'toolbar' element is focused
     curElement = curElement.parentNode;
   }
 
@@ -4176,16 +4361,20 @@ window.addEventListener('keydown', funct
         }
         /* falls through */
       case 75: // 'k'
       case 80: // 'p'
         PDFView.page--;
         handled = true;
         break;
       case 27: // esc key
+        if (SecondaryToolbar.isOpen) {
+          SecondaryToolbar.close();
+          handled = true;
+        }
         if (!PDFView.supportsIntegratedFind && PDFFindBar.opened) {
           PDFFindBar.close();
           handled = true;
         }
         break;
       case 40: // down arrow
       case 34: // pg down
       case 32: // spacebar
--- a/browser/extensions/pdfjs/extension-files
+++ b/browser/extensions/pdfjs/extension-files
@@ -16,29 +16,34 @@ content/web/images/annotation-newparagra
 content/web/images/annotation-note.svg
 content/web/images/annotation-paragraph.svg
 content/web/images/findbarButton-next-rtl.png
 content/web/images/findbarButton-next.png
 content/web/images/findbarButton-previous-rtl.png
 content/web/images/findbarButton-previous.png
 content/web/images/loading-icon.gif
 content/web/images/loading-small.png
+content/web/images/secondaryToolbarButton-firstPage.png
+content/web/images/secondaryToolbarButton-lastPage.png
+content/web/images/secondaryToolbarButton-rotateCcw.png
+content/web/images/secondaryToolbarButton-rotateCw.png
 content/web/images/shadow.png
 content/web/images/texture.png
 content/web/images/toolbarButton-bookmark.png
 content/web/images/toolbarButton-download.png
 content/web/images/toolbarButton-menuArrows.png
 content/web/images/toolbarButton-openFile.png
 content/web/images/toolbarButton-pageDown-rtl.png
 content/web/images/toolbarButton-pageDown.png
 content/web/images/toolbarButton-pageUp-rtl.png
 content/web/images/toolbarButton-pageUp.png
 content/web/images/toolbarButton-presentationMode.png
 content/web/images/toolbarButton-print.png
 content/web/images/toolbarButton-search.png
+content/web/images/toolbarButton-secondaryToolbarToggle.png
 content/web/images/toolbarButton-sidebarToggle.png
 content/web/images/toolbarButton-viewOutline.png
 content/web/images/toolbarButton-viewThumbnail.png
 content/web/images/toolbarButton-zoomIn.png
 content/web/images/toolbarButton-zoomOut.png
 content/web/l10n.js
 content/web/viewer.css
 content/web/viewer.html
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
@@ -1,15 +1,15 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!ENTITY index.title "App Manager">
-<!ENTITY index.projects "My Apps">
-<!ENTITY index.device "My Device">
+<!ENTITY index.projects2 "Apps">
+<!ENTITY index.device2 "Device">
 
 <!ENTITY device.screenshot "Screenshot">
 <!ENTITY device.title "Device Control Center">
 <!ENTITY device.notConnected "Not connected. Please connect your device below.">
 <!ENTITY device.startApp "Start">
 <!ENTITY device.stopApp "Stop">
 <!ENTITY device.debugApp "Debug">
 <!ENTITY device.name "Name">
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
@@ -6,17 +6,17 @@
 # the device's height, %3$S is the device's pixel density.
 # Example: 800x480 (86 DPI).
 device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI)
 # LOCALIZATION NOTE (connection.connectedToDevice, connection.connectTo):
 # %1$S is the host name, %2$S is the port number.
 connection.connectedToDevice=Connected to %1$S
 connection.connectTo=Connect to %1$S:%2$S
 project.filePickerTitle=Select a webapp folder
-project.installing=Installing...
+project.installing=Installing…
 project.installed=Installed!
 validator.nonExistingFolder=The project folder doesn't exists
 validator.expectProjectFolder=The project folder ends up being a file
 validator.wrongManifestFileName=Packaged apps require a manifest file that can only be named 'manifest.webapp' at project root folder
 validator.invalidManifestURL=Invalid manifest URL '%S'
 # LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
 # %1$S is the error message, %2$S is the URI of the manifest.
 validator.invalidManifestJSON=The webapp manifest isn't a valid JSON file: %1$S at: %2$S
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -25,27 +25,43 @@ next_label=Next
 page_label=Page:
 page_of=of {{pageCount}}
 
 zoom_out.title=Zoom Out
 zoom_out_label=Zoom Out
 zoom_in.title=Zoom In
 zoom_in_label=Zoom In
 zoom.title=Zoom
-print.title=Print
-print_label=Print
 presentation_mode.title=Switch to Presentation Mode
 presentation_mode_label=Presentation Mode
 open_file.title=Open File
-open_file_label=Open
+open_file_label=Open File
+print.title=Print
+print_label=Print
 download.title=Download
 download_label=Download
 bookmark.title=Current view (copy or open in new window)
 bookmark_label=Current View
 
+# Secondary toolbar and context menu
+tools.title=Tools
+tools_label=Tools
+first_page.title=Go to First Page
+first_page.label=Go to First Page
+first_page_label=Go to First Page
+last_page.title=Go to Last Page
+last_page.label=Go to Last Page
+last_page_label=Go to Last Page
+page_rotate_cw.title=Rotate Clockwise
+page_rotate_cw.label=Rotate Clockwise
+page_rotate_cw_label=Rotate Clockwise
+page_rotate_ccw.title=Rotate Counterclockwise
+page_rotate_ccw.label=Rotate Counterclockwise
+page_rotate_ccw_label=Rotate Counterclockwise
+
 # Tooltips and alt text for side panel toolbar buttons
 # (the _label strings are alt text for the buttons, the .title strings are
 # tooltips)
 toggle_sidebar.title=Toggle Sidebar
 toggle_sidebar_label=Toggle Sidebar
 outline.title=Show Document Outline
 outline_label=Document Outline
 thumbs.title=Show Thumbnails
@@ -56,22 +72,16 @@ findbar_label=Find
 # Thumbnails panel item (tooltip and alt text for images)
 # LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
 # number.
 thumb_page_title=Page {{page}}
 # LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
 # number.
 thumb_page_canvas=Thumbnail of Page {{page}}
 
-# Context menu
-first_page.label=Go to First Page
-last_page.label=Go to Last Page
-page_rotate_cw.label=Rotate Clockwise
-page_rotate_ccw.label=Rotate Counterclockwise
-
 # Find panel button title and messages
 find_label=Find:
 find_previous.title=Find the previous occurrence of the phrase
 find_previous_label=Previous
 find_next.title=Find the next occurrence of the phrase
 find_next_label=Next
 find_highlight=Highlight all
 find_match_case_label=Match case
--- a/browser/metro/base/content/flyoutpanels/SyncFlyoutPanel.js
+++ b/browser/metro/base/content/flyoutpanels/SyncFlyoutPanel.js
@@ -103,20 +103,20 @@ let SyncFlyoutPanel = {
   },
 
   _onSyncEnd: function() {
     this._isSyncing = false;
     this._updateConnectedPage();
   },
 
   showInitialScreen: function() {
-    if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED) {
+    if (Weave.Status.login == Weave.LOGIN_SUCCEEDED) {
+      this.showConnected();
+    } else {
       this.showPreSetup();
-    } else {
-      this.showConnected();
     }
   },
 
   abortEasySetup: function() {
     if (this._setupJpake) {
       this._setupJpake.abort();
     }
     this._cleanUpEasySetup();
--- a/browser/themes/shared/devtools/dark-theme.css
+++ b/browser/themes/shared/devtools/dark-theme.css
@@ -43,17 +43,17 @@
   background-position: -42px 0;
 }
 
 .theme-selected {
   background: #26394D;
 }
 
 .theme-bg-darker {
-  background-color: rgba(0,0,0,0.1);
+  background-color: rgba(0,0,0,0.5);
 }
 
 .theme-link { /* blue */
   color: #3689b2;
 }
 
 .theme-comment { /* grey */
   color: #5c6773;
--- a/browser/themes/shared/devtools/light-theme.css
+++ b/browser/themes/shared/devtools/light-theme.css
@@ -43,17 +43,17 @@
   background-position: -14px 0;
 }
 
 .theme-selected {
   background-color: #CCC;
 }
 
 .theme-bg-darker {
-  background: #F9F9F9;
+  background: #EFEFEF;
 }
 
 .theme-link { /* blue */
   color: hsl(208,56%,40%);
 }
 
 .theme-comment { /* grey */
   color: hsl(90,2%,46%);
--- a/browser/themes/shared/devtools/markup-view.css
+++ b/browser/themes/shared/devtools/markup-view.css
@@ -2,37 +2,15 @@
  * 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/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
-.newattr {
-  cursor: pointer;
-  padding: 1px 0;
-}
-
-li.container {
-  padding: 2px 0 0 2px;
-}
-
-.codebox {
-  padding-left: 14px;
-}
-
-.codebox > * {
-  vertical-align: middle;
-}
-
-.expander {
-  display: inline-block;
-  margin-left: -14px;
-}
-
 .more-nodes {
   padding-left: 16px;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
 }
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -323,22 +323,16 @@
   #navigator-toolbox[tabsontop=false] .searchbar-textbox:not(:-moz-lwtheme)[focused] {
     background-color: white;
   }
 
   .tabbrowser-tab:not(:-moz-lwtheme) {
     text-shadow: none;
   }
 
-  #main-window[sizemode=normal] .statuspanel-inner {
-    /* align with the browser's side borders */
-    padding-left: 1px;
-    padding-right: 1px;
-  }
-
   #ctrlTab-panel {
     background: transparent;
     -moz-appearance: -moz-win-glass;
     border-radius: 0;
     border: none;
     font: normal 1.2em "Segoe UI";
     color: black;
     text-shadow: white -1px -1px .35em, white -1px 1px .35em, white 1px 1px .35em, white 1px -1px .35em;
--- a/caps/tests/mochitest/test_bug292789.html
+++ b/caps/tests/mochitest/test_bug292789.html
@@ -7,50 +7,50 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 292789</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=292789">Mozilla Bug 292789</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  <script src="chrome://global/content/strres.js"></script>
+  <script src="chrome://global/content/treeUtils.js"></script>
   <script type="application/javascript;version=1.8" src="chrome://mozapps/content/xpinstall/xpinstallConfirm.js"></script>
   <script id="resjs" type="application/javascript;version=1.8"></script>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 292789
  **
  ** Selectively allow access to whitelisted chrome packages
  ** even for ALLOW_CHROME mechanisms (<script>, <img> etc)
  **/
 
 SimpleTest.waitForExplicitFinish();
 
 /** <script src=""> test **/
 function testScriptSrc(aCallback) {
-    is(typeof srGetStrBundle, "function",
+    is(typeof gTreeUtils.sort, "function",
        "content can still load <script> from chrome://global");
     is(typeof XPInstallConfirm, "undefined",
        "content should not be able to load <script> from chrome://mozapps");
-    
+
     /** make sure the last one didn't pass because someone
      ** moved the resource
      **/
     var resjs = document.getElementById("resjs");
     resjs.onload = scriptOnload;
     resjs.src = "resource://gre/chrome/toolkit/content/mozapps/xpinstall/xpinstallConfirm.js";
     document.getElementById("content").appendChild(resjs);
 
     function scriptOnload() {
       is(typeof XPInstallConfirm, "object",
          "xpinstallConfirm.js has not moved unexpectedly");
-  
+
       // trigger the callback
       if (aCallback)
         aCallback();
     }
 }
 
 /** <img src=""> tests **/
 var img_global = "chrome://global/skin/icons/Error.png";
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1482,17 +1482,17 @@ endif
 
 ifneq ($(DIST_CHROME_FILES),)
 DIST_CHROME_FILES_PATH := $(FINAL_TARGET)/chrome
 DIST_CHROME_FILES_FLAGS := $(XULAPP_DEFINES)
 PP_TARGETS += DIST_CHROME_FILES
 endif
 
 ifneq ($(XPI_PKGNAME),)
-libs realchrome::
+tools realchrome::
 ifdef STRIP_XPI
 ifndef MOZ_DEBUG
 	@echo "Stripping $(XPI_PKGNAME) package directory..."
 	@echo $(FINAL_TARGET)
 	@cd $(FINAL_TARGET) && find . ! -type d \
 			! -name "*.js" \
 			! -name "*.xpt" \
 			! -name "*.gif" \
@@ -1521,17 +1521,17 @@ endif
 	cd $(FINAL_TARGET) && $(ZIP) -qr ../$(XPI_PKGNAME).xpi *
 endif
 
 ifdef INSTALL_EXTENSION_ID
 ifndef XPI_NAME
 $(error XPI_NAME must be set for INSTALL_EXTENSION_ID)
 endif
 
-libs::
+tools::
 	$(RM) -r "$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID)"
 	$(NSINSTALL) -D "$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID)"
 	$(call copy_dir,$(FINAL_TARGET),$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID))
 
 endif
 
 #############################################################################
 # MDDEPDIR is the subdirectory where all the dependency files are placed.
--- a/configure.in
+++ b/configure.in
@@ -206,25 +206,27 @@ if test -n "$gonkdir" ; then
     esac
 
     case "$ANDROID_VERSION" in
     15)
         GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
         MOZ_B2G_BT=1
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
-        AC_DEFINE(MOZ_OMX_DECODER)
         AC_SUBST(MOZ_OMX_DECODER)
         ;;
     18)
-        GONK_INCLUDES="-I$gonkdir/frameworks/native/include"
+        GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include"
         if test -d "$gonkdir/external/bluetooth/bluez"; then
             GONK_INCLUDES+=" -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib"
             MOZ_B2G_BT=1
         fi
+        MOZ_B2G_CAMERA=1
+        MOZ_OMX_DECODER=1
+        AC_SUBST(MOZ_OMX_DECODER)
         ;;
     *)
         AC_MSG_ERROR([Unsupported platform version: $ANDROID_VERSION])
         ;;
     esac
     CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/hardware/libhardware/include -I$gonkdir/external/valgrind/fxos-include $GONK_INCLUDES $CPPFLAGS"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS $STLPORT_CPPFLAGS"
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -1,15 +1,19 @@
 # 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 $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
+ifdef MOZ_OMX_DECODER
+DEFINES += -DMOZ_OMX_DECODER
+endif
+
 LOCAL_INCLUDES  += \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/layout/generic \
   -I$(topsrcdir)/layout/xul/base/src \
   $(NULL)
 
 ifdef MOZ_DIRECTSHOW
 LOCAL_INCLUDES += -I$(topsrcdir)/media/webrtc/trunk/webrtc/modules/video_capture/windows/
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -467,17 +467,21 @@ bool OmxDecoder::AllocateMediaResources(
   // it can't connect.
   OMXClient client;
   DebugOnly<status_t> err = client.connect();
   NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver.");
   sp<IOMX> omx = client.interface();
 
   if ((mVideoTrack != nullptr) && (mVideoSource == nullptr)) {
     mNativeWindow = new GonkNativeWindow();
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+    mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow->getBufferQueue());
+#else
     mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow);
+#endif
 
     // Experience with OMX codecs is that only the HW decoders are
     // worth bothering with, at least on the platforms where this code
     // is currently used, and for formats this code is currently used
     // for (h.264).  So if we don't get a hardware decoder, just give
     // up.
     int flags = kHardwareCodecsOnly;
 
--- a/content/media/omx/mediaresourcemanager/IMediaResourceManagerDeathNotifier.cpp
+++ b/content/media/omx/mediaresourcemanager/IMediaResourceManagerDeathNotifier.cpp
@@ -19,73 +19,78 @@
 #define LOG_TAG "IMediaResourceManagerDeathNotifier"
 #include <utils/Log.h>
 
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
 
 #include "IMediaResourceManagerDeathNotifier.h"
 
+#define DN_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define DN_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define DN_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define DN_LOGE_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+
 namespace android {
 
 // client singleton for binder interface to services
 Mutex IMediaResourceManagerDeathNotifier::sServiceLock;
 sp<IMediaResourceManagerService> IMediaResourceManagerDeathNotifier::sMediaResourceManagerService;
 sp<IMediaResourceManagerDeathNotifier::DeathNotifier> IMediaResourceManagerDeathNotifier::sDeathNotifier;
 SortedVector< wp<IMediaResourceManagerDeathNotifier> > IMediaResourceManagerDeathNotifier::sObitRecipients;
 
 // establish binder interface to MediaResourceManagerService
 /*static*/const sp<IMediaResourceManagerService>&
 IMediaResourceManagerDeathNotifier::getMediaResourceManagerService()
 {
-    LOGV("getMediaResourceManagerService");
+    DN_LOGV("getMediaResourceManagerService");
     Mutex::Autolock _l(sServiceLock);
     if (sMediaResourceManagerService.get() == 0) {
         sp<IServiceManager> sm = defaultServiceManager();
         sp<IBinder> binder;
         do {
             binder = sm->getService(String16("media.resource_manager"));
             if (binder != 0) {
                 break;
              }
-             LOGW("Media resource manager service not published, waiting...");
+             DN_LOGW("Media resource manager service not published, waiting...");
              usleep(500000); // 0.5 s
         } while(true);
 
         if (sDeathNotifier == NULL) {
         sDeathNotifier = new DeathNotifier();
     }
     binder->linkToDeath(sDeathNotifier);
     sMediaResourceManagerService = interface_cast<IMediaResourceManagerService>(binder);
     }
-    LOGE_IF(sMediaResourceManagerService == 0, "no media player service!?");
+    DN_LOGE_IF(sMediaResourceManagerService == 0, "no media player service!?");
     return sMediaResourceManagerService;
 }
 
 /*static*/ void
 IMediaResourceManagerDeathNotifier::addObitRecipient(const wp<IMediaResourceManagerDeathNotifier>& recipient)
 {
-    LOGV("addObitRecipient");
     Mutex::Autolock _l(sServiceLock);
     sObitRecipients.add(recipient);
 }
 
 /*static*/ void
 IMediaResourceManagerDeathNotifier::removeObitRecipient(const wp<IMediaResourceManagerDeathNotifier>& recipient)
 {
-    LOGV("removeObitRecipient");
     Mutex::Autolock _l(sServiceLock);
     sObitRecipients.remove(recipient);
 }
 
 void
 IMediaResourceManagerDeathNotifier::DeathNotifier::binderDied(const wp<IBinder>& who)
 {
-    LOGW("media server died");
-
+    DN_LOGW("media resource manager service died");
     // Need to do this with the lock held
     SortedVector< wp<IMediaResourceManagerDeathNotifier> > list;
     {
         Mutex::Autolock _l(sServiceLock);
         sMediaResourceManagerService.clear();
         list = sObitRecipients;
     }
 
@@ -98,17 +103,16 @@ IMediaResourceManagerDeathNotifier::Deat
         if (notifier != 0) {
             notifier->died();
         }
     }
 }
 
 IMediaResourceManagerDeathNotifier::DeathNotifier::~DeathNotifier()
 {
-    LOGV("DeathNotifier::~DeathNotifier");
     Mutex::Autolock _l(sServiceLock);
     sObitRecipients.clear();
     if (sMediaResourceManagerService != 0) {
         sMediaResourceManagerService->asBinder()->unlinkToDeath(this);
     }
 }
 
 }; // namespace android
--- a/dom/audiochannel/AudioChannelCommon.h
+++ b/dom/audiochannel/AudioChannelCommon.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_audiochannelcommon_h__
 
 namespace mozilla {
 namespace dom {
 
 // The audio channel. Read the nsIHTMLMediaElement.idl for a description
 // about this attribute.
 enum AudioChannelType {
+  AUDIO_CHANNEL_DEFAULT = -1,
   AUDIO_CHANNEL_NORMAL = 0,
   AUDIO_CHANNEL_CONTENT,
   AUDIO_CHANNEL_NOTIFICATION,
   AUDIO_CHANNEL_ALARM,
   AUDIO_CHANNEL_TELEPHONY,
   AUDIO_CHANNEL_RINGER,
   AUDIO_CHANNEL_PUBLICNOTIFICATION,
   AUDIO_CHANNEL_LAST
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -68,16 +68,17 @@ AudioChannelService::Shutdown()
 }
 
 NS_IMPL_ISUPPORTS2(AudioChannelService, nsIObserver, nsITimerCallback)
 
 AudioChannelService::AudioChannelService()
 : mCurrentHigherChannel(AUDIO_CHANNEL_LAST)
 , mCurrentVisibleHigherChannel(AUDIO_CHANNEL_LAST)
 , mActiveContentChildIDsFrozen(false)
+, mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
 #ifdef MOZ_WIDGET_GONK
       // To monitor the volume settings based on audio channel.
       obs->AddObserver(this, "mozsettings-changed", false);
@@ -89,16 +90,18 @@ AudioChannelService::AudioChannelService
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                                AudioChannelType aType)
 {
+  MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
+
   AudioChannelAgentData* data = new AudioChannelAgentData(aType,
                                                           true /* mElementHidden */,
                                                           true /* mMuted */);
   mAgents.Put(aAgent, data);
   RegisterType(aType, CONTENT_PROCESS_ID_MAIN);
 }
 
 void
@@ -298,16 +301,46 @@ bool
 AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
 {
   return mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) ||
          mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID) ||
          mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].Contains(aChildID);
 }
 
 void
+AudioChannelService::SetDefaultVolumeControlChannel(AudioChannelType aType,
+                                                    bool aHidden)
+{
+  SetDefaultVolumeControlChannelInternal(aType, aHidden, CONTENT_PROCESS_ID_MAIN);
+}
+
+void
+AudioChannelService::SetDefaultVolumeControlChannelInternal(
+  AudioChannelType aType, bool aHidden, uint64_t aChildID)
+{
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return;
+  }
+
+  // If this child is in the background and mDefChannelChildID is set to
+  // others then it means other child in the foreground already set it's
+  // own default channel already.
+  if (!aHidden && mDefChannelChildID != aChildID) {
+    return;
+  }
+
+  mDefChannelChildID = aChildID;
+  nsString channelName;
+  channelName.AssignASCII(ChannelName(aType));
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  obs->NotifyObservers(nullptr, "default-volume-channel-changed",
+                       channelName.get());
+}
+
+void
 AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
   nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
   props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), aChildID);
@@ -514,16 +547,22 @@ AudioChannelService::Observe(nsISupports
         }
       }
 
       // We don't have to remove the agents from the mAgents hashtable because if
       // that table contains only agents running on the same process.
 
       SendAudioChannelChangedNotification(childID);
       Notify();
+
+      if (mDefChannelChildID == childID) {
+        SetDefaultVolumeControlChannelInternal(AUDIO_CHANNEL_DEFAULT,
+                                               false, childID);
+        mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
+      }
     } else {
       NS_WARNING("ipc:content-shutdown message without childID property");
     }
   }
 #ifdef MOZ_WIDGET_GONK
   // To process the volume control on each audio channel according to
   // change of settings
   else if (!strcmp(aTopic, "mozsettings-changed")) {
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -43,17 +43,17 @@ public:
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannelType.
    */
   virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                          AudioChannelType aType);
 
   /**
-   * Any  audio channel agent that stops playing should unregister itself to
+   * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
    * Return true if this type should be muted.
    */
   virtual bool GetMuted(AudioChannelAgent* aAgent, bool aElementHidden);
@@ -65,16 +65,23 @@ public:
   virtual bool ContentOrNormalChannelIsActive();
 
   /**
    * Return true iff a normal or content channel is active for the given process
    * ID.
    */
   virtual bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID);
 
+  /***
+   * AudioChannelManager calls this function to notify the default channel used
+   * to adjust volume when there is no any active channel.
+   */
+  virtual void SetDefaultVolumeControlChannel(AudioChannelType aType,
+                                              bool aHidden);
+
 protected:
   void Notify();
 
   /**
    * Send the audio-channel-changed notification for the given process ID if
    * needed.
    */
   void SendAudioChannelChangedNotification(uint64_t aChildID);
@@ -88,16 +95,20 @@ protected:
 
   bool GetMutedInternal(AudioChannelType aType, uint64_t aChildID,
                         bool aElementHidden, bool aElementWasHidden);
 
   /* Update the internal type value following the visibility changes */
   void UpdateChannelType(AudioChannelType aType, uint64_t aChildID,
                          bool aElementHidden, bool aElementWasHidden);
 
+  /* Send the default-volume-channel-changed notification */
+  void SetDefaultVolumeControlChannelInternal(AudioChannelType aType,
+                                              bool aHidden, uint64_t aChildID);
+
   AudioChannelService();
   virtual ~AudioChannelService();
 
   enum AudioChannelInternalType {
     AUDIO_CHANNEL_INT_NORMAL = 0,
     AUDIO_CHANNEL_INT_NORMAL_HIDDEN,
     AUDIO_CHANNEL_INT_CONTENT,
     AUDIO_CHANNEL_INT_CONTENT_HIDDEN,
@@ -149,16 +160,18 @@ protected:
 
   nsTArray<uint64_t> mActiveContentChildIDs;
   bool mActiveContentChildIDsFrozen;
 
   nsCOMPtr<nsITimer> mDeferTelChannelTimer;
   bool mTimerElementHidden;
   uint64_t mTimerChildID;
 
+  uint64_t mDefChannelChildID;
+
   // This is needed for IPC comunication between
   // AudioChannelServiceChild and this class.
   friend class ContentParent;
   friend class ContentChild;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/audiochannel/AudioChannelServiceChild.cpp
+++ b/dom/audiochannel/AudioChannelServiceChild.cpp
@@ -86,16 +86,18 @@ AudioChannelServiceChild::GetMuted(Audio
 
   return muted;
 }
 
 void
 AudioChannelServiceChild::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                                     AudioChannelType aType)
 {
+  MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
+
   AudioChannelService::RegisterAudioChannelAgent(aAgent, aType);
 
   ContentChild *cc = ContentChild::GetSingleton();
   if (cc) {
     cc->SendAudioChannelRegisterType(aType);
   }
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -123,8 +125,18 @@ AudioChannelServiceChild::UnregisterAudi
     cc->SendAudioChannelUnregisterType(data.mType, data.mElementHidden);
   }
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
   }
 }
+
+void
+AudioChannelServiceChild::SetDefaultVolumeControlChannel(
+  AudioChannelType aType, bool aHidden)
+{
+  ContentChild *cc = ContentChild::GetSingleton();
+  if (cc) {
+    cc->SendAudioChannelChangeDefVolChannel(aType, aHidden);
+  }
+}
--- a/dom/audiochannel/AudioChannelServiceChild.h
+++ b/dom/audiochannel/AudioChannelServiceChild.h
@@ -33,16 +33,18 @@ public:
                                          AudioChannelType aType);
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
    * Return true if this type + this mozHidden should be muted.
    */
   virtual bool GetMuted(AudioChannelAgent* aAgent, bool aMozHidden);
 
+  virtual void SetDefaultVolumeControlChannel(AudioChannelType aType, bool aHidden);
+
 protected:
   AudioChannelServiceChild();
   virtual ~AudioChannelServiceChild();
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/bluetooth/BluetoothUuid.cpp
+++ b/dom/bluetooth/BluetoothUuid.cpp
@@ -1,26 +1,31 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "BluetoothUuid.h"
 
+#include "BluetoothA2dpManager.h"
+#include "BluetoothHfpManager.h"
+#include "BluetoothHidManager.h"
+#include "BluetoothOppManager.h"
+
 USING_BLUETOOTH_NAMESPACE
 
 void
-BluetoothUuidHelper::GetString(BluetoothServiceClass aServiceClassUuid,
+BluetoothUuidHelper::GetString(BluetoothServiceClass aServiceClass,
                                nsAString& aRetUuidStr)
 {
   aRetUuidStr.Truncate();
 
   aRetUuidStr.AppendLiteral("0000");
-  aRetUuidStr.AppendInt(aServiceClassUuid, 16);
+  aRetUuidStr.AppendInt(aServiceClass, 16);
   aRetUuidStr.AppendLiteral("-0000-1000-8000-00805F9B34FB");
 }
 
 BluetoothServiceClass
 BluetoothUuidHelper::GetBluetoothServiceClass(const nsAString& aUuidStr)
 {
   // An example of input UUID string: 0000110D-0000-1000-8000-00805F9B34FB
   MOZ_ASSERT(aUuidStr.Length() == 36);
@@ -36,23 +41,48 @@ BluetoothUuidHelper::GetBluetoothService
   nsresult rv;
   int32_t integer = uuid.ToInteger(&rv, 16);
   NS_ENSURE_SUCCESS(rv, retValue);
 
   return GetBluetoothServiceClass(integer);
 }
 
 BluetoothServiceClass
-BluetoothUuidHelper::GetBluetoothServiceClass(uint16_t aProfileId)
+BluetoothUuidHelper::GetBluetoothServiceClass(uint16_t aServiceUuid)
 {
   BluetoothServiceClass retValue = BluetoothServiceClass::UNKNOWN;
-  switch (aProfileId) {
+  switch (aServiceUuid) {
     case BluetoothServiceClass::A2DP:
     case BluetoothServiceClass::HANDSFREE:
     case BluetoothServiceClass::HANDSFREE_AG:
     case BluetoothServiceClass::HEADSET:
     case BluetoothServiceClass::HEADSET_AG:
     case BluetoothServiceClass::HID:
     case BluetoothServiceClass::OBJECT_PUSH:
-      retValue = (BluetoothServiceClass)aProfileId;
+      retValue = (BluetoothServiceClass)aServiceUuid;
   }
   return retValue;
 }
+
+BluetoothProfileManagerBase*
+BluetoothUuidHelper::GetBluetoothProfileManager(uint16_t aServiceUuid)
+{
+  BluetoothProfileManagerBase* profile;
+  BluetoothServiceClass serviceClass = GetBluetoothServiceClass(aServiceUuid);
+  switch (serviceClass) {
+    case BluetoothServiceClass::HANDSFREE:
+    case BluetoothServiceClass::HEADSET:
+      profile = BluetoothHfpManager::Get();
+      break;
+    case BluetoothServiceClass::HID:
+      profile = BluetoothHidManager::Get();
+      break;
+    case BluetoothServiceClass::A2DP:
+      profile = BluetoothA2dpManager::Get();
+      break;
+    case BluetoothServiceClass::OBJECT_PUSH:
+      profile = BluetoothOppManager::Get();
+      break;
+    default:
+      profile = nullptr;
+  }
+  return profile;
+}
--- a/dom/bluetooth/BluetoothUuid.h
+++ b/dom/bluetooth/BluetoothUuid.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothuuid_h__
 #define mozilla_dom_bluetooth_bluetoothuuid_h__
 
 #include "BluetoothCommon.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
+class BluetoothProfileManagerBase;
+
 /*
  * Service classes and Profile Identifiers
  *
  * Supported Bluetooth services for v1 are listed as below.
  *
  * The value of each service class is defined in "AssignedNumbers/Service
  * Discovery Protocol (SDP)/Service classes and Profile Identifiers" in the
  * Bluetooth Core Specification.
@@ -50,17 +52,20 @@ public:
    *
    * @param aUuidStr  128-bit uuid string
    * @return  a value of BluetoothServiceClass
    */
   static BluetoothServiceClass
   GetBluetoothServiceClass(const nsAString& aUuidStr);
 
   static BluetoothServiceClass
-  GetBluetoothServiceClass(uint16_t aProfileId);
+  GetBluetoothServiceClass(uint16_t aServiceUuid);
+
+  static BluetoothProfileManagerBase*
+  GetBluetoothProfileManager(uint16_t aServiceUuid);
 };
 
 // TODO/qdot: Move these back into gonk and make the service handler deal with
 // it there.
 //
 // Gotten from reading the "u8" values in B2G/external/bluez/src/adapter.c
 // These were hardcoded into android
 enum BluetoothReservedChannels {
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -2200,25 +2200,19 @@ BluetoothDBusService::GetConnectedDevice
   BluetoothValue values = InfallibleTArray<BluetoothNamedValue>();
   if (!IsReady()) {
     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
     return NS_OK;
   }
 
   nsTArray<nsString> deviceAddresses;
-  BluetoothProfileManagerBase* profile;
-  if (aServiceUuid == BluetoothServiceClass::HANDSFREE ||
-      aServiceUuid == BluetoothServiceClass::HEADSET) {
-    profile = BluetoothHfpManager::Get();
-  } else if (aServiceUuid == BluetoothServiceClass::HID) {
-    profile = BluetoothHidManager::Get();
-  } else if (aServiceUuid == BluetoothServiceClass::OBJECT_PUSH) {
-    profile = BluetoothOppManager::Get();
-  } else {
+  BluetoothProfileManagerBase* profile =
+    BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
+  if (!profile) {
     DispatchBluetoothReply(aRunnable, values,
                            NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
     return NS_OK;
   }
 
   if (profile->IsConnected()) {
     nsString address;
     profile->GetAddress(address);
@@ -2675,25 +2669,19 @@ BluetoothDBusService::Disconnect(const n
   }
 }
 
 bool
 BluetoothDBusService::IsConnected(const uint16_t aServiceUuid)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  BluetoothProfileManagerBase* profile;
-  if (aServiceUuid == BluetoothServiceClass::HANDSFREE ||
-      aServiceUuid == BluetoothServiceClass::HEADSET) {
-    profile = BluetoothHfpManager::Get();
-  } else if (aServiceUuid == BluetoothServiceClass::HID) {
-    profile = BluetoothHidManager::Get();
-  } else if (aServiceUuid == BluetoothServiceClass::OBJECT_PUSH) {
-    profile = BluetoothOppManager::Get();
-  } else {
+  BluetoothProfileManagerBase* profile =
+    BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
+  if (!profile) {
     NS_WARNING(ERR_UNKNOWN_PROFILE);
     return false;
   }
 
   NS_ENSURE_TRUE(profile, false);
   return profile->IsConnected();
 }
 
deleted file mode 100644
--- a/dom/camera/AudioParameter.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2006-2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AudioParameter"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-
-#include <media/AudioParameter.h>
-
-namespace android {
-
-const char *AudioParameter::keyRouting = "routing";
-const char *AudioParameter::keySamplingRate = "sampling_rate";
-const char *AudioParameter::keyFormat = "format";
-const char *AudioParameter::keyChannels = "channels";
-const char *AudioParameter::keyFrameCount = "frame_count";
-const char *AudioParameter::keyInputSource = "input_source";
-
-AudioParameter::AudioParameter(const String8& keyValuePairs)
-{
-    char *str = new char[keyValuePairs.length()+1];
-    mKeyValuePairs = keyValuePairs;
-
-    strcpy(str, keyValuePairs.string());
-    char *pair = strtok(str, ";");
-    while (pair != NULL) {
-        if (strlen(pair) != 0) {
-            size_t eqIdx = strcspn(pair, "=");
-            String8 key = String8(pair, eqIdx);
-            String8 value;
-            if (eqIdx == strlen(pair)) {
-                value = String8("");
-            } else {
-                value = String8(pair + eqIdx + 1);
-            }
-            if (mParameters.indexOfKey(key) < 0) {
-                mParameters.add(key, value);
-            } else {
-                mParameters.replaceValueFor(key, value);
-            }
-        } else {
-            LOGV("AudioParameter() cstor empty key value pair");
-        }
-        pair = strtok(NULL, ";");
-    }
-
-    delete[] str;
-}
-
-AudioParameter::~AudioParameter()
-{
-    mParameters.clear();
-}
-
-String8 AudioParameter::toString()
-{
-    String8 str = String8("");
-
-    size_t size = mParameters.size();
-    for (size_t i = 0; i < size; i++) {
-        str += mParameters.keyAt(i);
-        str += "=";
-        str += mParameters.valueAt(i);
-        if (i < (size - 1)) str += ";";
-    }
-    return str;
-}
-
-status_t AudioParameter::add(const String8& key, const String8& value)
-{
-    if (mParameters.indexOfKey(key) < 0) {
-        mParameters.add(key, value);
-        return NO_ERROR;
-    } else {
-        mParameters.replaceValueFor(key, value);
-        return ALREADY_EXISTS;
-    }
-}
-
-status_t AudioParameter::addInt(const String8& key, const int value)
-{
-    char str[12];
-    if (snprintf(str, 12, "%d", value) > 0) {
-        String8 str8 = String8(str);
-        return add(key, str8);
-    } else {
-        return BAD_VALUE;
-    }
-}
-
-status_t AudioParameter::addFloat(const String8& key, const float value)
-{
-    char str[23];
-    if (snprintf(str, 23, "%.10f", value) > 0) {
-        String8 str8 = String8(str);
-        return add(key, str8);
-    } else {
-        return BAD_VALUE;
-    }
-}
-
-status_t AudioParameter::remove(const String8& key)
-{
-    if (mParameters.indexOfKey(key) >= 0) {
-        mParameters.removeItem(key);
-        return NO_ERROR;
-    } else {
-        return BAD_VALUE;
-    }
-}
-
-status_t AudioParameter::get(const String8& key, String8& value)
-{
-    if (mParameters.indexOfKey(key) >= 0) {
-        value = mParameters.valueFor(key);
-        return NO_ERROR;
-    } else {
-        return BAD_VALUE;
-    }
-}
-
-status_t AudioParameter::getInt(const String8& key, int& value)
-{
-    String8 str8;
-    status_t result = get(key, str8);
-    value = 0;
-    if (result == NO_ERROR) {
-        int val;
-        if (sscanf(str8.string(), "%d", &val) == 1) {
-            value = val;
-        } else {
-            result = INVALID_OPERATION;
-        }
-    }
-    return result;
-}
-
-status_t AudioParameter::getFloat(const String8& key, float& value)
-{
-    String8 str8;
-    status_t result = get(key, str8);
-    value = 0;
-    if (result == NO_ERROR) {
-        float val;
-        if (sscanf(str8.string(), "%f", &val) == 1) {
-            value = val;
-        } else {
-            result = INVALID_OPERATION;
-        }
-    }
-    return result;
-}
-
-status_t AudioParameter::getAt(size_t index, String8& key, String8& value)
-{
-    if (mParameters.size() > index) {
-        key = mParameters.keyAt(index);
-        value = mParameters.valueAt(index);
-        return NO_ERROR;
-    } else {
-        return BAD_VALUE;
-    }
-}
-
-};  // namespace android
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -178,24 +178,34 @@ GonkCameraHardware::Init()
   DOM_CAMERA_LOGI("Sensor orientation: base=%d, offset=%d, final=%d\n", info.orientation, offset, mSensorOrientation);
 
   // Disable shutter sound in android CameraService because gaia camera app will play it
   mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
 
   mNativeWindow = new GonkNativeWindow();
   mNativeWindow->setNewFrameCallback(this);
   mCamera->setListener(this);
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+  mCamera->setPreviewTexture(mNativeWindow->getBufferQueue());
+#else
   mCamera->setPreviewTexture(mNativeWindow);
+#endif
   mInitialized = true;
 }
 
 sp<GonkCameraHardware>
 GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId)
 {
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+  sp<Camera> camera = Camera::connect(aCameraId, /* clientPackageName */String16("gonk.camera"), Camera::USE_CALLING_UID);
+#else
   sp<Camera> camera = Camera::connect(aCameraId);
+#endif
+
+
   if (camera.get() == nullptr) {
     return nullptr;
   }
   sp<GonkCameraHardware> cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera);
   return cameraHardware;
  }
 
 void
--- a/dom/camera/GonkCameraSource.cpp
+++ b/dom/camera/GonkCameraSource.cpp
@@ -1,10 +1,11 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
@@ -13,34 +14,46 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <base/basictypes.h>
 #include "nsDebug.h"
 #define DOM_CAMERA_LOG_LEVEL        3
 #include "CameraCommon.h"
-#define LOGD DOM_CAMERA_LOGA
-#define LOGV DOM_CAMERA_LOGI
-#define LOGI DOM_CAMERA_LOGI
-#define LOGW DOM_CAMERA_LOGW
-#define LOGE DOM_CAMERA_LOGE
+/*
+#define CS_LOGD(...) DOM_CAMERA_LOGA(__VA_ARGS__)
+#define CS_LOGV(...) DOM_CAMERA_LOGI(__VA_ARGS__)
+#define CS_LOGI(...) DOM_CAMERA_LOGI(__VA_ARGS__)
+#define CS_LOGW(...) DOM_CAMERA_LOGW(__VA_ARGS__)
+#define CS_LOGE(...) DOM_CAMERA_LOGE(__VA_ARGS__)
+*/
+
+#define CS_LOGD(fmt, ...) DOM_CAMERA_LOGA("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define CS_LOGV(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define CS_LOGI(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define CS_LOGW(fmt, ...) DOM_CAMERA_LOGW("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define CS_LOGE(fmt, ...) DOM_CAMERA_LOGE("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
 
 #include <OMX_Component.h>
+#include <binder/IPCThreadState.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <camera/CameraParameters.h>
+#include <utils/String8.h>
+#include <cutils/properties.h>
+
 #include "GonkCameraSource.h"
 #include "GonkCameraListener.h"
 #include "GonkCameraHwMgr.h"
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MetaData.h>
-#include <utils/String8.h>
-#include <cutils/properties.h>
 
 using namespace mozilla;
+
 namespace android {
 
 static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
 
 struct GonkCameraSourceListener : public GonkCameraListener {
     GonkCameraSourceListener(const sp<GonkCameraSource> &source);
 
     virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
@@ -63,22 +76,22 @@ private:
 GonkCameraSourceListener::GonkCameraSourceListener(const sp<GonkCameraSource> &source)
     : mSource(source) {
 }
 
 GonkCameraSourceListener::~GonkCameraSourceListener() {
 }
 
 void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
-    LOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
+    CS_LOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
 }
 
 void GonkCameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
                                     camera_frame_metadata_t *metadata) {
-    LOGV("postData(%d, ptr:%p, size:%d)",
+    CS_LOGV("postData(%d, ptr:%p, size:%d)",
          msgType, dataPtr->pointer(), dataPtr->size());
 
     sp<GonkCameraSource> source = mSource.promote();
     if (source.get() != NULL) {
         source->dataCallback(msgType, dataPtr);
     }
 }
 
@@ -87,17 +100,17 @@ void GonkCameraSourceListener::postDataT
 
     sp<GonkCameraSource> source = mSource.promote();
     if (source.get() != NULL) {
         source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr);
     }
 }
 
 static int32_t getColorFormat(const char* colorFormat) {
-    return OMX_COLOR_FormatYUV420SemiPlanar;
+    return OMX_COLOR_FormatYUV420SemiPlanar; //XXX nsGonkCameraControl uses only YUV420SemiPlanar
 
     if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
        return OMX_COLOR_FormatYUV420Planar;
     }
 
     if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) {
        return OMX_COLOR_FormatYUV422SemiPlanar;
     }
@@ -112,21 +125,25 @@ static int32_t getColorFormat(const char
 
     if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) {
        return OMX_COLOR_Format16bitRGB565;
     }
 
     if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) {
        return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar;
     }
-
-    LOGE("Uknown color format (%s), please add it to "
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+    if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE)) {
+        return OMX_COLOR_FormatAndroidOpaque;
+    }
+#endif
+    CS_LOGE("Uknown color format (%s), please add it to "
          "GonkCameraSource::getColorFormat", colorFormat);
 
-    CHECK_EQ(0, "Unknown color format");
+    CHECK(!"Unknown color format");
 }
 
 GonkCameraSource *GonkCameraSource::Create(
     const sp<GonkCameraHardware>& aCameraHw,
     Size videoSize,
     int32_t frameRate,
     bool storeMetaDataInVideoBuffers) {
 
@@ -138,16 +155,17 @@ GonkCameraSource *GonkCameraSource::Crea
 
 GonkCameraSource::GonkCameraSource(
     const sp<GonkCameraHardware>& aCameraHw,
     Size videoSize,
     int32_t frameRate,
     bool storeMetaDataInVideoBuffers)
     : mCameraHw(aCameraHw),
       mCameraFlags(0),
+      mNumInputBuffers(0),
       mVideoFrameRate(-1),
       mNumFramesReceived(0),
       mLastFrameTimestampUs(0),
       mStarted(false),
       mNumFramesEncoded(0),
       mTimeBetweenFrameCaptureUs(0),
       mFirstFrameTimeUs(0),
       mNumFramesDropped(0),
@@ -176,17 +194,17 @@ status_t GonkCameraSource::initCheck() c
  * @param height the video frame height in pixels
  * @param suppportedSizes the vector of sizes that we check against
  * @return true if the dimension (width and height) is supported.
  */
 static bool isVideoSizeSupported(
     int32_t width, int32_t height,
     const Vector<Size>& supportedSizes) {
 
-    LOGV("isVideoSizeSupported");
+    CS_LOGV("isVideoSizeSupported");
     for (size_t i = 0; i < supportedSizes.size(); ++i) {
         if (width  == supportedSizes[i].width &&
             height == supportedSizes[i].height) {
             return true;
         }
     }
     return false;
 }
@@ -213,17 +231,17 @@ static bool isVideoSizeSupported(
 static void getSupportedVideoSizes(
     const CameraParameters& params,
     bool *isSetVideoSizeSupported,
     Vector<Size>& sizes) {
 
     *isSetVideoSizeSupported = true;
     params.getSupportedVideoSizes(sizes);
     if (sizes.size() == 0) {
-        LOGD("Camera does not support setVideoSize()");
+        CS_LOGD("Camera does not support setVideoSize()");
         params.getSupportedPreviewSizes(sizes);
         *isSetVideoSizeSupported = false;
     }
 }
 
 /*
  * Check whether the camera has the supported color format
  * @param params CameraParameters to retrieve the information
@@ -253,70 +271,70 @@ status_t GonkCameraSource::isCameraColor
  * @param height the target video frame height in pixels
  * @param frameRate the target frame rate in frames per second.
  * @return OK if no error.
  */
 status_t GonkCameraSource::configureCamera(
         CameraParameters* params,
         int32_t width, int32_t height,
         int32_t frameRate) {
-    LOGV("configureCamera");
+    CS_LOGV("configureCamera");
     Vector<Size> sizes;
     bool isSetVideoSizeSupportedByCamera = true;
     getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes);
     bool isCameraParamChanged = false;
     if (width != -1 && height != -1) {
         if (!isVideoSizeSupported(width, height, sizes)) {
-            LOGE("Video dimension (%dx%d) is unsupported", width, height);
+            CS_LOGE("Video dimension (%dx%d) is unsupported", width, height);
             return BAD_VALUE;
         }
         if (isSetVideoSizeSupportedByCamera) {
             params->setVideoSize(width, height);
         } else {
             params->setPreviewSize(width, height);
         }
         isCameraParamChanged = true;
     } else if ((width == -1 && height != -1) ||
                (width != -1 && height == -1)) {
         // If one and only one of the width and height is -1
         // we reject such a request.
-        LOGE("Requested video size (%dx%d) is not supported", width, height);
+        CS_LOGE("Requested video size (%dx%d) is not supported", width, height);
         return BAD_VALUE;
     } else {  // width == -1 && height == -1
         // Do not configure the camera.
         // Use the current width and height value setting from the camera.
     }
 
     if (frameRate != -1) {
         CHECK(frameRate > 0 && frameRate <= 120);
         const char* supportedFrameRates =
                 params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES);
         CHECK(supportedFrameRates != NULL);
-        LOGV("Supported frame rates: %s", supportedFrameRates);
+        CS_LOGV("Supported frame rates: %s", supportedFrameRates);
         char buf[4];
         snprintf(buf, 4, "%d", frameRate);
         if (strstr(supportedFrameRates, buf) == NULL) {
-            LOGE("Requested frame rate (%d) is not supported: %s",
+            CS_LOGE("Requested frame rate (%d) is not supported: %s",
                 frameRate, supportedFrameRates);
             return BAD_VALUE;
         }
 
         // The frame rate is supported, set the camera to the requested value.
         params->setPreviewFrameRate(frameRate);
         isCameraParamChanged = true;
     } else {  // frameRate == -1
         // Do not configure the camera.
         // Use the current frame rate value setting from the camera
     }
 
     if (isCameraParamChanged) {
         // Either frame rate or frame size needs to be changed.
         if (OK != mCameraHw->PushParameters(*params)) {
-            LOGE("Could not change settings."
-                 " Someone else is using camera ?");
+            CS_LOGE("Could not change settings."
+                 " Someone else is using camera?");
             return -EBUSY;
         }
     }
     return OK;
 }
 
 /*
  * Check whether the requested video frame size
@@ -328,43 +346,43 @@ status_t GonkCameraSource::configureCame
  * @param the target video frame width in pixels to check against
  * @param the target video frame height in pixels to check against
  * @return OK if no error
  */
 status_t GonkCameraSource::checkVideoSize(
         const CameraParameters& params,
         int32_t width, int32_t height) {
 
-    LOGV("checkVideoSize");
+    CS_LOGV("checkVideoSize");
     // The actual video size is the same as the preview size
     // if the camera hal does not support separate video and
     // preview output. In this case, we retrieve the video
     // size from preview.
     int32_t frameWidthActual = -1;
     int32_t frameHeightActual = -1;
     Vector<Size> sizes;
     params.getSupportedVideoSizes(sizes);
     if (sizes.size() == 0) {
         // video size is the same as preview size
         params.getPreviewSize(&frameWidthActual, &frameHeightActual);
     } else {
         // video size may not be the same as preview
         params.getVideoSize(&frameWidthActual, &frameHeightActual);
     }
     if (frameWidthActual < 0 || frameHeightActual < 0) {
-        LOGE("Failed to retrieve video frame size (%dx%d)",
+        CS_LOGE("Failed to retrieve video frame size (%dx%d)",
                 frameWidthActual, frameHeightActual);
         return UNKNOWN_ERROR;
     }
 
     // Check the actual video frame size against the target/requested
     // video frame size.
     if (width != -1 && height != -1) {
         if (frameWidthActual != width || frameHeightActual != height) {
-            LOGE("Failed to set video frame size to %dx%d. "
+            CS_LOGE("Failed to set video frame size to %dx%d. "
                     "The actual video size is %dx%d ", width, height,
                     frameWidthActual, frameHeightActual);
             return UNKNOWN_ERROR;
         }
     }
 
     // Good now.
     mVideoSize.width = frameWidthActual;
@@ -380,38 +398,38 @@ status_t GonkCameraSource::checkVideoSiz
  * @param params CameraParameters to retrieve the information
  * @param the target video frame rate to check against
  * @return OK if no error.
  */
 status_t GonkCameraSource::checkFrameRate(
         const CameraParameters& params,
         int32_t frameRate) {
 
-    LOGV("checkFrameRate");
+    CS_LOGV("checkFrameRate");
     int32_t frameRateActual = params.getPreviewFrameRate();
     if (frameRateActual < 0) {
-        LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual);
+        CS_LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual);
         return UNKNOWN_ERROR;
     }
 
     // Check the actual video frame rate against the target/requested
     // video frame rate.
     if (frameRate != -1 && (frameRateActual - frameRate) != 0) {
-        LOGE("Failed to set preview frame rate to %d fps. The actual "
+        CS_LOGE("Failed to set preview frame rate to %d fps. The actual "
                 "frame rate is %d", frameRate, frameRateActual);
         return UNKNOWN_ERROR;
     }
 
     // Good now.
     mVideoFrameRate = frameRateActual;
     return OK;
 }
 
 /*
- * Initialize the CameraSource to so that it becomes
+ * Initialize the GonkCameraSource so that it becomes
  * ready for providing the video input streams as requested.
  * @param camera the camera object used for the video source
  * @param cameraId if camera == 0, use camera with this id
  *      as the video source
  * @param videoSize the target video frame size. If both
  *      width and height in videoSize is -1, use the current
  *      width and heigth settings by the camera
  * @param frameRate the target frame rate in frames per second.
@@ -423,17 +441,17 @@ status_t GonkCameraSource::checkFrameRat
  *
  * @return OK if no error.
  */
 status_t GonkCameraSource::init(
         Size videoSize,
         int32_t frameRate,
         bool storeMetaDataInVideoBuffers) {
 
-    LOGV("init");
+    CS_LOGV("init");
     status_t err = OK;
     //TODO: need to do something here to check the sanity of camera
 
     CameraParameters params;
     mCameraHw->PullParameters(params);
     if ((err = isCameraColorFormatSupported(params)) != OK) {
         return err;
     }
@@ -461,144 +479,136 @@ status_t GonkCameraSource::init(
     mIsMetaDataStoredInVideoBuffers = false;
     mCameraHw->StoreMetaDataInBuffers(false);
     if (storeMetaDataInVideoBuffers) {
         if (OK == mCameraHw->StoreMetaDataInBuffers(true)) {
             mIsMetaDataStoredInVideoBuffers = true;
         }
     }
 
-    const char *hfr_str = params.get("video-hfr");
-    int32_t hfr = -1;
-    if ( hfr_str != NULL ) {
-      hfr = atoi(hfr_str);
-    }
-    if(hfr < 0) {
-      LOGW("Invalid hfr value(%d) set from app. Disabling HFR.", hfr);
-      hfr = 0;
-    }
-
     int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
     if (glitchDurationUs > mGlitchDurationThresholdUs) {
         mGlitchDurationThresholdUs = glitchDurationUs;
     }
 
-    const char * k3dFrameArrangement = "3d-frame-format";
-    const char * arrangement = params.get(k3dFrameArrangement);
-    // XXX: just assume left/right for now since that's all the camera supports
-    bool want3D = (arrangement != NULL && !strcmp("left-right", arrangement));
-
     // XXX: query camera for the stride and slice height
     // when the capability becomes available.
     mMeta = new MetaData;
     mMeta->setCString(kKeyMIMEType,  MEDIA_MIMETYPE_VIDEO_RAW);
     mMeta->setInt32(kKeyColorFormat, mColorFormat);
     mMeta->setInt32(kKeyWidth,       mVideoSize.width);
     mMeta->setInt32(kKeyHeight,      mVideoSize.height);
     mMeta->setInt32(kKeyStride,      mVideoSize.width);
     mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
     mMeta->setInt32(kKeyFrameRate,   mVideoFrameRate);
-
     return OK;
 }
 
 GonkCameraSource::~GonkCameraSource() {
     if (mStarted) {
-        stop();
+        reset();
     } else if (mInitCheck == OK) {
         // Camera is initialized but because start() is never called,
         // the lock on Camera is never released(). This makes sure
         // Camera's lock is released in this case.
         // TODO: Don't think I need to do this
         releaseCamera();
     }
 }
 
 int GonkCameraSource::startCameraRecording() {
-    LOGV("startCameraRecording");
+    CS_LOGV("startCameraRecording");
     return mCameraHw->StartRecording();
 }
 
 status_t GonkCameraSource::start(MetaData *meta) {
     int rv;
 
-    LOGV("start");
+    CS_LOGV("start");
     CHECK(!mStarted);
     if (mInitCheck != OK) {
-        LOGE("GonkCameraSource is not initialized yet");
+        CS_LOGE("GonkCameraSource is not initialized yet");
         return mInitCheck;
     }
 
     char value[PROPERTY_VALUE_MAX];
     if (property_get("media.stagefright.record-stats", value, NULL)
         && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
         mCollectStats = true;
     }
 
     mStartTimeUs = 0;
-    int64_t startTimeUs;
-    if (meta && meta->findInt64(kKeyTime, &startTimeUs)) {
-        LOGV("Metadata enabled, startime: %lld us", startTimeUs);
-        mStartTimeUs = startTimeUs;
+    mNumInputBuffers = 0;
+    if (meta) {
+        int64_t startTimeUs;
+        if (meta->findInt64(kKeyTime, &startTimeUs)) {
+            mStartTimeUs = startTimeUs;
+        }
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+        int32_t nBuffers;
+        if (meta->findInt32(kKeyNumBuffers, &nBuffers)) {
+            CHECK_GT(nBuffers, 0);
+            mNumInputBuffers = nBuffers;
+        }
+#endif
     }
 
     // Register a listener with GonkCameraHardware so that we can get callbacks
     mCameraHw->SetListener(new GonkCameraSourceListener(this));
 
     rv = startCameraRecording();
 
     mStarted = (rv == OK);
     return rv;
 }
 
 void GonkCameraSource::stopCameraRecording() {
-    LOGV("stopCameraRecording");
+    CS_LOGV("stopCameraRecording");
     mCameraHw->StopRecording();
 }
 
 void GonkCameraSource::releaseCamera() {
-    LOGV("releaseCamera");
+    CS_LOGV("releaseCamera");
 }
 
-status_t GonkCameraSource::stop() {
-    LOGV("stop: E");
+status_t GonkCameraSource::reset() {
+    CS_LOGD("reset: E");
     Mutex::Autolock autoLock(mLock);
     mStarted = false;
     mFrameAvailableCondition.signal();
 
     releaseQueuedFrames();
     while (!mFramesBeingEncoded.empty()) {
         if (NO_ERROR !=
             mFrameCompleteCondition.waitRelative(mLock,
                     mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
-            LOGW("Timed out waiting for outstanding frames being encoded: %d",
+            CS_LOGW("Timed out waiting for outstanding frames being encoded: %d",
                 mFramesBeingEncoded.size());
         }
     }
-    LOGV("Calling stopCameraRecording");
     stopCameraRecording();
     releaseCamera();
 
     if (mCollectStats) {
-        LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
+        CS_LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
                 mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped,
                 mLastFrameTimestampUs - mFirstFrameTimeUs);
     }
 
     if (mNumGlitches > 0) {
-        LOGW("%d long delays between neighboring video frames", mNumGlitches);
+        CS_LOGW("%d long delays between neighboring video frames", mNumGlitches);
     }
 
     CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
-    LOGV("stop: X");
+    CS_LOGD("reset: X");
     return OK;
 }
 
 void GonkCameraSource::releaseRecordingFrame(const sp<IMemory>& frame) {
-    LOGV("releaseRecordingFrame");
+    CS_LOGV("releaseRecordingFrame");
     mCameraHw->ReleaseRecordingFrame(frame);
 }
 
 void GonkCameraSource::releaseQueuedFrames() {
     List<sp<IMemory> >::iterator it;
     while (!mFramesReceived.empty()) {
         it = mFramesReceived.begin();
         releaseRecordingFrame(*it);
@@ -611,36 +621,36 @@ sp<MetaData> GonkCameraSource::getFormat
     return mMeta;
 }
 
 void GonkCameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) {
     releaseRecordingFrame(frame);
 }
 
 void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) {
-    LOGV("signalBufferReturned: %p", buffer->data());
+    CS_LOGV("signalBufferReturned: %p", buffer->data());
     Mutex::Autolock autoLock(mLock);
     for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
          it != mFramesBeingEncoded.end(); ++it) {
         if ((*it)->pointer() ==  buffer->data()) {
             releaseOneRecordingFrame((*it));
             mFramesBeingEncoded.erase(it);
             ++mNumFramesEncoded;
             buffer->setObserver(0);
             buffer->release();
             mFrameCompleteCondition.signal();
             return;
         }
     }
-    CHECK_EQ(0, "signalBufferReturned: bogus buffer");
+    CHECK(!"signalBufferReturned: bogus buffer");
 }
 
 status_t GonkCameraSource::read(
         MediaBuffer **buffer, const ReadOptions *options) {
-    LOGV("read");
+    CS_LOGV("read");
 
     *buffer = NULL;
 
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
         return ERROR_UNSUPPORTED;
     }
@@ -650,17 +660,17 @@ status_t GonkCameraSource::read(
 
     {
         Mutex::Autolock autoLock(mLock);
         while (mStarted && mFramesReceived.empty()) {
             if (NO_ERROR !=
                 mFrameAvailableCondition.waitRelative(mLock,
                     mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
                 //TODO: check sanity of camera?
-                LOGW("Timed out waiting for incoming camera video frames: %lld us",
+                CS_LOGW("Timed out waiting for incoming camera video frames: %lld us",
                     mLastFrameTimestampUs);
             }
         }
         if (!mStarted) {
             return OK;
         }
         frame = *mFramesReceived.begin();
         mFramesReceived.erase(mFramesReceived.begin());
@@ -673,34 +683,33 @@ status_t GonkCameraSource::read(
         (*buffer)->add_ref();
         (*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
     }
     return OK;
 }
 
 void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs,
         int32_t msgType, const sp<IMemory> &data) {
-    LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
-    //LOGV("dataCallbackTimestamp: data %x size %d", data->pointer(), data->size());
+    CS_LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
     Mutex::Autolock autoLock(mLock);
     if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
-        LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
+        CS_LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
         releaseOneRecordingFrame(data);
         return;
     }
 
     if (mNumFramesReceived > 0) {
         CHECK(timestampUs > mLastFrameTimestampUs);
         if (timestampUs - mLastFrameTimestampUs > mGlitchDurationThresholdUs) {
             ++mNumGlitches;
         }
     }
 
     // May need to skip frame or modify timestamp. Currently implemented
-    // by the subclass GonkCameraSourceTimeLapse.
+    // by the subclass CameraSourceTimeLapse.
     if (skipCurrentFrame(timestampUs)) {
         releaseOneRecordingFrame(data);
         return;
     }
 
     mLastFrameTimestampUs = timestampUs;
     if (mNumFramesReceived == 0) {
         mFirstFrameTimeUs = timestampUs;
@@ -716,19 +725,19 @@ void GonkCameraSource::dataCallbackTimes
         }
     }
     ++mNumFramesReceived;
 
     CHECK(data != NULL && data->size() > 0);
     mFramesReceived.push_back(data);
     int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
     mFrameTimes.push_back(timeUs);
-    LOGV("initial delay: %lld, current time stamp: %lld",
+    CS_LOGV("initial delay: %lld, current time stamp: %lld",
         mStartTimeUs, timeUs);
     mFrameAvailableCondition.signal();
 }
 
 bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const {
-    LOGV("isMetaDataStoredInVideoBuffers");
+    CS_LOGV("isMetaDataStoredInVideoBuffers");
     return mIsMetaDataStoredInVideoBuffers;
 }
 
-} // namespace android
+}  // namespace android
--- a/dom/camera/GonkCameraSource.h
+++ b/dom/camera/GonkCameraSource.h
@@ -18,37 +18,36 @@
 
 #define GONK_CAMERA_SOURCE_H_
 
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaSource.h>
 #include <camera/CameraParameters.h>
 #include <utils/List.h>
 #include <utils/RefBase.h>
-#include <utils/threads.h>
+#include <utils/String16.h>
 
 #include "GonkCameraHwMgr.h"
 
 namespace android {
 
 class IMemory;
-class GonkCameraSourceListener;
 
 class GonkCameraSource : public MediaSource, public MediaBufferObserver {
 public:
 
     static GonkCameraSource *Create(const sp<GonkCameraHardware>& aCameraHw,
                                     Size videoSize,
                                     int32_t frameRate,
                                     bool storeMetaDataInVideoBuffers = false);
 
     virtual ~GonkCameraSource();
 
     virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
     /**
      * Check whether a GonkCameraSource object is properly initialized.
      * Must call this method before stop().
      * @return OK if initialization has successfully completed.
      */
@@ -80,16 +79,17 @@ protected:
 
     enum CameraFlags {
         FLAGS_SET_CAMERA = 1L << 0,
         FLAGS_HOT_CAMERA = 1L << 1,
     };
 
     int32_t  mCameraFlags;
     Size     mVideoSize;
+    int32_t  mNumInputBuffers;
     int32_t  mVideoFrameRate;
     int32_t  mColorFormat;
     status_t mInitCheck;
 
     sp<MetaData> mMeta;
 
     int64_t mStartTimeUs;
     int32_t mNumFramesReceived;
@@ -148,16 +148,17 @@ private:
 
     status_t checkVideoSize(const CameraParameters& params,
                     int32_t width, int32_t height);
 
     status_t checkFrameRate(const CameraParameters& params,
                     int32_t frameRate);
 
     void releaseCamera();
+    status_t reset();
 
     GonkCameraSource(const GonkCameraSource &);
     GonkCameraSource &operator=(const GonkCameraSource &);
 };
 
 }  // namespace android
 
 #endif  // GONK_CAMERA_SOURCE_H_
--- a/dom/camera/GonkRecorder.cpp
+++ b/dom/camera/GonkRecorder.cpp
@@ -1,229 +1,225 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2013 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-
-#include <media/AudioParameter.h>
+#include "nsDebug.h"
+#define DOM_CAMERA_LOG_LEVEL        3
+#include "CameraCommon.h"
+#include "GonkCameraSource.h"
 #include "GonkRecorder.h"
 
+#define RE_LOGD(fmt, ...) DOM_CAMERA_LOGA("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define RE_LOGV(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define RE_LOGI(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define RE_LOGW(fmt, ...) DOM_CAMERA_LOGW("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+#define RE_LOGE(fmt, ...) DOM_CAMERA_LOGE("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
+
+#include <binder/IPCThreadState.h>
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+# include <media/openmax/OMX_Audio.h>
+#endif
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/AMRWriter.h>
+#include <media/stagefright/AACWriter.h>
 #include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
 #include <media/MediaProfiles.h>
-#include <utils/String8.h>
 
 #include <utils/Errors.h>
 #include <sys/types.h>
 #include <ctype.h>
 #include <unistd.h>
 
+#include <cutils/properties.h>
 #include <system/audio.h>
 
-#include "ARTPWriter.h"
-
-#include <cutils/properties.h>
-
-#include "CameraCommon.h"
-#include "GonkCameraSource.h"
-
 namespace android {
 
 GonkRecorder::GonkRecorder()
     : mWriter(NULL),
       mOutputFd(-1),
       mAudioSource(AUDIO_SOURCE_CNT),
       mVideoSource(VIDEO_SOURCE_LIST_END),
-      mStarted(false),
-      mDisableAudio(false) {
+      mStarted(false) {
 
-    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+    RE_LOGV("Constructor");
     reset();
 }
 
 GonkRecorder::~GonkRecorder() {
-    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+    RE_LOGV("Destructor");
     stop();
 }
 
 status_t GonkRecorder::init() {
-    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+    RE_LOGV("init");
     return OK;
 }
 
 status_t GonkRecorder::setAudioSource(audio_source_t as) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, as);
-
+    RE_LOGV("setAudioSource: %d", as);
     if (as < AUDIO_SOURCE_DEFAULT ||
         as >= AUDIO_SOURCE_CNT) {
-        DOM_CAMERA_LOGE("Invalid audio source: %d", as);
+        RE_LOGE("Invalid audio source: %d", as);
         return BAD_VALUE;
     }
 
-    if (mDisableAudio) {
-        return OK;
-    }
-
     if (as == AUDIO_SOURCE_DEFAULT) {
         mAudioSource = AUDIO_SOURCE_MIC;
     } else {
         mAudioSource = as;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::setVideoSource(video_source vs) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, vs);
+    RE_LOGV("setVideoSource: %d", vs);
     if (vs < VIDEO_SOURCE_DEFAULT ||
         vs >= VIDEO_SOURCE_LIST_END) {
-        DOM_CAMERA_LOGE("Invalid video source: %d", vs);
+        RE_LOGE("Invalid video source: %d", vs);
         return BAD_VALUE;
     }
 
     if (vs == VIDEO_SOURCE_DEFAULT) {
         mVideoSource = VIDEO_SOURCE_CAMERA;
     } else {
         mVideoSource = vs;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::setOutputFormat(output_format of) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, of);
+    RE_LOGV("setOutputFormat: %d", of);
     if (of < OUTPUT_FORMAT_DEFAULT ||
         of >= OUTPUT_FORMAT_LIST_END) {
-        DOM_CAMERA_LOGE("Invalid output format: %d", of);
+        RE_LOGE("Invalid output format: %d", of);
         return BAD_VALUE;
     }
 
     if (of == OUTPUT_FORMAT_DEFAULT) {
         mOutputFormat = OUTPUT_FORMAT_THREE_GPP;
     } else {
         mOutputFormat = of;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::setAudioEncoder(audio_encoder ae) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, ae);
+    RE_LOGV("setAudioEncoder: %d", ae);
     if (ae < AUDIO_ENCODER_DEFAULT ||
         ae >= AUDIO_ENCODER_LIST_END) {
-        DOM_CAMERA_LOGE("Invalid audio encoder: %d", ae);
+        RE_LOGE("Invalid audio encoder: %d", ae);
         return BAD_VALUE;
     }
 
-    if (mDisableAudio) {
-        return OK;
-    }
-
     if (ae == AUDIO_ENCODER_DEFAULT) {
         mAudioEncoder = AUDIO_ENCODER_AMR_NB;
     } else {
         mAudioEncoder = ae;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::setVideoEncoder(video_encoder ve) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, ve);
+    RE_LOGV("setVideoEncoder: %d", ve);
     if (ve < VIDEO_ENCODER_DEFAULT ||
         ve >= VIDEO_ENCODER_LIST_END) {
-        DOM_CAMERA_LOGE("Invalid video encoder: %d", ve);
+        RE_LOGE("Invalid video encoder: %d", ve);
         return BAD_VALUE;
     }
 
     if (ve == VIDEO_ENCODER_DEFAULT) {
         mVideoEncoder = VIDEO_ENCODER_H263;
     } else {
         mVideoEncoder = ve;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::setVideoSize(int width, int height) {
-    DOM_CAMERA_LOGI("%s: %dx%d", __func__, width, height);
+    RE_LOGV("setVideoSize: %dx%d", width, height);
     if (width <= 0 || height <= 0) {
-        DOM_CAMERA_LOGE("Invalid video size: %dx%d", width, height);
+        RE_LOGE("Invalid video size: %dx%d", width, height);
         return BAD_VALUE;
     }
 
     // Additional check on the dimension will be performed later
     mVideoWidth = width;
     mVideoHeight = height;
 
     return OK;
 }
 
 status_t GonkRecorder::setVideoFrameRate(int frames_per_second) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, frames_per_second);
+    RE_LOGV("setVideoFrameRate: %d", frames_per_second);
     if ((frames_per_second <= 0 && frames_per_second != -1) ||
         frames_per_second > 120) {
-        DOM_CAMERA_LOGE("Invalid video frame rate: %d", frames_per_second);
+        RE_LOGE("Invalid video frame rate: %d", frames_per_second);
         return BAD_VALUE;
     }
 
     // Additional check on the frame rate will be performed later
     mFrameRate = frames_per_second;
 
     return OK;
 }
 
 status_t GonkRecorder::setOutputFile(const char *path) {
-    DOM_CAMERA_LOGE("setOutputFile(const char*) must not be called");
+    RE_LOGE("setOutputFile(const char*) must not be called");
     // We don't actually support this at all, as the media_server process
     // no longer has permissions to create files.
 
     return -EPERM;
 }
 
 status_t GonkRecorder::setOutputFile(int fd, int64_t offset, int64_t length) {
-    DOM_CAMERA_LOGI("%s: %d, %lld, %lld", __func__, fd, offset, length);
+    RE_LOGV("setOutputFile: %d, %lld, %lld", fd, offset, length);
     // These don't make any sense, do they?
-    CHECK_EQ(offset, 0);
-    CHECK_EQ(length, 0);
+    CHECK_EQ(offset, 0ll);
+    CHECK_EQ(length, 0ll);
 
     if (fd < 0) {
-        DOM_CAMERA_LOGE("Invalid file descriptor: %d", fd);
+        RE_LOGE("Invalid file descriptor: %d", fd);
         return -EBADF;
     }
 
     if (mOutputFd >= 0) {
         ::close(mOutputFd);
     }
     mOutputFd = dup(fd);
 
     return OK;
 }
 
-// Attempt to parse an int64_t literal optionally surrounded by whitespace,
+// Attempt to parse an int64 literal optionally surrounded by whitespace,
 // returns true on success, false otherwise.
 static bool safe_strtoi64(const char *s, int64_t *val) {
     char *end;
 
     // It is lame, but according to man page, we have to set errno to 0
     // before calling strtoll().
     errno = 0;
     *val = strtoll(s, &end, 10);
@@ -233,17 +229,17 @@ static bool safe_strtoi64(const char *s,
     }
 
     // Skip trailing whitespace
     while (isspace(*end)) {
         ++end;
     }
 
     // For a successful return, the string must contain nothing but a valid
-    // int64_t literal optionally surrounded by whitespace.
+    // int64 literal optionally surrounded by whitespace.
 
     return *end == '\0';
 }
 
 // Return true if the value is in [0, 0x007FFFFFFF]
 static bool safe_strtoi32(const char *s, int32_t *val) {
     int64_t temp;
     if (safe_strtoi64(s, &temp)) {
@@ -269,229 +265,224 @@ static void TrimString(String8 *s) {
     while (i > leading_space && isspace(data[i - 1])) {
         --i;
     }
 
     s->setTo(String8(&data[leading_space], i - leading_space));
 }
 
 status_t GonkRecorder::setParamAudioSamplingRate(int32_t sampleRate) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, sampleRate);
+    RE_LOGV("setParamAudioSamplingRate: %d", sampleRate);
     if (sampleRate <= 0) {
-        DOM_CAMERA_LOGE("Invalid audio sampling rate: %d", sampleRate);
+        RE_LOGE("Invalid audio sampling rate: %d", sampleRate);
         return BAD_VALUE;
     }
 
     // Additional check on the sample rate will be performed later.
     mSampleRate = sampleRate;
     return OK;
 }
 
 status_t GonkRecorder::setParamAudioNumberOfChannels(int32_t channels) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, channels);
+    RE_LOGV("setParamAudioNumberOfChannels: %d", channels);
     if (channels <= 0 || channels >= 3) {
-        DOM_CAMERA_LOGE("Invalid number of audio channels: %d", channels);
+        RE_LOGE("Invalid number of audio channels: %d", channels);
         return BAD_VALUE;
     }
 
     // Additional check on the number of channels will be performed later.
     mAudioChannels = channels;
     return OK;
 }
 
 status_t GonkRecorder::setParamAudioEncodingBitRate(int32_t bitRate) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, bitRate);
+    RE_LOGV("setParamAudioEncodingBitRate: %d", bitRate);
     if (bitRate <= 0) {
-        DOM_CAMERA_LOGE("Invalid audio encoding bit rate: %d", bitRate);
+        RE_LOGE("Invalid audio encoding bit rate: %d", bitRate);
         return BAD_VALUE;
     }
 
     // The target bit rate may not be exactly the same as the requested.
     // It depends on many factors, such as rate control, and the bit rate
     // range that a specific encoder supports. The mismatch between the
     // the target and requested bit rate will NOT be treated as an error.
     mAudioBitRate = bitRate;
     return OK;
 }
 
 status_t GonkRecorder::setParamVideoEncodingBitRate(int32_t bitRate) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, bitRate);
+    RE_LOGV("setParamVideoEncodingBitRate: %d", bitRate);
     if (bitRate <= 0) {
-        DOM_CAMERA_LOGE("Invalid video encoding bit rate: %d", bitRate);
+        RE_LOGE("Invalid video encoding bit rate: %d", bitRate);
         return BAD_VALUE;
     }
 
     // The target bit rate may not be exactly the same as the requested.
     // It depends on many factors, such as rate control, and the bit rate
     // range that a specific encoder supports. The mismatch between the
     // the target and requested bit rate will NOT be treated as an error.
     mVideoBitRate = bitRate;
     return OK;
 }
 
 // Always rotate clockwise, and only support 0, 90, 180 and 270 for now.
 status_t GonkRecorder::setParamVideoRotation(int32_t degrees) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, degrees);
+    RE_LOGV("setParamVideoRotation: %d", degrees);
     if (degrees < 0 || degrees % 90 != 0) {
-        DOM_CAMERA_LOGE("Unsupported video rotation angle: %d", degrees);
+        RE_LOGE("Unsupported video rotation angle: %d", degrees);
         return BAD_VALUE;
     }
     mRotationDegrees = degrees % 360;
     return OK;
 }
 
 status_t GonkRecorder::setParamMaxFileDurationUs(int64_t timeUs) {
-    DOM_CAMERA_LOGI("%s: %lld us", __func__, timeUs);
+    RE_LOGV("setParamMaxFileDurationUs: %lld us", timeUs);
 
     // This is meant for backward compatibility for MediaRecorder.java
     if (timeUs <= 0) {
-        DOM_CAMERA_LOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs);
+        RE_LOGW("Max file duration is not positive: %lld us. Disabling duration limit.", timeUs);
         timeUs = 0; // Disable the duration limit for zero or negative values.
     } else if (timeUs <= 100000LL) {  // XXX: 100 milli-seconds
-        DOM_CAMERA_LOGE("Max file duration is too short: %lld us", timeUs);
+        RE_LOGE("Max file duration is too short: %lld us", timeUs);
         return BAD_VALUE;
     }
 
     if (timeUs <= 15 * 1000000LL) {
-        DOM_CAMERA_LOGW("Target duration (%lld us) too short to be respected", timeUs);
+        RE_LOGW("Target duration (%lld us) too short to be respected", timeUs);
     }
     mMaxFileDurationUs = timeUs;
     return OK;
 }
 
 status_t GonkRecorder::setParamMaxFileSizeBytes(int64_t bytes) {
-    DOM_CAMERA_LOGI("%s: %lld bytes", __func__, bytes);
+    RE_LOGV("setParamMaxFileSizeBytes: %lld bytes", bytes);
 
     // This is meant for backward compatibility for MediaRecorder.java
     if (bytes <= 0) {
-        DOM_CAMERA_LOGW("Max file size is not positive: %lld bytes. "
+        RE_LOGW("Max file size is not positive: %lld bytes. "
              "Disabling file size limit.", bytes);
         bytes = 0; // Disable the file size limit for zero or negative values.
     } else if (bytes <= 1024) {  // XXX: 1 kB
-        DOM_CAMERA_LOGE("Max file size is too small: %lld bytes", bytes);
+        RE_LOGE("Max file size is too small: %lld bytes", bytes);
         return BAD_VALUE;
     }
 
     if (bytes <= 100 * 1024) {
-        DOM_CAMERA_LOGW("Target file size (%lld bytes) is too small to be respected", bytes);
-    }
-
-    if (bytes >= 0xffffffffLL) {
-        DOM_CAMERA_LOGW("Target file size (%lld bytes) too larger than supported, clip to 4GB", bytes);
-        bytes = 0xffffffffLL;
+        RE_LOGW("Target file size (%lld bytes) is too small to be respected", bytes);
     }
 
     mMaxFileSizeBytes = bytes;
     return OK;
 }
 
 status_t GonkRecorder::setParamInterleaveDuration(int32_t durationUs) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, durationUs);
+    RE_LOGV("setParamInterleaveDuration: %d", durationUs);
     if (durationUs <= 500000) {           //  500 ms
         // If interleave duration is too small, it is very inefficient to do
         // interleaving since the metadata overhead will count for a significant
         // portion of the saved contents
-        DOM_CAMERA_LOGE("Audio/video interleave duration is too small: %d us", durationUs);
+        RE_LOGE("Audio/video interleave duration is too small: %d us", durationUs);
         return BAD_VALUE;
     } else if (durationUs >= 10000000) {  // 10 seconds
         // If interleaving duration is too large, it can cause the recording
         // session to use too much memory since we have to save the output
         // data before we write them out
-        DOM_CAMERA_LOGE("Audio/video interleave duration is too large: %d us", durationUs);
+        RE_LOGE("Audio/video interleave duration is too large: %d us", durationUs);
         return BAD_VALUE;
     }
     mInterleaveDurationUs = durationUs;
     return OK;
 }
 
 // If seconds <  0, only the first frame is I frame, and rest are all P frames
 // If seconds == 0, all frames are encoded as I frames. No P frames
 // If seconds >  0, it is the time spacing (seconds) between 2 neighboring I frames
 status_t GonkRecorder::setParamVideoIFramesInterval(int32_t seconds) {
-    DOM_CAMERA_LOGI("%s: %d seconds", __func__, seconds);
+    RE_LOGV("setParamVideoIFramesInterval: %d seconds", seconds);
     mIFramesIntervalSec = seconds;
     return OK;
 }
 
 status_t GonkRecorder::setParam64BitFileOffset(bool use64Bit) {
-    DOM_CAMERA_LOGI("%s: %s", __func__,
+    RE_LOGV("setParam64BitFileOffset: %s",
         use64Bit? "use 64 bit file offset": "use 32 bit file offset");
     mUse64BitFileOffset = use64Bit;
     return OK;
 }
 
 status_t GonkRecorder::setParamVideoCameraId(int32_t cameraId) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, cameraId);
+    RE_LOGV("setParamVideoCameraId: %d", cameraId);
     if (cameraId < 0) {
         return BAD_VALUE;
     }
     mCameraId = cameraId;
     return OK;
 }
 
 status_t GonkRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
-    DOM_CAMERA_LOGI("%s: %lld", __func__, timeDurationUs);
+    RE_LOGV("setParamTrackTimeStatus: %lld", timeDurationUs);
     if (timeDurationUs < 20000) {  // Infeasible if shorter than 20 ms?
-        DOM_CAMERA_LOGE("Tracking time duration too short: %lld us", timeDurationUs);
+        RE_LOGE("Tracking time duration too short: %lld us", timeDurationUs);
         return BAD_VALUE;
     }
     mTrackEveryTimeDurationUs = timeDurationUs;
     return OK;
 }
 
 status_t GonkRecorder::setParamVideoEncoderProfile(int32_t profile) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, profile);
+    RE_LOGV("setParamVideoEncoderProfile: %d", profile);
 
     // Additional check will be done later when we load the encoder.
     // For now, we are accepting values defined in OpenMAX IL.
     mVideoEncoderProfile = profile;
     return OK;
 }
 
 status_t GonkRecorder::setParamVideoEncoderLevel(int32_t level) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, level);
+    RE_LOGV("setParamVideoEncoderLevel: %d", level);
 
     // Additional check will be done later when we load the encoder.
     // For now, we are accepting values defined in OpenMAX IL.
     mVideoEncoderLevel = level;
     return OK;
 }
 
 status_t GonkRecorder::setParamMovieTimeScale(int32_t timeScale) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, timeScale);
+    RE_LOGV("setParamMovieTimeScale: %d", timeScale);
 
     // The range is set to be the same as the audio's time scale range
     // since audio's time scale has a wider range.
     if (timeScale < 600 || timeScale > 96000) {
-        DOM_CAMERA_LOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale);
+        RE_LOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale);
         return BAD_VALUE;
     }
     mMovieTimeScale = timeScale;
     return OK;
 }
 
 status_t GonkRecorder::setParamVideoTimeScale(int32_t timeScale) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, timeScale);
+    RE_LOGV("setParamVideoTimeScale: %d", timeScale);
 
     // 60000 is chosen to make sure that each video frame from a 60-fps
     // video has 1000 ticks.
     if (timeScale < 600 || timeScale > 60000) {
-        DOM_CAMERA_LOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale);
+        RE_LOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale);
         return BAD_VALUE;
     }
     mVideoTimeScale = timeScale;
     return OK;
 }
 
 status_t GonkRecorder::setParamAudioTimeScale(int32_t timeScale) {
-    DOM_CAMERA_LOGI("%s: %d", __func__, timeScale);
+    RE_LOGV("setParamAudioTimeScale: %d", timeScale);
 
     // 96000 Hz is the highest sampling rate support in AAC.
     if (timeScale < 600 || timeScale > 96000) {
-        DOM_CAMERA_LOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale);
+        RE_LOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale);
         return BAD_VALUE;
     }
     mAudioTimeScale = timeScale;
     return OK;
 }
 
 status_t GonkRecorder::setParamGeoDataLongitude(
     int64_t longitudex10000) {
@@ -510,17 +501,17 @@ status_t GonkRecorder::setParamGeoDataLa
         return BAD_VALUE;
     }
     mLatitudex10000 = latitudex10000;
     return OK;
 }
 
 status_t GonkRecorder::setParameter(
         const String8 &key, const String8 &value) {
-    DOM_CAMERA_LOGI("%s: key (%s) => value (%s)", __func__, key.string(), value.string());
+    RE_LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
     if (key == "max-duration") {
         int64_t max_duration_ms;
         if (safe_strtoi64(value.string(), &max_duration_ms)) {
             return setParamMaxFileDurationUs(1000LL * max_duration_ms);
         }
     } else if (key == "max-filesize") {
         int64_t max_filesize_bytes;
         if (safe_strtoi64(value.string(), &max_filesize_bytes)) {
@@ -607,35 +598,35 @@ status_t GonkRecorder::setParameter(
             return setParamVideoCameraId(cameraId);
         }
     } else if (key == "video-param-time-scale") {
         int32_t timeScale;
         if (safe_strtoi32(value.string(), &timeScale)) {
             return setParamVideoTimeScale(timeScale);
         }
     } else {
-        DOM_CAMERA_LOGE("setParameter: failed to find key %s", key.string());
+        RE_LOGE("setParameter: failed to find key %s", key.string());
     }
     return BAD_VALUE;
 }
 
 status_t GonkRecorder::setParameters(const String8 &params) {
-    DOM_CAMERA_LOGI("%s: %s", __func__, params.string());
+    RE_LOGV("setParameters: %s", params.string());
     const char *cparams = params.string();
     const char *key_start = cparams;
     for (;;) {
         const char *equal_pos = strchr(key_start, '=');
         if (equal_pos == NULL) {
-            DOM_CAMERA_LOGE("Parameters %s miss a value", cparams);
+            RE_LOGE("Parameters %s miss a value", cparams);
             return BAD_VALUE;
         }
         String8 key(key_start, equal_pos - key_start);
         TrimString(&key);
         if (key.length() == 0) {
-            DOM_CAMERA_LOGE("Parameters %s contains an empty key", cparams);
+            RE_LOGE("Parameters %s contains an empty key", cparams);
             return BAD_VALUE;
         }
         const char *value_start = equal_pos + 1;
         const char *semicolon_pos = strchr(value_start, ';');
         String8 value;
         if (semicolon_pos == NULL) {
             value.setTo(value_start);
         } else {
@@ -653,36 +644,33 @@ status_t GonkRecorder::setParameters(con
 }
 
 status_t GonkRecorder::setListener(const sp<IMediaRecorderClient> &listener) {
     mListener = listener;
 
     return OK;
 }
 
-status_t GonkRecorder::prepare() {
-  DOM_CAMERA_LOGI(" %s E", __func__ );
+status_t GonkRecorder::setClientName(const String16& clientName) {
+    mClientName = clientName;
 
-  if(mVideoSource != VIDEO_SOURCE_LIST_END && mVideoEncoder != VIDEO_ENCODER_LIST_END && mVideoHeight && mVideoWidth &&             /*Video recording*/
-         (mMaxFileDurationUs <=0 ||             /*Max duration is not set*/
-         (mVideoHeight * mVideoWidth < 720 * 1280 && mMaxFileDurationUs > 30*60*1000*1000) ||
-         (mVideoHeight * mVideoWidth >= 720 * 1280 && mMaxFileDurationUs > 10*60*1000*1000))) {
-    /*Above Check can be further optimized for lower resolutions to reduce file size*/
-    DOM_CAMERA_LOGI("File is huge so setting 64 bit file offsets");
-    setParam64BitFileOffset(true);
-  }
-  DOM_CAMERA_LOGI(" %s X", __func__ );
-  return OK;
+    return OK;
+}
+
+status_t GonkRecorder::prepare() {
+    return OK;
 }
 
 status_t GonkRecorder::start() {
-    CHECK(mOutputFd >= 0);
+    CHECK_GE(mOutputFd, 0);
 
+    // Get UID here for permission checking
+    mClientUid = IPCThreadState::self()->getCallingUid();
     if (mWriter != NULL) {
-        DOM_CAMERA_LOGE("File writer is not available");
+        RE_LOGE("File writer is not avaialble");
         return UNKNOWN_ERROR;
     }
 
     status_t status = OK;
 
     switch (mOutputFormat) {
         case OUTPUT_FORMAT_DEFAULT:
         case OUTPUT_FORMAT_THREE_GPP:
@@ -690,62 +678,84 @@ status_t GonkRecorder::start() {
             status = startMPEG4Recording();
             break;
 
         case OUTPUT_FORMAT_AMR_NB:
         case OUTPUT_FORMAT_AMR_WB:
             status = startAMRRecording();
             break;
 
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+        case OUTPUT_FORMAT_AAC_ADIF:
+        case OUTPUT_FORMAT_AAC_ADTS:
+            status = startAACRecording();
+            break;
+#endif
+
+        case OUTPUT_FORMAT_RTP_AVP:
+            status = startRTPRecording();
+            break;
+
         case OUTPUT_FORMAT_MPEG2TS:
             status = startMPEG2TSRecording();
-		    break;
+            break;
+
         default:
-            DOM_CAMERA_LOGE("Unsupported output file format: %d", mOutputFormat);
+            RE_LOGE("Unsupported output file format: %d", mOutputFormat);
             status = UNKNOWN_ERROR;
             break;
     }
 
     if ((status == OK) && (!mStarted)) {
         mStarted = true;
     }
 
     return status;
 }
 
 sp<MediaSource> GonkRecorder::createAudioSource() {
-
     sp<AudioSource> audioSource =
         new AudioSource(
                 mAudioSource,
                 mSampleRate,
                 mAudioChannels);
 
     status_t err = audioSource->initCheck();
 
     if (err != OK) {
-        DOM_CAMERA_LOGE("audio source is not initialized");
+        RE_LOGE("audio source is not initialized");
         return NULL;
     }
 
     sp<MetaData> encMeta = new MetaData;
     const char *mime;
     switch (mAudioEncoder) {
         case AUDIO_ENCODER_AMR_NB:
         case AUDIO_ENCODER_DEFAULT:
             mime = MEDIA_MIMETYPE_AUDIO_AMR_NB;
             break;
         case AUDIO_ENCODER_AMR_WB:
             mime = MEDIA_MIMETYPE_AUDIO_AMR_WB;
             break;
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
         case AUDIO_ENCODER_AAC:
             mime = MEDIA_MIMETYPE_AUDIO_AAC;
+            encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectLC);
             break;
+        case AUDIO_ENCODER_HE_AAC:
+            mime = MEDIA_MIMETYPE_AUDIO_AAC;
+            encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectHE);
+            break;
+        case AUDIO_ENCODER_AAC_ELD:
+            mime = MEDIA_MIMETYPE_AUDIO_AAC;
+            encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectELD);
+            break;
+#endif
         default:
-            DOM_CAMERA_LOGE("Unknown audio encoder: %d", mAudioEncoder);
+            RE_LOGE("Unknown audio encoder: %d", mAudioEncoder);
             return NULL;
     }
     encMeta->setCString(kKeyMIMEType, mime);
 
     int32_t maxInputSize;
     CHECK(audioSource->getFormat()->findInt32(
                 kKeyMaxInputSize, &maxInputSize));
 
@@ -756,57 +766,78 @@ sp<MediaSource> GonkRecorder::createAudi
     if (mAudioTimeScale > 0) {
         encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
     }
 
     // OMXClient::connect() always returns OK and abort's fatally if
     // it can't connect.
     OMXClient client;
     // CHECK_EQ causes an abort if the given condition fails.
-    CHECK_EQ(client.connect(), OK);
-
+    CHECK_EQ(client.connect(), (status_t)OK);
     sp<MediaSource> audioEncoder =
         OMXCodec::Create(client.interface(), encMeta,
                          true /* createEncoder */, audioSource);
     mAudioSourceNode = audioSource;
 
     return audioEncoder;
 }
 
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+status_t GonkRecorder::startAACRecording() {
+    // FIXME:
+    // Add support for OUTPUT_FORMAT_AAC_ADIF
+    CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS);
+
+    CHECK(mAudioEncoder == AUDIO_ENCODER_AAC ||
+          mAudioEncoder == AUDIO_ENCODER_HE_AAC ||
+          mAudioEncoder == AUDIO_ENCODER_AAC_ELD);
+    CHECK(mAudioSource != AUDIO_SOURCE_CNT);
+
+    mWriter = new AACWriter(mOutputFd);
+    status_t status = startRawAudioRecording();
+    if (status != OK) {
+        mWriter.clear();
+        mWriter = NULL;
+    }
+
+    return status;
+}
+#endif
+
 status_t GonkRecorder::startAMRRecording() {
     CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB ||
           mOutputFormat == OUTPUT_FORMAT_AMR_WB);
 
     if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) {
         if (mAudioEncoder != AUDIO_ENCODER_DEFAULT &&
             mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
-            DOM_CAMERA_LOGE("Invalid encoder %d used for AMRNB recording",
+            RE_LOGE("Invalid encoder %d used for AMRNB recording",
                     mAudioEncoder);
             return BAD_VALUE;
         }
     } else {  // mOutputFormat must be OUTPUT_FORMAT_AMR_WB
         if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
-            DOM_CAMERA_LOGE("Invalid encoder %d used for AMRWB recording",
+            RE_LOGE("Invlaid encoder %d used for AMRWB recording",
                     mAudioEncoder);
             return BAD_VALUE;
         }
     }
 
     mWriter = new AMRWriter(mOutputFd);
     status_t status = startRawAudioRecording();
     if (status != OK) {
         mWriter.clear();
         mWriter = NULL;
     }
     return status;
 }
 
 status_t GonkRecorder::startRawAudioRecording() {
     if (mAudioSource >= AUDIO_SOURCE_CNT) {
-        DOM_CAMERA_LOGE("Invalid audio source: %d", mAudioSource);
+        RE_LOGE("Invalid audio source: %d", mAudioSource);
         return BAD_VALUE;
     }
 
     status_t status = BAD_VALUE;
     if (OK != (status = checkAudioEncoderCapabilities())) {
         return status;
     }
 
@@ -825,26 +856,33 @@ status_t GonkRecorder::startRawAudioReco
         mWriter->setMaxFileSize(mMaxFileSizeBytes);
     }
     mWriter->setListener(mListener);
     mWriter->start();
 
     return OK;
 }
 
+status_t GonkRecorder::startRTPRecording() {
+    return INVALID_OPERATION;
+}
+
 status_t GonkRecorder::startMPEG2TSRecording() {
     CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS);
 
     sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd);
 
     if (mAudioSource != AUDIO_SOURCE_CNT) {
-        if (mAudioEncoder != AUDIO_ENCODER_AAC) {
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+        if (mAudioEncoder != AUDIO_ENCODER_AAC &&
+            mAudioEncoder != AUDIO_ENCODER_HE_AAC &&
+            mAudioEncoder != AUDIO_ENCODER_AAC_ELD) {
             return ERROR_UNSUPPORTED;
         }
-
+#endif
         status_t err = setupAudioEncoder(writer);
 
         if (err != OK) {
             return err;
         }
     }
 
     if (mVideoSource < VIDEO_SOURCE_LIST_END) {
@@ -877,81 +915,82 @@ status_t GonkRecorder::startMPEG2TSRecor
     }
 
     mWriter = writer;
 
     return mWriter->start();
 }
 
 void GonkRecorder::clipVideoFrameRate() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mVideoEncoder);
+    RE_LOGV("clipVideoFrameRate: encoder %d", mVideoEncoder);
     int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.fps.min", mVideoEncoder);
     int maxFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.fps.max", mVideoEncoder);
-    if (mFrameRate < minFrameRate && mFrameRate != -1) {
-        DOM_CAMERA_LOGW("Intended video encoding frame rate (%d fps) is too small"
+    if (mFrameRate < minFrameRate && minFrameRate != -1) {
+        RE_LOGW("Intended video encoding frame rate (%d fps) is too small"
              " and will be set to (%d fps)", mFrameRate, minFrameRate);
         mFrameRate = minFrameRate;
-    } else if (mFrameRate > maxFrameRate) {
-        DOM_CAMERA_LOGW("Intended video encoding frame rate (%d fps) is too large"
+    } else if (mFrameRate > maxFrameRate && maxFrameRate != -1) {
+        RE_LOGW("Intended video encoding frame rate (%d fps) is too large"
              " and will be set to (%d fps)", mFrameRate, maxFrameRate);
         mFrameRate = maxFrameRate;
     }
 }
 
 void GonkRecorder::clipVideoBitRate() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mVideoEncoder);
+    RE_LOGV("clipVideoBitRate: encoder %d", mVideoEncoder);
     int minBitRate = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.bps.min", mVideoEncoder);
     int maxBitRate = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.bps.max", mVideoEncoder);
-    if (mVideoBitRate < minBitRate) {
-        DOM_CAMERA_LOGW("Intended video encoding bit rate (%d bps) is too small"
+    if (mVideoBitRate < minBitRate && minBitRate != -1) {
+        RE_LOGW("Intended video encoding bit rate (%d bps) is too small"
              " and will be set to (%d bps)", mVideoBitRate, minBitRate);
         mVideoBitRate = minBitRate;
-    } else if (mVideoBitRate > maxBitRate) {
-        DOM_CAMERA_LOGW("Intended video encoding bit rate (%d bps) is too large"
+    } else if (mVideoBitRate > maxBitRate && maxBitRate != -1) {
+        RE_LOGW("Intended video encoding bit rate (%d bps) is too large"
              " and will be set to (%d bps)", mVideoBitRate, maxBitRate);
         mVideoBitRate = maxBitRate;
     }
 }
 
 void GonkRecorder::clipVideoFrameWidth() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mVideoEncoder);
+    RE_LOGV("clipVideoFrameWidth: encoder %d", mVideoEncoder);
     int minFrameWidth = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.width.min", mVideoEncoder);
     int maxFrameWidth = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.width.max", mVideoEncoder);
-    if (mVideoWidth < minFrameWidth) {
-        DOM_CAMERA_LOGW("Intended video encoding frame width (%d) is too small"
+    if (mVideoWidth < minFrameWidth && minFrameWidth != -1) {
+        RE_LOGW("Intended video encoding frame width (%d) is too small"
              " and will be set to (%d)", mVideoWidth, minFrameWidth);
         mVideoWidth = minFrameWidth;
-    } else if (mVideoWidth > maxFrameWidth) {
-        DOM_CAMERA_LOGW("Intended video encoding frame width (%d) is too large"
+    } else if (mVideoWidth > maxFrameWidth && maxFrameWidth != -1) {
+        RE_LOGW("Intended video encoding frame width (%d) is too large"
              " and will be set to (%d)", mVideoWidth, maxFrameWidth);
         mVideoWidth = maxFrameWidth;
     }
 }
 
 status_t GonkRecorder::checkVideoEncoderCapabilities() {
-        // Dont clip for time lapse capture as encoder will have enough
-        // time to encode because of slow capture rate of time lapse.
-        clipVideoBitRate();
-        clipVideoFrameRate();
-        clipVideoFrameWidth();
-        clipVideoFrameHeight();
-        setDefaultProfileIfNecessary();
+
+    // Dont clip for time lapse capture as encoder will have enough
+    // time to encode because of slow capture rate of time lapse.
+    clipVideoBitRate();
+    clipVideoFrameRate();
+    clipVideoFrameWidth();
+    clipVideoFrameHeight();
+    setDefaultProfileIfNecessary();
     return OK;
 }
 
 // Set to use AVC baseline profile if the encoding parameters matches
 // CAMCORDER_QUALITY_LOW profile; this is for the sake of MMS service.
 void GonkRecorder::setDefaultProfileIfNecessary() {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("setDefaultProfileIfNecessary");
 
     camcorder_quality quality = CAMCORDER_QUALITY_LOW;
 
     int64_t durationUs   = mEncoderProfiles->getCamcorderProfileParamByName(
                                 "duration", mCameraId, quality) * 1000000LL;
 
     int fileFormat       = mEncoderProfiles->getCamcorderProfileParamByName(
                                 "file.format", mCameraId, quality);
@@ -990,107 +1029,107 @@ void GonkRecorder::setDefaultProfileIfNe
         videoFrameRate == mFrameRate &&
         videoFrameWidth == mVideoWidth &&
         videoFrameHeight == mVideoHeight &&
         audioCodec == mAudioEncoder &&
         audioBitRate == mAudioBitRate &&
         audioSampleRate == mSampleRate &&
         audioChannels == mAudioChannels) {
         if (videoCodec == VIDEO_ENCODER_H264) {
-            DOM_CAMERA_LOGI("Force to use AVC baseline profile");
+            RE_LOGI("Force to use AVC baseline profile");
             setParamVideoEncoderProfile(OMX_VIDEO_AVCProfileBaseline);
         }
     }
 }
 
 status_t GonkRecorder::checkAudioEncoderCapabilities() {
     clipAudioBitRate();
     clipAudioSampleRate();
     clipNumberOfAudioChannels();
     return OK;
 }
 
 void GonkRecorder::clipAudioBitRate() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mAudioEncoder);
+    RE_LOGV("clipAudioBitRate: encoder %d", mAudioEncoder);
 
     int minAudioBitRate =
             mEncoderProfiles->getAudioEncoderParamByName(
                 "enc.aud.bps.min", mAudioEncoder);
-    if (mAudioBitRate < minAudioBitRate) {
-        DOM_CAMERA_LOGW("Intended audio encoding bit rate (%d) is too small"
+    if (minAudioBitRate != -1 && mAudioBitRate < minAudioBitRate) {
+        RE_LOGW("Intended audio encoding bit rate (%d) is too small"
             " and will be set to (%d)", mAudioBitRate, minAudioBitRate);
         mAudioBitRate = minAudioBitRate;
     }
 
     int maxAudioBitRate =
             mEncoderProfiles->getAudioEncoderParamByName(
                 "enc.aud.bps.max", mAudioEncoder);
-    if (mAudioBitRate > maxAudioBitRate) {
-        DOM_CAMERA_LOGW("Intended audio encoding bit rate (%d) is too large"
+    if (maxAudioBitRate != -1 && mAudioBitRate > maxAudioBitRate) {
+        RE_LOGW("Intended audio encoding bit rate (%d) is too large"
             " and will be set to (%d)", mAudioBitRate, maxAudioBitRate);
         mAudioBitRate = maxAudioBitRate;
     }
 }
 
 void GonkRecorder::clipAudioSampleRate() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mAudioEncoder);
+    RE_LOGV("clipAudioSampleRate: encoder %d", mAudioEncoder);
 
     int minSampleRate =
             mEncoderProfiles->getAudioEncoderParamByName(
                 "enc.aud.hz.min", mAudioEncoder);
-    if (mSampleRate < minSampleRate) {
-        DOM_CAMERA_LOGW("Intended audio sample rate (%d) is too small"
+    if (minSampleRate != -1 && mSampleRate < minSampleRate) {
+        RE_LOGW("Intended audio sample rate (%d) is too small"
             " and will be set to (%d)", mSampleRate, minSampleRate);
         mSampleRate = minSampleRate;
     }
 
     int maxSampleRate =
             mEncoderProfiles->getAudioEncoderParamByName(
                 "enc.aud.hz.max", mAudioEncoder);
-    if (mSampleRate > maxSampleRate) {
-        DOM_CAMERA_LOGW("Intended audio sample rate (%d) is too large"
+    if (maxSampleRate != -1 && mSampleRate > maxSampleRate) {
+        RE_LOGW("Intended audio sample rate (%d) is too large"
             " and will be set to (%d)", mSampleRate, maxSampleRate);
         mSampleRate = maxSampleRate;
     }
 }
 
 void GonkRecorder::clipNumberOfAudioChannels() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mAudioEncoder);
+    RE_LOGV("clipNumberOfAudioChannels: encoder %d", mAudioEncoder);
 
     int minChannels =
             mEncoderProfiles->getAudioEncoderParamByName(
                 "enc.aud.ch.min", mAudioEncoder);
-    if (mAudioChannels < minChannels) {
-        DOM_CAMERA_LOGW("Intended number of audio channels (%d) is too small"
+    if (minChannels != -1 && mAudioChannels < minChannels) {
+        RE_LOGW("Intended number of audio channels (%d) is too small"
             " and will be set to (%d)", mAudioChannels, minChannels);
         mAudioChannels = minChannels;
     }
 
     int maxChannels =
             mEncoderProfiles->getAudioEncoderParamByName(
                 "enc.aud.ch.max", mAudioEncoder);
-    if (mAudioChannels > maxChannels) {
-        DOM_CAMERA_LOGW("Intended number of audio channels (%d) is too large"
+    if (maxChannels != -1 && mAudioChannels > maxChannels) {
+        RE_LOGW("Intended number of audio channels (%d) is too large"
             " and will be set to (%d)", mAudioChannels, maxChannels);
         mAudioChannels = maxChannels;
     }
 }
 
 void GonkRecorder::clipVideoFrameHeight() {
-    DOM_CAMERA_LOGI("%s: encoder %d", __func__, mVideoEncoder);
+    RE_LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder);
     int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.height.min", mVideoEncoder);
     int maxFrameHeight = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.height.max", mVideoEncoder);
-    if (mVideoHeight < minFrameHeight) {
-        DOM_CAMERA_LOGW("Intended video encoding frame height (%d) is too small"
+    if (minFrameHeight != -1 && mVideoHeight < minFrameHeight) {
+        RE_LOGW("Intended video encoding frame height (%d) is too small"
              " and will be set to (%d)", mVideoHeight, minFrameHeight);
         mVideoHeight = minFrameHeight;
-    } else if (mVideoHeight > maxFrameHeight) {
-        DOM_CAMERA_LOGW("Intended video encoding frame height (%d) is too large"
+    } else if (maxFrameHeight != -1 && mVideoHeight > maxFrameHeight) {
+        RE_LOGW("Intended video encoding frame height (%d) is too large"
              " and will be set to (%d)", mVideoHeight, maxFrameHeight);
         mVideoHeight = maxFrameHeight;
     }
 }
 
 // Set up the appropriate MediaSource depending on the chosen option
 status_t GonkRecorder::setupMediaSource(
                       sp<MediaSource> *mediaSource) {
@@ -1139,17 +1178,17 @@ status_t GonkRecorder::setupCameraSource
     }
 
     // When frame rate is not set, the actual frame rate will be set to
     // the current frame rate being used.
     if (mFrameRate == -1) {
         int32_t frameRate = 0;
         CHECK ((*cameraSource)->getFormat()->findInt32(
                     kKeyFrameRate, &frameRate));
-        DOM_CAMERA_LOGI("Frame rate is not explicitly set. Use the current frame "
+        RE_LOGI("Frame rate is not explicitly set. Use the current frame "
              "rate (%d fps)", frameRate);
         mFrameRate = frameRate;
     }
 
     CHECK(mFrameRate != -1);
 
     mIsMetaDataStoredInVideoBuffers =
         (*cameraSource)->isMetaDataStoredInVideoBuffers();
@@ -1198,94 +1237,40 @@ status_t GonkRecorder::setupVideoEncoder
     enc_meta->setInt32(kKeyHeight, height);
     enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec);
     enc_meta->setInt32(kKeyStride, stride);
     enc_meta->setInt32(kKeySliceHeight, sliceHeight);
     enc_meta->setInt32(kKeyColorFormat, colorFormat);
     if (mVideoTimeScale > 0) {
         enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
     }
-
-    /*
-     * can set profile from the app as a parameter.
-     * For the mean time, set from shell
-     */
-
-    char value[PROPERTY_VALUE_MAX];
-    bool customProfile = false;
-
-    if (property_get("encoder.video.profile", value, NULL) > 0) {
-        customProfile = true;
-    }
-
-    if (customProfile) {
-        switch ( mVideoEncoder ) {
-        case VIDEO_ENCODER_H264:
-            if (strncmp("base", value, 4) == 0) {
-                mVideoEncoderProfile = OMX_VIDEO_AVCProfileBaseline;
-                DOM_CAMERA_LOGI("H264 Baseline Profile");
-            }
-            else if (strncmp("main", value, 4) == 0) {
-                mVideoEncoderProfile = OMX_VIDEO_AVCProfileMain;
-                DOM_CAMERA_LOGI("H264 Main Profile");
-            }
-            else if (strncmp("high", value, 4) == 0) {
-                mVideoEncoderProfile = OMX_VIDEO_AVCProfileHigh;
-                DOM_CAMERA_LOGI("H264 High Profile");
-            }
-            else {
-               DOM_CAMERA_LOGW("Unsupported H264 Profile");
-            }
-            break;
-        case VIDEO_ENCODER_MPEG_4_SP:
-            if (strncmp("simple", value, 5) == 0 ) {
-                mVideoEncoderProfile = OMX_VIDEO_MPEG4ProfileSimple;
-                DOM_CAMERA_LOGI("MPEG4 Simple profile");
-            }
-            else if (strncmp("asp", value, 3) == 0 ) {
-                mVideoEncoderProfile = OMX_VIDEO_MPEG4ProfileAdvancedSimple;
-                DOM_CAMERA_LOGI("MPEG4 Advanced Simple Profile");
-            }
-            else {
-                DOM_CAMERA_LOGW("Unsupported MPEG4 Profile");
-            }
-            break;
-        default:
-            DOM_CAMERA_LOGW("No custom profile support for other codecs");
-            break;
-        }
-    }
-
     if (mVideoEncoderProfile != -1) {
         enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
     }
     if (mVideoEncoderLevel != -1) {
         enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel);
     }
 
-    uint32_t encoder_flags = 0;
-    if (mIsMetaDataStoredInVideoBuffers) {
-        DOM_CAMERA_LOGW("Camera source supports metadata mode, create OMXCodec for metadata");
-        encoder_flags |= OMXCodec::kHardwareCodecsOnly;
-        encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
-        encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime;
-    }
-
     // OMXClient::connect() always returns OK and abort's fatally if
     // it can't connect.
     OMXClient client;
     // CHECK_EQ causes an abort if the given condition fails.
-    CHECK_EQ(client.connect(), OK);
+    CHECK_EQ(client.connect(), (status_t)OK);
+
+    uint32_t encoder_flags = 0;
+    if (mIsMetaDataStoredInVideoBuffers) {
+        encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
+    }
 
     sp<MediaSource> encoder = OMXCodec::Create(
             client.interface(), enc_meta,
             true /* createEncoder */, cameraSource,
             NULL, encoder_flags);
     if (encoder == NULL) {
-        DOM_CAMERA_LOGW("Failed to create the encoder");
+        RE_LOGW("Failed to create the encoder");
         // When the encoder fails to be created, we need
         // release the camera source due to the camera's lock
         // and unlock mechanism.
         cameraSource->stop();
         return UNKNOWN_ERROR;
     }
 
     *source = encoder;
@@ -1297,21 +1282,25 @@ status_t GonkRecorder::setupAudioEncoder
     status_t status = BAD_VALUE;
     if (OK != (status = checkAudioEncoderCapabilities())) {
         return status;
     }
 
     switch(mAudioEncoder) {
         case AUDIO_ENCODER_AMR_NB:
         case AUDIO_ENCODER_AMR_WB:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
         case AUDIO_ENCODER_AAC:
+        case AUDIO_ENCODER_HE_AAC:
+        case AUDIO_ENCODER_AAC_ELD:
+#endif
             break;
 
         default:
-            DOM_CAMERA_LOGE("Unsupported audio encoder: %d", mAudioEncoder);
+            RE_LOGE("Unsupported audio encoder: %d", mAudioEncoder);
             return UNKNOWN_ERROR;
     }
 
     sp<MediaSource> audioEncoder = createAudioSource();
     if (audioEncoder == NULL) {
         return UNKNOWN_ERROR;
     }
 
@@ -1394,17 +1383,17 @@ void GonkRecorder::setupMPEG4MetaData(in
     }
     if (mTrackEveryTimeDurationUs > 0) {
         (*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
     }
 
     char value[PROPERTY_VALUE_MAX];
     if (property_get("debug.camcorder.rotation", value, 0) > 0 && atoi(value) >= 0) {
         mRotationDegrees = atoi(value);
-        DOM_CAMERA_LOGI("Setting rotation to %d", mRotationDegrees );
+        RE_LOGI("Setting rotation to %d", mRotationDegrees );
     }
 
     if (mRotationDegrees != 0) {
         (*meta)->setInt32(kKeyRotation, mRotationDegrees);
     }
 }
 
 status_t GonkRecorder::startMPEG4Recording() {
@@ -1431,32 +1420,32 @@ status_t GonkRecorder::startMPEG4Recordi
     if (err != OK) {
         return err;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::pause() {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("pause");
     if (mWriter == NULL) {
         return UNKNOWN_ERROR;
     }
     mWriter->pause();
 
     if (mStarted) {
         mStarted = false;
     }
 
 
     return OK;
 }
 
 status_t GonkRecorder::stop() {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("stop");
     status_t err = OK;
 
     if (mWriter != NULL) {
         err = mWriter->stop();
         mWriter.clear();
     }
 
     if (mOutputFd >= 0) {
@@ -1468,24 +1457,24 @@ status_t GonkRecorder::stop() {
         mStarted = false;
     }
 
 
     return err;
 }
 
 status_t GonkRecorder::close() {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("close");
     stop();
 
     return OK;
 }
 
 status_t GonkRecorder::reset() {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("reset");
     stop();
 
     // No audio or video source by default
     mAudioSource = AUDIO_SOURCE_CNT;
     mVideoSource = VIDEO_SOURCE_LIST_END;
 
     // Default parameters
     mOutputFormat  = OUTPUT_FORMAT_THREE_GPP;
@@ -1494,17 +1483,17 @@ status_t GonkRecorder::reset() {
     mVideoWidth    = 176;
     mVideoHeight   = 144;
     mFrameRate     = -1;
     mVideoBitRate  = 192000;
     mSampleRate    = 8000;
     mAudioChannels = 1;
     mAudioBitRate  = 12200;
     mInterleaveDurationUs = 0;
-    mIFramesIntervalSec = 2;
+    mIFramesIntervalSec = 1;
     mAudioSourceNode = 0;
     mUse64BitFileOffset = false;
     mMovieTimeScale  = -1;
     mAudioTimeScale  = -1;
     mVideoTimeScale  = -1;
     mCameraId        = 0;
     mStartTimeOffsetMs = -1;
     mVideoEncoderProfile = -1;
@@ -1520,44 +1509,39 @@ status_t GonkRecorder::reset() {
 
     mOutputFd = -1;
     mCameraHw.clear();
     //TODO: May need to register a listener eventually
     //if someone is interested in recorder events for now
     //default to no listener registered
     mListener = NULL;
 
-    // Disable Audio Encoding
-    char value[PROPERTY_VALUE_MAX];
-    property_get("camcorder.debug.disableaudio", value, "0");
-    if(atoi(value)) mDisableAudio = true;
-
     return OK;
 }
 
 status_t GonkRecorder::getMaxAmplitude(int *max) {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("getMaxAmplitude");
 
     if (max == NULL) {
-        DOM_CAMERA_LOGE("Null pointer argument");
+        RE_LOGE("Null pointer argument");
         return BAD_VALUE;
     }
 
     if (mAudioSourceNode != 0) {
         *max = mAudioSourceNode->getMaxAmplitude();
     } else {
         *max = 0;
     }
 
     return OK;
 }
 
 status_t GonkRecorder::dump(
         int fd, const Vector<String16>& args) const {
-    DOM_CAMERA_LOGI("%s", __func__);
+    RE_LOGV("dump");
     const size_t SIZE = 256;
     char buffer[SIZE];
     String8 result;
     if (mWriter != 0) {
         mWriter->dump(fd, args);
     } else {
         snprintf(buffer, SIZE, "   No file writer\n");
         result.append(buffer);
--- a/dom/camera/GonkRecorder.h
+++ b/dom/camera/GonkRecorder.h
@@ -1,11 +1,10 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
- * Copyright (C) 2013 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
@@ -14,21 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef GONK_RECORDER_H_
 #define GONK_RECORDER_H_
 
 #include "nsISupportsImpl.h"
+#include "GonkCameraHwMgr.h"
 
-#include <media/mediarecorder.h>
+#include <media/MediaRecorderBase.h>
 #include <camera/CameraParameters.h>
 #include <utils/String8.h>
 #include <system/audio.h>
+
+#include "mozilla/RefPtr.h"
 #include "GonkCameraHwMgr.h"
 
 namespace android {
 
 class GonkCameraSource;
 struct MediaSource;
 struct MediaWriter;
 class MetaData;
@@ -49,31 +51,34 @@ struct GonkRecorder {
     virtual status_t setVideoEncoder(video_encoder ve);
     virtual status_t setVideoSize(int width, int height);
     virtual status_t setVideoFrameRate(int frames_per_second);
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
     virtual status_t setCamera(const sp<GonkCameraHardware>& aCameraHw);
     virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
+    virtual status_t setClientName(const String16& clientName);
     virtual status_t prepare();
     virtual status_t start();
     virtual status_t pause();
     virtual status_t stop();
     virtual status_t close();
     virtual status_t reset();
     virtual status_t getMaxAmplitude(int *max);
     virtual status_t dump(int fd, const Vector<String16>& args) const;
     // Querying a SurfaceMediaSourcer
 
 protected:
     virtual ~GonkRecorder();
 
 private:
     sp<IMediaRecorderClient> mListener;
+    String16 mClientName;
+    uid_t mClientUid;
     sp<MediaWriter> mWriter;
     int mOutputFd;
     sp<AudioSource> mAudioSourceNode;
 
     audio_source_t mAudioSource;
     video_source mVideoSource;
     output_format mOutputFormat;
     audio_encoder mAudioEncoder;
@@ -103,33 +108,37 @@ private:
 
     String8 mParams;
 
     bool mIsMetaDataStoredInVideoBuffers;
     MediaProfiles *mEncoderProfiles;
 
     bool mStarted;
     // Needed when GLFrames are encoded.
-    // An <ISurfaceTexture> pointer
+    // An <IGraphicBufferProducer> pointer
     // will be sent to the client side using which the
     // frame buffers will be queued and dequeued
-    bool mDisableAudio;
+
     sp<GonkCameraHardware> mCameraHw;
 
     status_t setupMPEG4Recording(
         int outputFd,
         int32_t videoWidth, int32_t videoHeight,
         int32_t videoBitRate,
         int32_t *totalBitRate,
         sp<MediaWriter> *mediaWriter);
     void setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
         sp<MetaData> *meta);
     status_t startMPEG4Recording();
     status_t startAMRRecording();
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+    status_t startAACRecording();
+#endif
     status_t startRawAudioRecording();
+    status_t startRTPRecording();
     status_t startMPEG2TSRecording();
     sp<MediaSource> createAudioSource();
     status_t checkVideoEncoderCapabilities();
     status_t checkAudioEncoderCapabilities();
     // Generic MediaSource set-up. Returns the appropriate
     // source (CameraSource or SurfaceMediaSource)
     // depending on the videosource type
     status_t setupMediaSource(sp<MediaSource> *mediaSource);
--- a/dom/camera/moz.build
+++ b/dom/camera/moz.build
@@ -34,17 +34,16 @@ CPP_SOURCES += [
 
 if CONFIG['MOZ_B2G_CAMERA']:
     CPP_SOURCES += [
         'GonkCameraManager.cpp',
         'GonkCameraControl.cpp',
         'GonkCameraHwMgr.cpp',
         'GonkRecorder.cpp',
         'GonkCameraSource.cpp',
-        'AudioParameter.cpp',
         'GonkRecorderProfiles.cpp',
     ]
 else:
     CPP_SOURCES += [
         'FallbackCameraManager.cpp',
         'FallbackCameraControl.cpp',
     ]
 
--- a/dom/fmradio/FMRadioService.cpp
+++ b/dom/fmradio/FMRadioService.cpp
@@ -223,20 +223,16 @@ class SetFrequencyRunnable MOZ_FINAL : p
 {
 public:
   SetFrequencyRunnable(int32_t aFrequency)
     : mFrequency(aFrequency) { }
 
   NS_IMETHOD Run()
   {
     SetFMRadioFrequency(mFrequency);
-
-    FMRadioService* fmRadioService = FMRadioService::Singleton();
-    fmRadioService->UpdateFrequency();
-
     return NS_OK;
   }
 
 private:
   int32_t mFrequency;
 };
 
 class SeekRunnable MOZ_FINAL : public nsRunnable
@@ -751,16 +747,19 @@ FMRadioService::Notify(const FMRadioOper
       // Seek action might be cancelled by SetFrequency(), we need to check if
       // the current state is Seeking.
       if (mState == Seeking) {
         TransitionState(SuccessResponse(), Enabled);
       }
 
       UpdateFrequency();
       break;
+    case FM_RADIO_OPERATION_TUNE:
+      UpdateFrequency();
+      break;
     default:
       MOZ_CRASH();
   }
 }
 
 void
 FMRadioService::UpdatePowerState()
 {
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1625,16 +1625,29 @@ ContentParent::RecvAudioChannelChangedNo
         AudioChannelService::GetAudioChannelService();
     if (service) {
        service->SendAudioChannelChangedNotification(ChildID());
     }
     return true;
 }
 
 bool
+ContentParent::RecvAudioChannelChangeDefVolChannel(
+  const AudioChannelType& aType, const bool& aHidden)
+{
+    nsRefPtr<AudioChannelService> service =
+        AudioChannelService::GetAudioChannelService();
+    if (service) {
+       service->SetDefaultVolumeControlChannelInternal(aType,
+                                                       aHidden, mChildID);
+    }
+    return true;
+}
+
+bool
 ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
 {
 #ifdef MOZ_WIDGET_GONK
     nsresult rv;
     nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
     if (vs) {
         vs->BroadcastVolume(aVolumeName);
     }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -420,16 +420,19 @@ private:
                                           bool* aValue);
 
     virtual bool RecvAudioChannelRegisterType(const AudioChannelType& aType);
     virtual bool RecvAudioChannelUnregisterType(const AudioChannelType& aType,
                                                 const bool& aElementHidden);
 
     virtual bool RecvAudioChannelChangedNotification();
 
+    virtual bool RecvAudioChannelChangeDefVolChannel(
+      const AudioChannelType& aType, const bool& aHidden);
+
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
 
     virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus);
 
     virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
 
     virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) MOZ_OVERRIDE;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -439,16 +439,18 @@ parent:
                               bool aElementWasHidden)
         returns (bool value);
 
     sync AudioChannelRegisterType(AudioChannelType aType);
     sync AudioChannelUnregisterType(AudioChannelType aType,
                                     bool aElementHidden);
 
     async AudioChannelChangedNotification();
+    async AudioChannelChangeDefVolChannel(AudioChannelType aType,
+                                          bool aHidden);
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
                                nsCString aReason);
     // get nsIVolumeService to broadcast volume information
     async BroadcastVolume(nsString volumeName);
 
--- a/dom/ipc/TabMessageUtils.h
+++ b/dom/ipc/TabMessageUtils.h
@@ -56,16 +56,16 @@ struct ParamTraits<mozilla::dom::RemoteD
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
   }
 };
 
 template <>
 struct ParamTraits<mozilla::dom::AudioChannelType>
   : public EnumSerializer<mozilla::dom::AudioChannelType,
-                          mozilla::dom::AUDIO_CHANNEL_NORMAL,
+                          mozilla::dom::AUDIO_CHANNEL_DEFAULT,
                           mozilla::dom::AUDIO_CHANNEL_LAST>
 { };
 
 }
 
 
 #endif
--- a/dom/src/notification/Notification.h
+++ b/dom/src/notification/Notification.h
@@ -29,16 +29,47 @@ public:
   Notification(const nsAString& aTitle, const nsAString& aBody,
                NotificationDirection aDir, const nsAString& aLang,
                const nsAString& aTag, const nsAString& aIconUrl);
 
   static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
                                                     const nsAString& aTitle,
                                                     const NotificationOptions& aOption,
                                                     ErrorResult& aRv);
+  void GetTitle(nsString& aRetval)
+  {
+    aRetval = mTitle;
+  }
+
+  NotificationDirection Dir()
+  {
+    return mDir;
+  }
+
+  void GetLang(nsString& aRetval)
+  {
+    aRetval = mLang;
+  }
+
+  void GetBody(nsString& aRetval)
+  {
+    aRetval = mBody;
+  }
+
+  void GetTag(nsString& aRetval)
+  {
+    if (StringBeginsWith(mTag, NS_LITERAL_STRING("tag:"))) {
+      aRetval = Substring(mTag, 4);
+    }
+  }
+
+  void GetIcon(nsString& aRetval)
+  {
+    aRetval = mIconUrl;
+  }
 
   static void RequestPermission(const GlobalObject& aGlobal,
                                 const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
                                 ErrorResult& aRv);
 
   static NotificationPermission GetPermission(const GlobalObject& aGlobal,
                                               ErrorResult& aRv);
 
--- a/dom/system/gonk/AudioChannelManager.cpp
+++ b/dom/system/gonk/AudioChannelManager.cpp
@@ -1,51 +1,173 @@
 /* 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 "nsIDOMClassInfo.h"
+#include "nsIDOMEventListener.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIPermissionManager.h"
+#include "nsIInterfaceRequestorUtils.h"
 #include "AudioChannelManager.h"
-#include "nsIDOMClassInfo.h"
 #include "mozilla/dom/AudioChannelManagerBinding.h"
 
 using namespace mozilla::hal;
 
 namespace mozilla {
 namespace dom {
 namespace system {
 
+NS_IMPL_QUERY_INTERFACE_INHERITED1(AudioChannelManager, nsDOMEventTargetHelper,
+                                   nsIDOMEventListener)
+NS_IMPL_ADDREF_INHERITED(AudioChannelManager, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(AudioChannelManager, nsDOMEventTargetHelper)
+
 AudioChannelManager::AudioChannelManager()
   : mState(SWITCH_STATE_UNKNOWN)
+  , mVolumeChannel(AUDIO_CHANNEL_DEFAULT)
 {
   RegisterSwitchObserver(SWITCH_HEADPHONES, this);
   mState = GetCurrentSwitchState(SWITCH_HEADPHONES);
   SetIsDOMBinding();
 }
 
 AudioChannelManager::~AudioChannelManager()
 {
   UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
+
+  nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
+  NS_ENSURE_TRUE_VOID(target);
+
+  target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                    this,
+                                    /* useCapture = */ true);
 }
 
 void
 AudioChannelManager::Init(nsPIDOMWindow* aWindow)
 {
   BindToOwner(aWindow->IsOuterWindow() ?
-    aWindow->GetCurrentInnerWindow() : aWindow);
+              aWindow->GetCurrentInnerWindow() : aWindow);
+
+  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+  NS_ENSURE_TRUE_VOID(target);
+
+  target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                 this,
+                                 /* useCapture = */ true,
+                                 /* wantsUntrusted = */ false);
 }
 
 JSObject*
 AudioChannelManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return AudioChannelManagerBinding::Wrap(aCx, aScope, this);
 }
 
 void
 AudioChannelManager::Notify(const SwitchEvent& aEvent)
 {
   mState = aEvent.status();
 
   DispatchTrustedEvent(NS_LITERAL_STRING("headphoneschange"));
 }
 
+bool
+AudioChannelManager::SetVolumeControlChannel(const nsAString& aChannel)
+{
+  if (aChannel.EqualsASCII("publicnotification")) {
+    return false;
+  }
+
+  AudioChannelType oldVolumeChannel = mVolumeChannel;
+  // Only normal channel doesn't need permission.
+  if (aChannel.EqualsASCII("normal")) {
+    mVolumeChannel = AUDIO_CHANNEL_NORMAL;
+  } else {
+    nsCOMPtr<nsIPermissionManager> permissionManager =
+      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+    if (!permissionManager) {
+      return false;
+    }
+    uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+    permissionManager->TestPermissionFromWindow(GetOwner(),
+      nsCString(NS_LITERAL_CSTRING("audio-channel-") +
+      NS_ConvertUTF16toUTF8(aChannel)).get(), &perm);
+    if (perm != nsIPermissionManager::ALLOW_ACTION) {
+      return false;
+    }
+
+    if (aChannel.EqualsASCII("content")) {
+      mVolumeChannel = AUDIO_CHANNEL_CONTENT;
+    } else if (aChannel.EqualsASCII("notification")) {
+      mVolumeChannel = AUDIO_CHANNEL_NOTIFICATION;
+    } else if (aChannel.EqualsASCII("alarm")) {
+      mVolumeChannel = AUDIO_CHANNEL_ALARM;
+    } else if (aChannel.EqualsASCII("telephony")) {
+      mVolumeChannel = AUDIO_CHANNEL_TELEPHONY;
+    } else if (aChannel.EqualsASCII("ringer")) {
+      mVolumeChannel = AUDIO_CHANNEL_RINGER;
+    }
+  }
+
+  if (oldVolumeChannel == mVolumeChannel) {
+    return true;
+  }
+  NotifyVolumeControlChannelChanged();
+  return true;
+}
+
+bool
+AudioChannelManager::GetVolumeControlChannel(nsAString & aChannel)
+{
+  if (mVolumeChannel == AUDIO_CHANNEL_NORMAL) {
+    aChannel.AssignASCII("normal");
+  } else if (mVolumeChannel == AUDIO_CHANNEL_CONTENT) {
+    aChannel.AssignASCII("content");
+  } else if (mVolumeChannel == AUDIO_CHANNEL_NOTIFICATION) {
+    aChannel.AssignASCII("notification");
+  } else if (mVolumeChannel == AUDIO_CHANNEL_ALARM) {
+    aChannel.AssignASCII("alarm");
+  } else if (mVolumeChannel == AUDIO_CHANNEL_TELEPHONY) {
+    aChannel.AssignASCII("telephony");
+  } else if (mVolumeChannel == AUDIO_CHANNEL_RINGER) {
+    aChannel.AssignASCII("ringer");
+  } else {
+    aChannel.AssignASCII("");
+  }
+
+  return true;
+}
+
+void
+AudioChannelManager::NotifyVolumeControlChannelChanged()
+{
+  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
+  NS_ENSURE_TRUE_VOID(docshell);
+
+  bool isActive = false;
+  docshell->GetIsActive(&isActive);
+
+  AudioChannelService* service = AudioChannelService::GetAudioChannelService();
+  if (isActive) {
+    service->SetDefaultVolumeControlChannel(mVolumeChannel, isActive);
+  } else {
+    service->SetDefaultVolumeControlChannel(AUDIO_CHANNEL_DEFAULT, isActive);
+  }
+}
+
+NS_IMETHODIMP
+AudioChannelManager::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsAutoString type;
+  aEvent->GetType(type);
+
+  if (type.EqualsLiteral("visibilitychange")) {
+    NotifyVolumeControlChannelChanged();
+  }
+  return NS_OK;
+}
+
 } // namespace system
 } // namespace dom
 } // namespace mozilla
--- a/dom/system/gonk/AudioChannelManager.h
+++ b/dom/system/gonk/AudioChannelManager.h
@@ -3,33 +3,39 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_system_AudioChannelManager_h
 #define mozilla_dom_system_AudioChannelManager_h
 
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
 #include "nsDOMEventTargetHelper.h"
+#include "AudioChannelService.h"
 
 namespace mozilla {
 namespace hal {
 class SwitchEvent;
 typedef Observer<SwitchEvent> SwitchObserver;
 } // namespace hal
 
 namespace dom {
 namespace system {
 
-class AudioChannelManager : public nsDOMEventTargetHelper
-                          , public hal::SwitchObserver
+class AudioChannelManager MOZ_FINAL
+  : public nsDOMEventTargetHelper
+  , public hal::SwitchObserver
+  , public nsIDOMEventListener
 {
 public:
   AudioChannelManager();
   virtual ~AudioChannelManager();
 
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMEVENTLISTENER
+
   void Notify(const hal::SwitchEvent& aEvent);
 
   void Init(nsPIDOMWindow* aWindow);
 
   /**
    * WebIDL Interface
    */
 
@@ -42,19 +48,26 @@ public:
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   bool Headphones() const
   {
     MOZ_ASSERT(mState != hal::SWITCH_STATE_UNKNOWN);
     return mState != hal::SWITCH_STATE_OFF;
   }
 
+  bool SetVolumeControlChannel(const nsAString& aChannel);
+
+  bool GetVolumeControlChannel(nsAString& aChannel);
+
   IMPL_EVENT_HANDLER(headphoneschange)
 
 private:
+  void NotifyVolumeControlChannelChanged();
+
   hal::SwitchState mState;
+  AudioChannelType mVolumeChannel;
 };
 
 } // namespace system
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_system_AudioChannelManager_h
--- a/dom/tests/mochitest/notification/test_web_notifications.html
+++ b/dom/tests/mochitest/notification/test_web_notifications.html
@@ -33,22 +33,40 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       try {
         Notification.requestPermission({});
         ok(false, "Non callable arugment to request permission should throw exception.");
       } catch (ex) {
         ok(true, "Non callable arugment to request permission should throw exception.");
       }
 
-      var notification = new Notification("This is a title", {
-          dir: "auto",
-          lang: "",
-          body: "This is a notification body",
-          tag: "sometag",
-        });
+      var title = "This is a title";
+
+      var notification = new Notification(title);
+
+      is(notification.title, title, "Title should be set");
+      is(notification.dir, "auto", "Dir should default to 'auto'");
+      is(notification.lang, "", "Lang should not be set");
+      is(notification.body, "", "Body should not be set");
+      is(notification.tag, "", "Tag should not be set");
+
+      var options = {
+        dir: "auto",
+        lang: "",
+        body: "This is a notification body",
+        tag: "sometag"
+      };
+
+      var notification = new Notification(title, options);
+
+      is(notification.title, title, "Title should be set");
+      is(notification.dir, options.dir, "Dir should be set");
+      is(notification.lang, options.lang, "Lang should be set");
+      is(notification.body, options.body, "Body should be set");
+      is(notification.tag, options.tag, "Tag should be set");
 
       notification.onclose = function() {
         ok(true, "Notification should be closed.");
         callbackCalled();
       };
 
       notification.onshow = function() {
         ok(true, "Notification should be shown.");
--- a/dom/webidl/AudioChannelManager.webidl
+++ b/dom/webidl/AudioChannelManager.webidl
@@ -19,9 +19,15 @@ interface AudioChannelManager : EventTar
    *
    * If audio is currently playing in this window or in one of its children, we
    * will fire this event before we switch the audio output from headphones to
    * speakers (or vice versa).  This allows you to, for example, pause your
    * window's audio when the headphones are unplugged.
    */
   [SetterThrows]
   attribute EventHandler onheadphoneschange;
+
+  /**
+   * Indicates which audio channel is used to adjust volume when pressing HW
+   * volume keys.
+   */
+  attribute DOMString volumeControlChannel;
 };
--- a/dom/webidl/Notification.webidl
+++ b/dom/webidl/Notification.webidl
@@ -26,16 +26,34 @@ interface Notification : EventTarget {
   attribute EventHandler onshow;
 
   [SetterThrows]
   attribute EventHandler onerror;
 
   [SetterThrows]
   attribute EventHandler onclose;
 
+  [Constant]
+  readonly attribute DOMString title;
+
+  [Constant]
+  readonly attribute NotificationDirection dir;
+
+  [Constant]
+  readonly attribute DOMString? lang;
+
+  [Constant]
+  readonly attribute DOMString? body;
+
+  [Constant]
+  readonly attribute DOMString? tag;
+
+  [Constant]
+  readonly attribute DOMString? icon;
+
   void close();
 };
 
 dictionary NotificationOptions {
   NotificationDirection dir = "auto";
   DOMString lang = "";
   DOMString body = "";
   DOMString tag;
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -168,22 +168,16 @@ var WifiManager = (function() {
   function voidControlMessage(cmd, callback) {
     controlMessage({ cmd: cmd }, function (data) {
       callback(data.status);
     });
   }
 
   var driverLoaded = false;
 
-  manager.checkDriverState = function(expectState) {
-    if (!unloadDriverEnabled)
-      return true;
-    return (expectState === driverLoaded);
-  }
-
   function loadDriver(callback) {
     if (driverLoaded) {
       callback(0);
       return;
     }
 
     voidControlMessage("load_driver", function(status) {
       driverLoaded = (status >= 0);
@@ -1310,17 +1304,16 @@ var WifiManager = (function() {
             }
 
             function doStartSupplicant() {
               cancelWaitForDriverReadyTimer();
               startSupplicant(function (status) {
                 if (status < 0) {
                   unloadDriver(function() {
                     callback(status);
-
                   });
                   manager.state = "UNINITIALIZED";
                   return;
                 }
 
                 manager.supplicantStarted = true;
                 enableInterface(manager.ifname, function (ok) {
                   callback(ok ? 0 : -1);
@@ -2085,38 +2078,32 @@ function WifiWorker() {
     });
 
     try {
       self._allowWpaEap = Services.prefs.getBoolPref("b2g.wifi.allow_unsafe_wpa_eap");
     } catch (e) {
       self._allowWpaEap = false;
     }
 
-    // Check if we need to dequeue requests first.
-    self._notifyAfterStateChange(true, true);
-
     // Notify everybody, even if they didn't ask us to come up.
     WifiManager.getMacAddress(function (mac) {
       self.macAddress = mac;
       debug("Got mac: " + mac);
       self._fireEvent("wifiUp", { macAddress: mac });
     });
 
     if (WifiManager.state === "SCANNING")
       startScanStuckTimer();
   };
 
   WifiManager.onsupplicantlost = function() {
     WifiManager.enabled = WifiManager.supplicantStarted = false;
     WifiManager.state = "UNINITIALIZED";
     debug("Supplicant died!");
 
-    // Check if we need to dequeue requests first.
-    self._notifyAfterStateChange(this.success, false);
-
     // Notify everybody, even if they didn't ask us to come up.
     self._fireEvent("wifiDown", {});
   };
 
   WifiManager.onpasswordmaybeincorrect = function() {
     WifiManager.authenticationFailuresCount++;
   };
 
@@ -2928,113 +2915,43 @@ WifiWorker.prototype = {
       for (let networkKey in this.configuredNetworks) {
         networks.push(netToDOM(this.configuredNetworks[networkKey]));
       }
 
       this._sendMessage(message, true, networks, msg);
     }).bind(this));
   },
 
-  _notifyAfterStateChange: function(success, newState) {
-    if (!this._stateRequests.length)
-      return;
-
-    // First, notify all of the requests that were trying to make this change.
-    let state = this._stateRequests[0].enabled;
-    let driverReady = WifiManager.checkDriverState(newState);
-
-    // It is callback function's responsibility to handle the pending request.
-    // So we just return here.
-    if (this._stateRequests.length > 0
-        && ("callback" in this._stateRequests[0])) {
-      return;
-    }
-
-    // If the new state is not the same as state or new state is not the same as
-    // driver loaded state, then we weren't processing the first request (we
-    // were racing somehow) so don't notify.
-    // For newState is false(disable), we expect driverLoaded is false(driver unloaded)
-    // to proceed, and vice versa.
-    if (!success || (driverReady && state === newState)) {
-      do {
-        if (!("callback" in this._stateRequests[0])) {
-          this._stateRequests.shift();
-        }
-        // Don't remove more than one request if the previous one failed.
-      } while (success &&
-               this._stateRequests.length &&
-               !("callback" in this._stateRequests[0]) &&
-               this._stateRequests[0].enabled === state);
-    }
-    // If there were requests queued after this one, run them.
-    if (this._stateRequests.length > 0) {
-      let self = this;
-      let callback = null;
-      this._callbackTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      if (driverReady) {
-        // Driver is ready for next request.
-        callback = function(timer) {
-          if ("callback" in self._stateRequests[0]) {
-            self._stateRequests[0].callback.call(self, self._stateRequests[0].enabled);
-          } else {
-            WifiManager.setWifiEnabled(self._stateRequests[0].enabled,
-                                       self._setWifiEnabledCallback.bind(self));
-          }
-          timer = null;
-        };
-      } else {
-        // Wait driver until it's ready.
-        callback = function(timer) {
-          self._notifyAfterStateChange(success, newState);
-          timer = null;
-        };
-      }
-      this._callbackTimer.initWithCallback(callback, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-  },
-
   _setWifiEnabledCallback: function(status) {
-    if (status === "no change") {
-      this._notifyAfterStateChange(true, this._stateRequests[0].enabled);
-      return;
-    }
-
-    if (status) {
-      // Don't call notifyAndContinue because we don't want to skip another
-      // attempt to turn wifi on or off if this one failed.
-      this._notifyAfterStateChange(false, this._stateRequests[0].enabled);
-      return;
-    }
-
     // If we're enabling ourselves, then wait until we've connected to the
     // supplicant to notify. If we're disabling, we take care of this in
     // supplicantlost.
     if (WifiManager.supplicantStarted)
       WifiManager.start();
+
+    this.requestDone();
   },
 
-  setWifiEnabled: function(msg) {
-    // There are two problems that we're trying to solve here:
-    //   - If we get multiple requests to turn on and off wifi before the
-    //     current request has finished, then we need to queue up the requests
-    //     and handle each on/off request in turn.
-    //   - Because we can't pass a callback to WifiManager.start, we need to
-    //     have a way to communicate with our onsupplicantconnection callback.
-    this._stateRequests.push(msg);
-    if (this._stateRequests.length === 1) {
-      if ("callback" in this._stateRequests[0]) {
-        this._stateRequests[0].callback.call(this, msg.enabled);
-      } else {
-        WifiManager.setWifiEnabled(msg.enabled, this._setWifiEnabledCallback.bind(this));
-      }
+  setWifiEnabled: function(enabled, callback) {
+    WifiManager.setWifiEnabled(enabled, callback);
+  },
+
+  // requestDone() must be called to before callback complete(or error)
+  // so next queue in the request quene can be executed.
+  queueRequest: function(enabled, callback) {
+    if (!callback) {
+        throw "Try to enqueue a request without callback";
     }
-  },
-
-  queueRequest: function(enabled, callback) {
-    this.setWifiEnabled({enabled: enabled, callback: callback});
+
+    this._stateRequests.push({
+      enabled: enabled,
+      callback: callback
+    });
+
+    this.nextRequest();
   },
 
   getWifiTetheringParameters: function getWifiTetheringParameters(enable) {
     let ssid;
     let securityType;
     let securityId;
     let interfaceIp;
     let prefix;
@@ -3098,16 +3015,17 @@ WifiWorker.prototype = {
       link: enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
     };
   },
 
   setWifiApEnabled: function(enabled, callback) {
     let configuration = this.getWifiTetheringParameters(enabled);
 
     if (!configuration) {
+      this.requestDone();
       debug("Invalid Wifi Tethering configuration.");
       return;
     }
 
     WifiManager.setWifiApEnabled(enabled, configuration, callback);
   },
 
   associate: function(msg) {
@@ -3330,98 +3248,150 @@ WifiWorker.prototype = {
   },
 
   // This is a bit ugly, but works. In particular, this depends on the fact
   // that RadioManager never actually tries to get the worker from us.
   get worker() { throw "Not implemented"; },
 
   shutdown: function() {
     debug("shutting down ...");
-    this.setWifiEnabled({enabled: false});
+    this.queueRequest(false, function(data) {
+      this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
+    }.bind(this));
+  },
+
+  requestProcessing: false,   // Hold while dequeue and execution a request.
+                              // Released upon the request is fully executed,
+                              // i.e, mostly after callback is done.
+  requestDone: function requestDone() {
+    this.requestProcessing = false;
+    this.nextRequest();
   },
 
   nextRequest: function nextRequest() {
-    if (this._stateRequests.length <= 0 ||
-        !("callback" in this._stateRequests[0])) {
+    // No request to process
+    if (this._stateRequests.length === 0) {
+      return;
+    }
+
+    // Handling request, wait for it.
+    if (this.requestProcessing) {
       return;
     }
-    this._stateRequests.shift();
-    // Serve the pending requests.
-    if (this._stateRequests.length > 0) {
-      if ("callback" in this._stateRequests[0]) {
-        this._stateRequests[0].callback.call(this,
-                                             this._stateRequests[0].enabled);
-      } else {
-        WifiManager.setWifiEnabled(this._stateRequests[0].enabled,
-                                   this._setWifiEnabledCallback.bind(this));
-      }
-    }
+
+    // Hold processing lock
+    this.requestProcessing = true;
+
+    // Find next valid request
+    let request = this._stateRequests.shift();
+
+    request.callback(request.enabled);
   },
 
   notifyTetheringOn: function notifyTetheringOn() {
     // It's really sad that we don't have an API to notify the wifi
     // hotspot status. Toggle settings to let gaia know that wifi hotspot
     // is enabled.
+    let self = this;
     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = true;
     this._oldWifiTetheringEnabledState = true;
     gSettingsService.createLock().set(
-      SETTINGS_WIFI_TETHERING_ENABLED, true, null, "fromInternalSetting");
-    // Check for the next request.
-    this.nextRequest();
+      SETTINGS_WIFI_TETHERING_ENABLED,
+      true,
+      {
+        handle: function(aName, aResult) {
+          self.requestDone();
+        },
+        handleError: function(aErrorMessage) {
+          self.requestDone();
+        }
+      },
+      "fromInternalSetting");
   },
 
   notifyTetheringOff: function notifyTetheringOff() {
     // It's really sad that we don't have an API to notify the wifi
     // hotspot status. Toggle settings to let gaia know that wifi hotspot
     // is disabled.
+    let self = this;
     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
     this._oldWifiTetheringEnabledState = false;
     gSettingsService.createLock().set(
-      SETTINGS_WIFI_TETHERING_ENABLED, false, null, "fromInternalSetting");
-    // Check for the next request.
-    this.nextRequest();
+      SETTINGS_WIFI_TETHERING_ENABLED,
+      false,
+      {
+        handle: function(aName, aResult) {
+          self.requestDone();
+        },
+        handleError: function(aErrorMessage) {
+          self.requestDone();
+        }
+      },
+      "fromInternalSetting");
   },
 
   handleWifiEnabled: function(enabled) {
     if (WifiManager.enabled === enabled) {
       return;
     }
     // Make sure Wifi hotspot is idle before switching to Wifi mode.
-    if (enabled && (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
-         WifiManager.tetheringState != "UNINITIALIZED")) {
+    if (enabled) {
       this.queueRequest(false, function(data) {
+        if (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
+            WifiManager.tetheringState != "UNINITIALIZED") {
+          this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
+        } else {
+          this.requestDone();
+        }
         this.disconnectedByWifi = true;
-        this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
       }.bind(this));
     }
-    this.setWifiEnabled({enabled: enabled});
-
-    if (!enabled && this.disconnectedByWifi) {
+
+    this.queueRequest(enabled, function(data) {
+      this.setWifiEnabled(enabled, this._setWifiEnabledCallback.bind(this));
+    }.bind(this));
+
+    if (!enabled) {
       this.queueRequest(true, function(data) {
+        if (this.disconnectedByWifi) {
+          this.setWifiApEnabled(true, this.notifyTetheringOn.bind(this));
+        } else {
+          this.requestDone();
+        }
         this.disconnectedByWifi = false;
-        this.setWifiApEnabled(true, this.notifyTetheringOn.bind(this));
       }.bind(this));
     }
   },
 
   handleWifiTetheringEnabled: function(enabled) {
     // Make sure Wifi is idle before switching to Wifi hotspot mode.
-    if (enabled && (WifiManager.enabled ||
-         WifiManager.state != "UNINITIALIZED")) {
-      this.disconnectedByWifiTethering = true;
-      this.setWifiEnabled({enabled: false});
+    if (enabled) {
+      this.queueRequest(false, function(data) {
+        if (WifiManager.enabled || WifiManager.state != "UNINITIALIZED") {
+          this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
+        } else {
+          this.requestDone();
+        }
+        this.disconnectedByWifiTethering = true;
+      }.bind(this));
     }
 
     this.queueRequest(enabled, function(data) {
-      this.setWifiApEnabled(data, this.nextRequest.bind(this));
+      this.setWifiApEnabled(data, this.requestDone.bind(this));
     }.bind(this));
 
-    if (!enabled && this.disconnectedByWifiTethering) {
-      this.disconnectedByWifiTethering = false;
-      this.setWifiEnabled({enabled: true});
+    if (!enabled) {
+      this.queueRequest(true, function(data) {
+        if (this.disconnectedByWifiTethering) {
+          this.setWifiEnabled(true, this._setWifiEnabledCallback.bind(this));
+        } else {
+          this.requestDone();
+        }
+        this.disconnectedByWifiTethering = false;
+      }.bind(this));
     }
   },
 
   // nsIObserver implementation
   observe: function observe(subject, topic, data) {
     // Note that this function gets called for any and all settings changes,
     // so we need to carefully check if we have the one we're interested in.
     // The string we're interested in will be a JSON string that looks like:
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -24,16 +24,18 @@ namespace mozilla {
 namespace layers {
 
 uint32_t GrallocImage::sColorIdMap[] = {
     HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar,
     HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar,
     HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar,
     HAL_PIXEL_FORMAT_YCrCb_420_SP, -1,
     HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1,
+    HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED,
+    HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
     HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar,
     0, 0
 };
 
 struct GraphicBufferAutoUnlock {
   android::sp<GraphicBuffer> mGraphicBuffer;
 
   GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer)
--- a/gfx/layers/GrallocImages.h
+++ b/gfx/layers/GrallocImages.h
@@ -99,16 +99,18 @@ public:
 
   // From [android 4.0.4]/hardware/msm7k/libgralloc-qsd8k/gralloc_priv.h
   enum {
     /* OEM specific HAL formats */
     HAL_PIXEL_FORMAT_YCbCr_422_P            = 0x102,
     HAL_PIXEL_FORMAT_YCbCr_420_P            = 0x103,
     HAL_PIXEL_FORMAT_YCbCr_420_SP           = 0x109,
     HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO    = 0x10A,
+    HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED     = 0x7FA30C03,
+    HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS     = 0x7FA30C04,
   };
 
   virtual already_AddRefed<gfxASurface> GetAsSurface();
 
   void* GetNativeBuffer()
   {
     if (IsValid()) {
       return GrallocBufferActor::GetFrom(GetSurfaceDescriptor())->getNativeBuffer();
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -208,16 +208,17 @@ ApplyTransform(nsIntPoint* aPoint, const
   gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   aPoint->x = NS_lround(result.x);
   aPoint->y = NS_lround(result.y);
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
 {
+  nsEventStatus result = nsEventStatus_eIgnore;
   gfx3DMatrix transformToApzc;
   gfx3DMatrix transformToScreen;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
       const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
       if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
         mApzcForInputBlock = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
         for (size_t i = 1; i < multiTouchInput.mTouches.Length(); i++) {
@@ -233,48 +234,48 @@ APZCTreeManager::ReceiveInputEvent(const
         APZC_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
       }
       if (mApzcForInputBlock) {
         GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToScreen);
         MultiTouchInput inputForApzc(multiTouchInput);
         for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
           ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
         }
-        mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
+        result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
         // If we have an mApzcForInputBlock and it's the end of the touch sequence
         // then null it out so we don't keep a dangling reference and leak things.
         if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
             (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_END && multiTouchInput.mTouches.Length() == 1)) {
           mApzcForInputBlock = nullptr;
         }
       }
       break;
     } case PINCHGESTURE_INPUT: {
       const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint);
       if (apzc) {
         GetInputTransforms(apzc, transformToApzc, transformToScreen);
         PinchGestureInput inputForApzc(pinchInput);
         ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
-        apzc->ReceiveInputEvent(inputForApzc);
+        result = apzc->ReceiveInputEvent(inputForApzc);
       }
       break;
     } case TAPGESTURE_INPUT: {
       const TapGestureInput& tapInput = aEvent.AsTapGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint));
       if (apzc) {
         GetInputTransforms(apzc, transformToApzc, transformToScreen);
         TapGestureInput inputForApzc(tapInput);
         ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
-        apzc->ReceiveInputEvent(inputForApzc);
+        result = apzc->ReceiveInputEvent(inputForApzc);
       }
       break;
     }
   }
-  return nsEventStatus_eIgnore;
+  return result;
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(const nsInputEvent& aEvent,
                                    nsInputEvent* aOutEvent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -137,22 +137,28 @@ static float gXStationarySizeMultiplier 
 static float gYStationarySizeMultiplier = 2.5f;
 
 /**
  * The time period in ms that throttles mozbrowserasyncscroll event.
  * Default is 100ms if there is no "apzc.asyncscroll.throttle" in preference.
  */
 
 static int gAsyncScrollThrottleTime = 100;
+
 /**
  * The timeout in ms for mAsyncScrollTimeoutTask delay task.
  * Default is 300ms if there is no "apzc.asyncscroll.timeout" in preference.
  */
 static int gAsyncScrollTimeout = 300;
 
+/**
+ * Temporary pref for disabling zoom in metrofx on aurora.
+ */
+static bool gAsyncZoomDisabled = false;
+
 static TimeStamp sFrameTime;
 
 static TimeStamp
 GetFrameTime() {
   if (sFrameTime.IsNull()) {
     return TimeStamp::Now();
   }
   return sFrameTime;
@@ -180,16 +186,17 @@ AsyncPanZoomController::InitializeGlobal
   Preferences::AddIntVarCache(&gNumPaintDurationSamples, "gfx.azpc.num_paint_duration_samples", gNumPaintDurationSamples);
   Preferences::AddFloatVarCache(&gTouchStartTolerance, "gfx.azpc.touch_start_tolerance", gTouchStartTolerance);
   Preferences::AddFloatVarCache(&gXSkateSizeMultiplier, "gfx.azpc.x_skate_size_multiplier", gXSkateSizeMultiplier);
   Preferences::AddFloatVarCache(&gYSkateSizeMultiplier, "gfx.azpc.y_skate_size_multiplier", gYSkateSizeMultiplier);
   Preferences::AddFloatVarCache(&gXStationarySizeMultiplier, "gfx.azpc.x_stationary_size_multiplier", gXStationarySizeMultiplier);
   Preferences::AddFloatVarCache(&gYStationarySizeMultiplier, "gfx.azpc.y_stationary_size_multiplier", gYStationarySizeMultiplier);
   Preferences::AddIntVarCache(&gAsyncScrollThrottleTime, "apzc.asyncscroll.throttle", gAsyncScrollThrottleTime);
   Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apzc.asyncscroll.timeout", gAsyncScrollTimeout);
+  Preferences::AddBoolVarCache(&gAsyncZoomDisabled, "apzc.asynczoom.disabled", gAsyncZoomDisabled);
 
   gComputedTimingFunction = new ComputedTimingFunction();
   gComputedTimingFunction->Init(
     nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
   ClearOnShutdown(&gComputedTimingFunction);
 }
 
 AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
@@ -216,16 +223,19 @@ AsyncPanZoomController::AsyncPanZoomCont
      mHandlingTouchQueue(false),
      mDelayPanning(false)
 {
   MOZ_COUNT_CTOR(AsyncPanZoomController);
 
   if (aGestures == USE_GESTURE_DETECTOR) {
     mGestureEventListener = new GestureEventListener(this);
   }
+  if (gAsyncZoomDisabled) {
+    mAllowZoom = false;
+  }
 }
 
 AsyncPanZoomController::~AsyncPanZoomController() {
   MOZ_COUNT_DTOR(AsyncPanZoomController);
 }
 
 already_AddRefed<GeckoContentController>
 AsyncPanZoomController::GetGeckoContentController() {
@@ -1358,16 +1368,19 @@ void AsyncPanZoomController::SetZoomAndR
   mFrameMetrics.mCumulativeResolution = aZoom / mFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
   // The parent resolution will not have changed.
   mFrameMetrics.mResolution = mFrameMetrics.mCumulativeResolution / parentResolution;
 }
 
 void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,
                                                    const CSSToScreenScale& aMinZoom,
                                                    const CSSToScreenScale& aMaxZoom) {
+  if (gAsyncZoomDisabled) {
+    return;
+  }
   mAllowZoom = aAllowZoom;
   mMinZoom = (MIN_ZOOM > aMinZoom ? MIN_ZOOM : aMinZoom);
   mMaxZoom = (MAX_ZOOM > aMaxZoom ? aMaxZoom : MAX_ZOOM);
 }
 
 void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -2,16 +2,17 @@
 //  * 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 "GLContext.h"
 #include "gfxImageSurface.h"
 #include "gfx2DGlue.h"
 #include <ui/GraphicBuffer.h>
+#include "GrallocImages.h"  // for GrallocImage
 #include "mozilla/layers/GrallocTextureHost.h"
 #include "mozilla/layers/CompositorOGL.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace android;
 
@@ -28,16 +29,18 @@ SurfaceFormatForAndroidPixelFormat(andro
     return swapRB ? gfx::FORMAT_B8G8R8X8 : gfx::FORMAT_R8G8B8X8;
   case android::PIXEL_FORMAT_RGB_565:
     return gfx::FORMAT_R5G6B5;
   case android::PIXEL_FORMAT_A_8:
     return gfx::FORMAT_A8;
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
     return gfx::FORMAT_B8G8R8A8; // yup, use FORMAT_B8G8R8A8 even though it's a YUV texture. This is an external texture.
   default:
     if (aFormat >= 0x100 && aFormat <= 0x1FF) {
       // Reserved range for HAL specific formats.
       return gfx::FORMAT_B8G8R8A8;
     } else {
       // This is not super-unreachable, there's a bunch of hypothetical pixel
@@ -54,16 +57,18 @@ SurfaceFormatForAndroidPixelFormat(andro
 
 static GLenum
 TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
 {
   switch (aFormat) {
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
     return LOCAL_GL_TEXTURE_EXTERNAL;
   case android::PIXEL_FORMAT_RGBA_8888:
   case android::PIXEL_FORMAT_RGBX_8888:
   case android::PIXEL_FORMAT_RGB_565:
   case android::PIXEL_FORMAT_A_8:
     return LOCAL_GL_TEXTURE_2D;
   default:
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -14,16 +14,19 @@
 #include "gfx2DGlue.h"                  // for ContentForFormat, etc
 #include "gfxImageSurface.h"            // for gfxImageSurface
 #include "gfxPoint.h"                   // for gfxIntSize
 #include "gfxReusableSurfaceWrapper.h"  // for gfxReusableSurfaceWrapper
 #include "ipc/AutoOpenSurface.h"        // for AutoOpenSurface
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
+#ifdef MOZ_WIDGET_GONK
+# include "GrallocImages.h"  // for GrallocImage
+#endif
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include "mozilla/layers/GrallocTextureHost.h"
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRegion.h"                   // for nsIntRegion
 #include "GfxTexturesReporter.h"        // for GfxTexturesReporter
 #ifdef XP_MACOSX
 #include "SharedSurfaceIO.h"
@@ -947,16 +950,18 @@ SurfaceFormatForAndroidPixelFormat(andro
     return swapRB ? FORMAT_B8G8R8X8 : FORMAT_R8G8B8X8;
   case android::PIXEL_FORMAT_RGB_565:
     return FORMAT_R5G6B5;
   case android::PIXEL_FORMAT_A_8:
     return FORMAT_A8;
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
     return FORMAT_B8G8R8A8; // yup, use FORMAT_B8G8R8A8 even though it's a YUV texture. This is an external texture.
   default:
     if (aFormat >= 0x100 && aFormat <= 0x1FF) {
       // Reserved range for HAL specific formats.
       return FORMAT_B8G8R8A8;
     } else {
       // This is not super-unreachable, there's a bunch of hypothetical pixel
@@ -972,16 +977,18 @@ SurfaceFormatForAndroidPixelFormat(andro
 
 static GLenum
 TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
 {
   switch (aFormat) {
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+  case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
     return LOCAL_GL_TEXTURE_EXTERNAL;
   case android::PIXEL_FORMAT_RGBA_8888:
   case android::PIXEL_FORMAT_RGBX_8888:
   case android::PIXEL_FORMAT_RGB_565:
   case android::PIXEL_FORMAT_A_8:
     return LOCAL_GL_TEXTURE_2D;
   default:
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -123,16 +123,17 @@ enum WakeLockControl {
 
 class FMRadioOperationInformation;
 
 enum FMRadioOperation {
   FM_RADIO_OPERATION_UNKNOWN = -1,
   FM_RADIO_OPERATION_ENABLE,
   FM_RADIO_OPERATION_DISABLE,
   FM_RADIO_OPERATION_SEEK,
+  FM_RADIO_OPERATION_TUNE,
   NUM_FM_RADIO_OPERATION
 };
 
 enum FMRadioOperationStatus {
   FM_RADIO_OPERATION_STATUS_UNKNOWN = -1,
   FM_RADIO_OPERATION_STATUS_SUCCESS,
   FM_RADIO_OPERATION_STATUS_FAIL,
   NUM_FM_RADIO_OPERATION_STATUS
--- a/hal/gonk/GonkFMRadio.cpp
+++ b/hal/gonk/GonkFMRadio.cpp
@@ -229,16 +229,20 @@ runTavaruaRadio(void *)
           NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE,
                                                   hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
         }
         break;
       case TAVARUA_EVT_SEEK_COMPLETE:
         NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK,
                                                 hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
         break;
+      case TAVARUA_EVT_TUNE_SUCC:
+        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_TUNE,
+                                                hal::FM_RADIO_OPERATION_STATUS_SUCCESS));
+        break;
       default:
         break;
       }
     }
   }
 
   return nullptr;
 }
--- a/ipc/glue/AsyncChannel.cpp
+++ b/ipc/glue/AsyncChannel.cpp
@@ -153,18 +153,19 @@ AsyncChannel::ProcessLink::Open(mozilla:
             // Transport::Connect() has already been called.  Take
             // over the channel from the previous listener and process
             // any queued messages.
             mIOLoop->PostTask(
                 FROM_HERE,
                 NewRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel));
         }
 
-        // FIXME/cjones: handle errors
-        while (!mChan->Connected()) {
+        // Should not wait here if something goes wrong with the channel.
+        while (!mChan->Connected() &&
+               mChan->mChannelState != AsyncChannel::ChannelError) {
             mChan->mMonitor->Wait();
         }
     }
 }
 
 void
 AsyncChannel::ProcessLink::EchoMessage(Message *msg)
 {
@@ -851,18 +852,20 @@ AsyncChannel::OnMessageReceivedFromLink(
 }
 
 void
 AsyncChannel::OnChannelErrorFromLink()
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
-    if (ChannelClosing != mChannelState)
+    if (ChannelClosing != mChannelState) {
         mChannelState = ChannelError;
+        mMonitor->Notify();
+    }
 
     PostErrorNotifyTask();
 }
 
 void
 AsyncChannel::CloseWithError()
 {
     AssertWorkerThread();
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -1482,17 +1482,17 @@ endif
 
 ifneq ($(DIST_CHROME_FILES),)
 DIST_CHROME_FILES_PATH := $(FINAL_TARGET)/chrome
 DIST_CHROME_FILES_FLAGS := $(XULAPP_DEFINES)
 PP_TARGETS += DIST_CHROME_FILES
 endif
 
 ifneq ($(XPI_PKGNAME),)
-libs realchrome::
+tools realchrome::
 ifdef STRIP_XPI
 ifndef MOZ_DEBUG
 	@echo "Stripping $(XPI_PKGNAME) package directory..."
 	@echo $(FINAL_TARGET)
 	@cd $(FINAL_TARGET) && find . ! -type d \
 			! -name "*.js" \
 			! -name "*.xpt" \
 			! -name "*.gif" \
@@ -1521,17 +1521,17 @@ endif
 	cd $(FINAL_TARGET) && $(ZIP) -qr ../$(XPI_PKGNAME).xpi *
 endif
 
 ifdef INSTALL_EXTENSION_ID
 ifndef XPI_NAME
 $(error XPI_NAME must be set for INSTALL_EXTENSION_ID)
 endif
 
-libs::
+tools::
 	$(RM) -r "$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID)"
 	$(NSINSTALL) -D "$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID)"
 	$(call copy_dir,$(FINAL_TARGET),$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID))
 
 endif
 
 #############################################################################
 # MDDEPDIR is the subdirectory where all the dependency files are placed.
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -74,17 +74,17 @@ class RefTest(object):
       prefs['reftest.filter'] = options.filter
     prefs['reftest.focusFilterMode'] = options.focusFilterMode
 
     for v in options.extraPrefs:
       thispref = v.split('=')
       if len(thispref) < 2:
         print "Error: syntax error in --setpref=" + v
         sys.exit(1)
-        prefs[thispref[0]] = thispref[1]
+      prefs[thispref[0]] = thispref[1]
 
     # install the reftest extension bits into the profile
     addons = []
     addons.append(os.path.join(SCRIPT_DIRECTORY, "reftest"))
 
     # I would prefer to use "--install-extension reftest/specialpowers", but that requires tight coordination with
     # release engineering and landing on multiple branches at once.
     if special_powers and (manifest.endswith('crashtests.list') or manifest.endswith('jstests.list')):
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -3,29 +3,29 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.favicons.Favicons;
+import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
+import org.mozilla.gecko.favicons.LoadFaviconTask;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
-import org.mozilla.gecko.gfx.LayerView;
-import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.health.BrowserHealthRecorder;
 import org.mozilla.gecko.health.BrowserHealthReporter;
 import org.mozilla.gecko.home.BrowserSearch;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.util.Clipboard;
-import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UiAsyncTask;
 import org.mozilla.gecko.widget.GeckoActionProvider;
 import org.mozilla.gecko.widget.ButtonToast;
 
@@ -37,53 +37,44 @@ import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.nfc.NdefMessage;
 import android.nfc.NdefRecord;
 import android.nfc.NfcAdapter;
 import android.nfc.NfcEvent;
 import android.os.Build;
 import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 import android.widget.Toast;
 
-import java.io.File;
-import java.io.InputStream;
-import java.net.URL;
 import java.net.URLEncoder;
 import java.util.EnumSet;
-import java.util.List;
 import java.util.Vector;
 
 abstract public class BrowserApp extends GeckoApp
                                  implements TabsPanel.TabsLayoutChangeListener,
                                             PropertyAnimator.PropertyAnimationListener,
                                             View.OnKeyListener,
                                             GeckoLayerClient.OnMetricsChangedListener,
                                             BrowserSearch.OnSearchListener,
@@ -711,19 +702,18 @@ abstract public class BrowserApp extends
             Tab tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
                 final String url = tab.getURL();
                 final String title = tab.getDisplayTitle();
                 if (url == null || title == null) {
                     return true;
                 }
 
-                Favicons favicons = Favicons.getInstance();
-                favicons.loadFavicon(url, tab.getFaviconURL(), 0,
-                new Favicons.OnFaviconLoadedListener() {
+                Favicons.loadFavicon(url, tab.getFaviconURL(), 0,
+                new OnFaviconLoadedListener() {
                     @Override
                     public void onFaviconLoaded(String url, Bitmap favicon) {
                         GeckoAppShell.createShortcut(title, url, url, favicon == null ? null : favicon, "");
                     }
                 });
             }
             return true;
         }
@@ -1339,19 +1329,19 @@ abstract public class BrowserApp extends
     private void openReadingList() {
         Tabs.getInstance().loadUrl(ABOUT_HOME, Tabs.LOADURL_READING_LIST);
     }
 
     /* Favicon methods */
     private void loadFavicon(final Tab tab) {
         maybeCancelFaviconLoad(tab);
 
-        int flags = Favicons.FLAG_SCALE | ( (tab.isPrivate() || tab.getErrorType() != Tab.ErrorType.NONE) ? 0 : Favicons.FLAG_PERSIST);
-        long id = Favicons.getInstance().loadFavicon(tab.getURL(), tab.getFaviconURL(), flags,
-                        new Favicons.OnFaviconLoadedListener() {
+        int flags = LoadFaviconTask.FLAG_SCALE | ( (tab.isPrivate() || tab.getErrorType() != Tab.ErrorType.NONE) ? 0 : LoadFaviconTask.FLAG_PERSIST);
+        int id = Favicons.loadFavicon(tab.getURL(), tab.getFaviconURL(), flags,
+                        new OnFaviconLoadedListener() {
 
             @Override
             public void onFaviconLoaded(String pageUrl, Bitmap favicon) {
                 // Leave favicon UI untouched if we failed to load the image
                 // for some reason.
                 if (favicon == null)
                     return;
 
@@ -1366,23 +1356,23 @@ abstract public class BrowserApp extends
                 Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.FAVICON);
             }
         });
 
         tab.setFaviconLoadId(id);
     }
 
     private void maybeCancelFaviconLoad(Tab tab) {
-        long faviconLoadId = tab.getFaviconLoadId();
+        int faviconLoadId = tab.getFaviconLoadId();
 
         if (faviconLoadId == Favicons.NOT_LOADING)
             return;
 
         // Cancel pending favicon load task
-        Favicons.getInstance().cancelFaviconLoad(faviconLoadId);
+        Favicons.cancelFaviconLoad(faviconLoadId);
 
         // Reset favicon load state
         tab.setFaviconLoadId(Favicons.NOT_LOADING);
     }
 
     /**
      * Enters editing mode with the current tab's URL. There might be no
      * tabs loaded by the time the user enters editing mode e.g. just after
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -241,20 +241,20 @@ public class BrowserToolbar extends Geck
         mUrlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
         mDefaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset);
         mUrlDisplayContainer = findViewById(R.id.url_display_container);
         mUrlBarEntry = findViewById(R.id.url_bar_entry);
 
         mUrlEditContainer = findViewById(R.id.url_edit_container);
         mUrlEditText = (CustomEditText) findViewById(R.id.url_edit_text);
 
-        // This will clip the right edge's image at half of its width
+        // This will clip the right edge's image at 60% of its width
         mUrlBarRightEdge = (ImageView) findViewById(R.id.url_bar_right_edge);
         if (mUrlBarRightEdge != null) {
-            mUrlBarRightEdge.getDrawable().setLevel(5000);
+            mUrlBarRightEdge.getDrawable().setLevel(6000);
         }
 
         mTitle = (GeckoTextView) findViewById(R.id.url_bar_title);
         mTitlePadding = mTitle.getPaddingRight();
 
         mTabs = (ShapedButton) findViewById(R.id.tabs);
         mTabsCounter = (TabCounter) findViewById(R.id.tabs_counter);
         mBack = (ImageButton) findViewById(R.id.back);
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1,18 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
-import org.mozilla.gecko.DataReportingNotification;
 import org.mozilla.gecko.background.announcements.AnnouncementsBroadcastService;
 import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PluginLayer;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.menu.MenuPanel;
 import org.mozilla.gecko.health.BrowserHealthRecorder;
@@ -30,29 +30,25 @@ import org.mozilla.gecko.widget.ButtonTo
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
-import android.graphics.Point;
 import android.graphics.RectF;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.location.Location;
 import android.location.LocationListener;
 import android.net.wifi.ScanResult;
 import android.net.Uri;
@@ -66,54 +62,47 @@ import android.preference.PreferenceMana
 import android.provider.ContactsContract;
 import android.provider.MediaStore.Images.Media;
 
 import android.telephony.CellLocation;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.telephony.PhoneStateListener;
-import android.telephony.SignalStrength;
 import android.telephony.gsm.GsmCellLocation;
 
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.Log;
 import android.util.SparseBooleanArray;
-import android.view.Display;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
-import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.AbsoluteLayout;
 import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.SimpleAdapter;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import java.io.BufferedReader;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileReader;
-import java.io.InputStreamReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -490,17 +479,17 @@ abstract public class GeckoApp
         outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground());
         outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
     }
 
     void handleFaviconRequest(final String url) {
         (new UiAsyncTask<Void, Void, String>(ThreadUtils.getBackgroundHandler()) {
             @Override
             public String doInBackground(Void... params) {
-                return Favicons.getInstance().getFaviconUrlForPageUrl(url);
+                return Favicons.getFaviconUrlForPageUrl(url);
             }
 
             @Override
             public void onPostExecute(String faviconUrl) {
                 JSONObject args = new JSONObject();
 
                 if (faviconUrl != null) {
                     try {
@@ -1199,17 +1188,17 @@ abstract public class GeckoApp
         ((GeckoApplication)getApplication()).initialize();
 
         sAppContext = this;
         GeckoAppShell.setContextGetter(this);
         GeckoAppShell.setGeckoInterface(this);
         ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
 
         Tabs.getInstance().attachToContext(this);
-        Favicons.getInstance().attachToContext(this);
+        Favicons.attachToContext(this);
 
         // When we detect a locale change, we need to restart Gecko, which
         // actually means restarting the entire application. This logic should
         // actually be handled elsewhere since GeckoApp may not be alive to
         // handle this event if "Don't keep activities" is enabled (filed as
         // bug 889082).
         if (((GeckoApplication)getApplication()).needsRestart()) {
             doRestart();
@@ -2080,16 +2069,18 @@ abstract public class GeckoApp
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     rec.close();
                 }
             });
         }
 
+        Favicons.close();
+
         super.onDestroy();
 
         Tabs.unregisterOnTabsChangedListener(this);
     }
 
     protected void registerEventListener(String event) {
         GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
     }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -71,17 +71,19 @@ FENNEC_JAVA_FILES = \
   db/BrowserDB.java \
   db/LocalBrowserDB.java \
   db/DBUtils.java \
   DataReportingNotification.java \
   Distribution.java \
   DoorHanger.java \
   DoorHangerPopup.java \
   EditBookmarkDialog.java \
-  Favicons.java \
+  favicons/Favicons.java \
+  favicons/LoadFaviconTask.java \
+  favicons/OnFaviconLoadedListener.java \
   FilePickerResultHandler.java \
   FilePickerResultHandlerSync.java \
   FindInPageBar.java \
   FlowLayout.java \
   FontSizePreference.java \
   FormAssistPopup.java \
   ForwardButton.java \
   GeckoAccessibility.java \
--- a/mobile/android/base/MemoryMonitor.java
+++ b/mobile/android/base/MemoryMonitor.java
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Build;
@@ -149,17 +150,17 @@ class MemoryMonitor extends BroadcastRec
 
         // TODO hook in memory-reduction stuff for different levels here
         if (level >= MEMORY_PRESSURE_MEDIUM) {
             //Only send medium or higher events because that's all that is used right now
             if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createLowMemoryEvent(level));
             }
 
-            Favicons.getInstance().clearMemCache();
+            Favicons.clearMemCache();
         }
         return true;
     }
 
     private boolean decreaseMemoryPressure() {
         int newLevel;
         synchronized (this) {
             if (mMemoryPressure <= 0) {
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -49,17 +49,17 @@ public class Tab {
     private BitmapDrawable mThumbnail;
     private int mHistoryIndex;
     private int mHistorySize;
     private int mParentId;
     private HomePager.Page mAboutHomePage;
     private boolean mExternal;
     private boolean mBookmark;
     private boolean mReadingListItem;
-    private long mFaviconLoadId;
+    private int mFaviconLoadId;
     private String mContentType;
     private boolean mHasTouchListeners;
     private ZoomConstraints mZoomConstraints;
     private boolean mIsRTL;
     private ArrayList<View> mPluginViews;
     private HashMap<Object, Layer> mPluginLayers;
     private int mBackgroundColor;
     private int mState;
@@ -346,21 +346,21 @@ public class Tab {
     public void setHasTouchListeners(boolean aValue) {
         mHasTouchListeners = aValue;
     }
 
     public boolean getHasTouchListeners() {
         return mHasTouchListeners;
     }
 
-    public void setFaviconLoadId(long faviconLoadId) {
+    public void setFaviconLoadId(int faviconLoadId) {
         mFaviconLoadId = faviconLoadId;
     }
 
-    public long getFaviconLoadId() {
+    public int getFaviconLoadId() {
         return mFaviconLoadId;
     }
 
     public void updateFavicon(Bitmap favicon) {
         mFavicon = favicon;
     }
 
     public synchronized void updateFaviconURL(String faviconUrl, int size) {
rename from mobile/android/base/Favicons.java
rename to mobile/android/base/favicons/Favicons.java
--- a/mobile/android/base/Favicons.java
+++ b/mobile/android/base/favicons/Favicons.java
@@ -1,119 +1,79 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-package org.mozilla.gecko;
+package org.mozilla.gecko.favicons;
 
+import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.gfx.BitmapUtils;
-import org.mozilla.gecko.util.GeckoJarReader;
 import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.UiAsyncTask;
 
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.entity.BufferedHttpEntity;
-
-import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.net.http.AndroidHttpClient;
-import android.os.Handler;
 import android.support.v4.util.LruCache;
 import android.util.Log;
 
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
 public class Favicons {
     private static final String LOGTAG = "GeckoFavicons";
 
-    public static final long NOT_LOADING = 0;
-    public static final long FAILED_EXPIRY_NEVER = -1;
+    public static final int NOT_LOADING = 0;
+    public static final int FAILED_EXPIRY_NEVER = -1;
     public static final int FLAG_PERSIST = 1;
     public static final int FLAG_SCALE = 2;
 
     private static int sFaviconSmallSize = -1;
     private static int sFaviconLargeSize = -1;
 
-    private Context mContext;
-
-    private Map<Long,LoadFaviconTask> mLoadTasks;
-    private long mNextFaviconLoadId;
-    private LruCache<String, Bitmap> mFaviconsCache;
-    private LruCache<String, Long> mFailedCache;
-    private LruCache<String, Integer> mColorCache;
-    private static final String USER_AGENT = GeckoAppShell.getGeckoInterface().getDefaultUAString();
-    private AndroidHttpClient mHttpClient;
-
-    public interface OnFaviconLoadedListener {
-        public void onFaviconLoaded(String url, Bitmap favicon);
-    }
-
-    public Favicons() {
-        Log.d(LOGTAG, "Creating Favicons instance");
-
-        mLoadTasks = Collections.synchronizedMap(new HashMap<Long,LoadFaviconTask>());
-        mNextFaviconLoadId = 0;
+    protected static Context sContext;
 
-        // Create a favicon memory cache that have up to 1mb of size
-        mFaviconsCache = new LruCache<String, Bitmap>(1024 * 1024) {
-            @Override
-            protected int sizeOf(String url, Bitmap image) {
-                return image.getRowBytes() * image.getHeight();
-            }
-        };
-
-        // Create a failed favicon memory cache that has up to 64 entries
-        mFailedCache = new LruCache<String, Long>(64);
+    private static final Map<Integer, LoadFaviconTask> sLoadTasks = Collections.synchronizedMap(new HashMap<Integer, LoadFaviconTask>());
+    private static final LruCache<String, Bitmap> sFaviconCache = new LruCache<String, Bitmap>(1024 * 1024) {
+        @Override
+        protected int sizeOf(String url, Bitmap image) {
+            return image.getRowBytes() * image.getHeight();
+        }
+    };
 
-        // Create a cache to store favicon dominant colors
-        mColorCache = new LruCache<String, Integer>(256);
-    }
+    // A cache of the Favicon which have recently failed to download - prevents us from repeatedly
+    // trying to download a Favicon when doing so is currently impossible.
+    private static final LruCache<String, Long> sFailedCache = new LruCache<String, Long>(64);
 
-    private synchronized AndroidHttpClient getHttpClient() {
-        if (mHttpClient != null)
-            return mHttpClient;
-
-        mHttpClient = AndroidHttpClient.newInstance(USER_AGENT);
-        return mHttpClient;
-    }
-
-    private void dispatchResult(final String pageUrl, final Bitmap image,
+    // A cache holding the dominant colours of favicons - used by FaviconView to fill the extra space
+    // around a Favicon when it is asked to render a Favicon small than the view.
+    private static final LruCache<String, Integer> sColorCache = new LruCache<String, Integer>(256);
+    static void dispatchResult(final String pageUrl, final Bitmap image,
             final OnFaviconLoadedListener listener) {
         if (pageUrl != null && image != null)
             putFaviconInMemCache(pageUrl, image);
 
         // We want to always run the listener on UI thread
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 if (listener != null)
                     listener.onFaviconLoaded(pageUrl, image);
             }
         });
     }
 
-    public String getFaviconUrlForPageUrl(String pageUrl) {
-        return BrowserDB.getFaviconUrlForHistoryUrl(mContext.getContentResolver(), pageUrl);
+    public static String getFaviconUrlForPageUrl(String pageUrl) {
+        return BrowserDB.getFaviconUrlForHistoryUrl(sContext.getContentResolver(), pageUrl);
     }
 
-    public long loadFavicon(String pageUrl, String faviconUrl, int flags,
+    public static int loadFavicon(String pageUrl, String faviconUrl, int flags,
             OnFaviconLoadedListener listener) {
 
         // Handle the case where page url is empty
         if (pageUrl == null || pageUrl.length() == 0) {
             dispatchResult(null, null, listener);
             return -1;
         }
 
@@ -127,292 +87,121 @@ public class Favicons {
         Bitmap image = getFaviconFromMemCache(pageUrl);
         if (image != null) {
             dispatchResult(pageUrl, image, listener);
             return -1;
         }
 
         LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageUrl, faviconUrl, flags, listener);
 
-        long taskId = task.getId();
-        mLoadTasks.put(taskId, task);
+        int taskId = task.getId();