Merge last PGO-green changeset of mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Thu, 12 Jul 2012 17:28:05 +0100
changeset 99068 f9499238bd4b82cb34b2ab2fd1aafde29ea3007e
parent 98958 69f5207c57c8f3b6e13fbaae102f6747472492fd (current diff)
parent 99067 f0be4b70b814d6cd310667b309d2d80859aad71e (diff)
child 99069 87ff6a0cc45e174de9299911f899006078a3cc78
child 99295 4c552a902d9cf7e1882dda536fe31d4726189fb7
child 106542 35ef899801bc41b0af7b694f3858ba3c225dbd8e
push id23099
push useremorley@mozilla.com
push dateThu, 12 Jul 2012 16:29:09 +0000
treeherdermozilla-central@f9499238bd4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone16.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 last PGO-green changeset of mozilla-inbound to mozilla-central
content/media/MediaEngine.h
content/media/MediaEngineDefault.cpp
content/media/MediaEngineDefault.h
editor/libeditor/base/nsIEditorSupport.h
js/src/tests/js1_8_1/regress/regress-452498-108.js
mobile/android/app/macbuild/Contents/Info.plist.in
mobile/android/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
mobile/android/app/maemo/toolbar_splash.png
mobile/android/base/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -48,16 +48,19 @@ endif
 
 ifdef MOZ_MEMORY
 tier_base_dirs += memory/mozjemalloc
 ifdef MOZ_JEMALLOC
 tier_base_dirs += memory/jemalloc
 endif
 tier_base_dirs += memory/build
 endif
+ifndef MOZ_NATIVE_ZLIB
+tier_base_dirs += modules/zlib
+endif
 tier_base_dirs += \
   mozglue \
   memory/mozalloc \
   $(NULL)
 endif
 
 ifdef COMPILE_ENVIRONMENT
 include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -14,16 +14,17 @@ builtin(include, build/autoconf/mozcommo
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
 builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
 builtin(include, build/autoconf/expandlibs.m4)dnl
 builtin(include, build/autoconf/arch.m4)dnl
 builtin(include, build/autoconf/android.m4)dnl
+builtin(include, build/autoconf/zlib.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
 
 # Read the user's .mozconfig script.  We can't do this in
 # configure.in: autoconf puts the argument parsing code above anything
 # expanded from configure.in, and we need to get the configure options
 # from .mozconfig in place before that argument parsing code.
 MOZ_READ_MOZCONFIG(.)
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -38,16 +38,19 @@
 @BINPATH@/dictionaries/*
 @BINPATH@/hyphenation/*
 #ifdef XP_WIN32
 @BINPATH@/uninstall/helper.exe
 #endif
 
 [xpcom]
 @BINPATH@/dependentlibs.list
+#ifdef XP_WIN32
+@BINPATH@/@DLL_PREFIX@gkmedias@DLL_SUFFIX@
+#endif
 #ifndef MOZ_STATIC_JS
 @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
 #endif
 @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@xpcom@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
@@ -468,16 +471,19 @@
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
 
 @BINPATH@/components/SystemMessageInternal.js
 @BINPATH@/components/SystemMessageManager.js
 @BINPATH@/components/SystemMessageManager.manifest
 
+@BINPATH@/components/AppProtocolHandler.js
+@BINPATH@/components/AppProtocolHandler.manifest
+
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
 @BINPATH@/components/nsUrlClassifierListManager.js
 @BINPATH@/components/nsUrlClassifierLib.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6053,27 +6053,56 @@ function WindowIsClosing()
 
 /**
  * Checks if this is the last full *browser* window around. If it is, this will
  * be communicated like quitting. Otherwise, we warn about closing multiple tabs.
  * @returns true if closing can proceed, false if it got cancelled.
  */
 function warnAboutClosingWindow() {
   // Popups aren't considered full browser windows.
-  if (!toolbar.visible)
+  let isPBWindow = gPrivateBrowsingUI.privateWindow;
+  if (!isPBWindow && !toolbar.visible)
     return gBrowser.warnAboutClosingTabs(true);
 
   // Figure out if there's at least one other browser window around.
   let e = Services.wm.getEnumerator("navigator:browser");
+  let otherPBWindowExists = false;
+  let warnAboutClosingTabs = false;
   while (e.hasMoreElements()) {
     let win = e.getNext();
-    if (win != window && win.toolbar.visible)
-      return gBrowser.warnAboutClosingTabs(true);
+    if (win != window) {
+      if (isPBWindow &&
+          ("gPrivateBrowsingUI" in win) &&
+          win.gPrivateBrowsingUI.privateWindow)
+        otherPBWindowExists = true;
+      if (win.toolbar.visible)
+        warnAboutClosingTabs = true;
+      // If the current window is not in private browsing mode we don't need to 
+      // look for other pb windows, we can leave the loop when finding the 
+      // first non-popup window. If however the current window is in private 
+      // browsing mode then we need at least one other pb and one non-popup 
+      // window to break out early.
+      if ((!isPBWindow || otherPBWindowExists) && warnAboutClosingTabs)
+        break;
+    }
   }
 
+  if (isPBWindow && !otherPBWindowExists) {
+    let exitingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
+                          createInstance(Ci.nsISupportsPRBool);
+    exitingCanceled.data = false;
+    Services.obs.notifyObservers(exitingCanceled,
+                                 "last-pb-context-exiting",
+                                 null);
+    if (exitingCanceled.data)
+      return false;
+  }
+  if (warnAboutClosingTabs)
+    return gBrowser.warnAboutClosingTabs(true);
+
   let os = Services.obs;
 
   let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
                         createInstance(Ci.nsISupportsPRBool);
   os.notifyObservers(closingCanceled,
                      "browser-lastwindow-close-requested", null);
   if (closingCanceled.data)
     return false;
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -223,17 +223,21 @@ GetHelperPath(nsAutoString& aPath)
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return appHelper->GetPath(aPath);
+  rv = appHelper->GetPath(aPath);
+
+  aPath.Insert(L'"', 0);
+  aPath.Append(L'"');
+  return rv;
 }
 
 nsresult
 LaunchHelper(nsAutoString& aPath)
 {
   STARTUPINFOW si = {sizeof(si), 0};
   PROCESS_INFORMATION pi = {0};
 
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -38,8 +38,9 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3
 # of values.
 ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
 # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
 MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
 MOZ_WEBAPP_RUNTIME=1
+MOZ_MEDIA_NAVIGATOR=1
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -695,16 +695,18 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 #ifdef XP_WIN
 @BINPATH@/webapp-uninstaller@BIN_SUFFIX@
 #endif
 @BINPATH@/webapprt-stub@BIN_SUFFIX@
 @BINPATH@/webapprt/webapprt.ini
 @BINPATH@/webapprt/chrome.manifest
 @BINPATH@/webapprt/chrome/webapprt@JAREXT@
 @BINPATH@/webapprt/chrome/webapprt.manifest
+@BINPATH@/webapprt/chrome/@AB_CD@@JAREXT@
+@BINPATH@/webapprt/chrome/@AB_CD@.manifest
 @BINPATH@/webapprt/components/CommandLineHandler.js
 @BINPATH@/webapprt/components/ContentPermission.js
 @BINPATH@/webapprt/components/ContentPolicy.js
 @BINPATH@/webapprt/components/DirectoryProvider.js
 @BINPATH@/webapprt/components/components.manifest
 @BINPATH@/webapprt/defaults/preferences/prefs.js
 @BINPATH@/webapprt/modules/WebappRT.jsm
 @BINPATH@/webapprt/modules/WebappsHandler.jsm
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -174,22 +174,25 @@ repackage-win32-installer-%:
 else
 repackage-win32-installer-%: ;
 endif
 
 
 clobber-zip:
 	$(RM) $(STAGEDIST)/chrome/$(AB_CD).jar \
 	  $(STAGEDIST)/chrome/$(AB_CD).manifest \
+	  $(STAGEDIST)/webapprt/chrome/$(AB_CD).jar \
+	  $(STAGEDIST)/webapprt/chrome/$(AB_CD).manifest \
 	  $(STAGEDIST)/$(PREF_DIR)/firefox-l10n.js
 	$(RM) -rf  $(STAGEDIST)/searchplugins \
 	  $(STAGEDIST)/dictionaries \
 	  $(STAGEDIST)/hyphenation \
 	  $(STAGEDIST)/defaults/profile \
-	  $(STAGEDIST)/chrome/$(AB_CD)
+	  $(STAGEDIST)/chrome/$(AB_CD) \
+	  $(STAGEDIST)/webapprt/chrome/$(AB_CD)
 
 
 langpack: langpack-$(AB_CD)
 
 # This is a generic target that will make a langpack, repack ZIP (+tarball)
 # builds, and repack an installer if applicable. It is called from the
 # tinderbox scripts. Alter it with caution.
 
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -115,12 +115,13 @@
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
 % locale testpilot @AB_CD@ %locale/feedback/
     locale/feedback/main.dtd                       (%feedback/main.dtd)
     locale/feedback/main.properties                (%feedback/main.properties)
 % locale pdf.js @AB_CD@ %locale/pdfviewer/
     locale/pdfviewer/viewer.properties             (%pdfviewer/viewer.properties)
     locale/pdfviewer/chrome.properties             (%pdfviewer/chrome.properties)
 #ifdef MOZ_WEBAPP_RUNTIME
+../webapprt/chrome/@AB_CD@.jar:
 % locale webapprt @AB_CD@ %locale/webapprt/
     locale/webapprt/webapp.dtd                     (%webapprt/webapp.dtd)
     locale/webapprt/webapp.properties              (%webapprt/webapp.properties)
 #endif
new file mode 100644
--- /dev/null
+++ b/build/autoconf/zlib.m4
@@ -0,0 +1,54 @@
+dnl This Source Code Form is subject to the terms of the Mozilla Public
+dnl License, v. 2.0. If a copy of the MPL was not distributed with this
+dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+dnl Usage: MOZ_ZLIB_CHECK([version])
+
+AC_DEFUN([MOZ_ZLIB_CHECK],
+[
+
+MOZZLIB=$1
+
+MOZ_ARG_WITH_STRING(system-zlib,
+[  --with-system-zlib[=PFX]
+                          Use system libz [installed at prefix PFX]],
+    ZLIB_DIR=$withval)
+
+if test -z "$MOZ_ZLIB_LIBS$MOZ_ZLIB_CFLAGS$SKIP_LIBRARY_CHECKS"; then
+    _SAVE_CFLAGS=$CFLAGS
+    _SAVE_LDFLAGS=$LDFLAGS
+    _SAVE_LIBS=$LIBS
+
+    if test -n "${ZLIB_DIR}" -a "${ZLIB_DIR}" != "yes"; then
+        MOZ_ZLIB_CFLAGS="-I${ZLIB_DIR}/include"
+        MOZ_ZLIB_LIBS="-L${ZLIB_DIR}/lib"
+        CFLAGS="$MOZ_ZLIB_CFLAGS $CFLAGS"
+        LDFLAGS="$MOZ_ZLIB_LIBS $LDFLAGS"
+    fi
+    if test -z "$ZLIB_DIR" -o "$ZLIB_DIR" = no; then
+        MOZ_NATIVE_ZLIB=
+    else
+        AC_CHECK_LIB(z, gzread, [MOZ_NATIVE_ZLIB=1 MOZ_ZLIB_LIBS="$MOZ_ZLIB_LIBS -lz"],
+            [MOZ_NATIVE_ZLIB=])
+        if test "$MOZ_NATIVE_ZLIB" = 1; then
+            MOZZLIBNUM=`echo $MOZZLIB | awk -F. changequote(<<, >>)'{printf "0x%x\n", (((<<$>>1 * 16 + <<$>>2) * 16) + <<$>>3) * 16 + <<$>>4}'changequote([, ])`
+            AC_TRY_COMPILE([ #include <stdio.h>
+                             #include <string.h>
+                             #include <zlib.h> ],
+                           [ #if ZLIB_VERNUM < $MOZZLIBNUM
+                             #error "Insufficient zlib version ($MOZZLIBNUM required)."
+                             #endif ],
+                           MOZ_NATIVE_ZLIB=1,
+                           AC_MSG_ERROR([Insufficient zlib version for --with-system-zlib ($MOZZLIB required)]))
+        fi
+    fi
+    CFLAGS=$_SAVE_CFLAGS
+    LDFLAGS=$_SAVE_LDFLAGS
+    LIBS=$_SAVE_LIBS
+fi
+
+AC_SUBST(MOZ_ZLIB_CFLAGS)
+AC_SUBST(MOZ_ZLIB_LIBS)
+AC_SUBST(MOZ_NATIVE_ZLIB)
+
+])
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -453,27 +453,25 @@ class ShutdownLeakLogger(object):
       self.seenShutdown = True
 
   def parse(self):
     leakingTests = self._parseLeakingTests()
 
     if leakingTests:
       totalWindows = sum(len(test["leakedWindows"]) for test in leakingTests)
       totalDocShells = sum(len(test["leakedDocShells"]) for test in leakingTests)
-      msgType = "INFO" if totalWindows + totalDocShells <= self.MAX_LEAK_COUNT else "UNEXPECTED-FAIL"
-      self.logger.info("TEST-%s | ShutdownLeaks | leaked %d DOMWindow(s) and %d DocShell(s) until shutdown", msgType, totalWindows, totalDocShells)
+      msgType = "TEST-INFO" if totalWindows + totalDocShells <= self.MAX_LEAK_COUNT else "TEST-UNEXPECTED-FAIL"
+      self.logger.info("%s | ShutdownLeaks | leaked %d DOMWindow(s) and %d DocShell(s) until shutdown", msgType, totalWindows, totalDocShells)
 
     for test in leakingTests:
-      self.logger.info("\n[%s]", test["fileName"])
-
       for url, count in self._zipLeakedWindows(test["leakedWindows"]):
-        self.logger.info("  %d window(s) [url = %s]", count, url)
+        self.logger.info("%s | %s | leaked %d window(s) until shutdown [url = %s]", msgType, test["fileName"], count, url)
 
       if test["leakedDocShells"]:
-        self.logger.info("  %d docShell(s)", len(test["leakedDocShells"]))
+        self.logger.info("%s | %s | leaked %d docShell(s) until shutdown", msgType, test["fileName"], len(test["leakedDocShells"]))
 
   def _logWindow(self, line):
     created = line[:2] == "++"
     id = self._parseValue(line, "serial")
 
     # log line has invalid format
     if not id:
       return
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -7,29 +7,29 @@ from devicemanager import DeviceManager,
 import re
 import os
 import sys
 import tempfile
 
 class DeviceManagerADB(DeviceManager):
 
   def __init__(self, host=None, port=20701, retrylimit=5, packageName='fennec',
-               adbPath='adb', deviceSerial=None):
+               adbPath='adb', deviceSerial=None, deviceRoot=None):
     self.host = host
     self.port = port
     self.retrylimit = retrylimit
     self.retries = 0
     self._sock = None
     self.useRunAs = False
     self.haveRoot = False
     self.useDDCopy = False
     self.useZip = False
     self.packageName = None
     self.tempDir = None
-    self.deviceRoot = None
+    self.deviceRoot = deviceRoot
 
     # the path to adb, or 'adb' to assume that it's on the PATH
     self.adbPath = adbPath
 
     # The serial number of the device to use with adb, used in cases
     # where multiple devices are being managed by the same adb instance.
     self.deviceSerial = deviceSerial
 
@@ -532,16 +532,23 @@ class DeviceManagerADB(DeviceManager):
     return data.split()[3]
 
   def getLocalHash(self, filename):
     data = p = subprocess.Popen(["ls", "-l", filename], stdout=subprocess.PIPE).stdout.read()
     return data.split()[4]
 
   # Internal method to setup the device root and cache its value
   def setupDeviceRoot(self):
+    # if self.deviceRoot is already set, create it if necessary, and use it
+    if self.deviceRoot:
+      if not self.dirExists(self.deviceRoot):
+        if not self.mkDir(self.deviceRoot):
+          raise DMError("Unable to create device root %s" % self.deviceRoot)
+      return
+
     # /mnt/sdcard/tests is preferred to /data/local/tests, but this can be
     # over-ridden by creating /data/local/tests
     testRoot = "/data/local/tests"
     if (self.dirExists(testRoot)):
       self.deviceRoot = testRoot
       return
 
     for (basePath, subPath) in [('/mnt/sdcard', 'tests'),
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -30,45 +30,42 @@ class DeviceManagerSUT(DeviceManager):
   port = 0
   debug = 2 
   retries = 0
   tempRoot = os.getcwd()
   base_prompt = '$>'
   base_prompt_re = '\$\>'
   prompt_sep = '\x00'
   prompt_regex = '.*(' + base_prompt_re + prompt_sep + ')'
-  agentErrorRE = re.compile('^##AGENT-WARNING##.*')
+  agentErrorRE = re.compile('^##AGENT-WARNING##\ ?(.*)')
 
   # TODO: member variable to indicate error conditions.
   # This should be set to a standard error from the errno module.
   # So, for example, when an error occurs because of a missing file/directory,
   # before returning, the function would do something like 'self.error = errno.ENOENT'.
   # The error would be set where appropriate--so sendCMD() could set socket errors,
   # pushFile() and other file-related commands could set filesystem errors, etc.
 
-  def __init__(self, host, port = 20701, retrylimit = 5):
+  def __init__(self, host, port = 20701, retrylimit = 5, deviceRoot = None):
     self.host = host
     self.port = port
     self.retrylimit = retrylimit
     self.retries = 0
     self._sock = None
+    self.deviceRoot = deviceRoot
     if self.getDeviceRoot() == None:
         raise BaseException("Failed to connect to SUT Agent and retrieve the device root.")
 
   def _cmdNeedsResponse(self, cmd):
     """ Not all commands need a response from the agent:
-        * if the cmd matches the pushRE then it is the first half of push
-          and therefore we want to wait until the second half before looking
-          for a response
         * rebt obviously doesn't get a response
         * uninstall performs a reboot to ensure starting in a clean state and
           so also doesn't look for a response
     """
-    noResponseCmds = [re.compile('^push .*$'),
-                      re.compile('^rebt'),
+    noResponseCmds = [re.compile('^rebt'),
                       re.compile('^uninst .*$'),
                       re.compile('^pull .*$')]
 
     for c in noResponseCmds:
       if (c.match(cmd)):
         return False
 
     # If the command is not in our list, then it gets a response
@@ -114,53 +111,53 @@ class DeviceManagerSUT(DeviceManager):
                          re.compile('^uninst .*$')]
 
     for c in socketClosingCmds:
       if (c.match(cmd)):
         return True
 
     return False
 
-  def sendCmds(self, cmdlist, outputfile, timeout = None, newline = True):
+  def sendCmds(self, cmdlist, outputfile, timeout = None):
     '''
     a wrapper for _doCmds that loops up to self.retrylimit iterations.
     this allows us to move the retry logic outside of the _doCmds() to make it
     easier for debugging in the future.
     note that since cmdlist is a list of commands, they will all be retried if
     one fails.  this is necessary in particular for pushFile(), where we don't want
     to accidentally send extra data if a failure occurs during data transmission.
     '''
     done = False
     while self.retries < self.retrylimit:
       try:
-        self._doCmds(cmdlist, outputfile, timeout, newline)
+        self._doCmds(cmdlist, outputfile, timeout)
         return
       except AgentError, err:
         # re-raise error if it's fatal (i.e. the device got the command but
         # couldn't execute it). retry otherwise
         if err.fatal:
           raise err
         if self.debug >= 2:
           print err
         self.retries += 1
 
     raise AgentError("unable to connect to %s after %s attempts" % (self.host, self.retrylimit))
 
-  def runCmds(self, cmdlist, timeout = None, newline = True):
+  def runCmds(self, cmdlist, timeout = None):
     '''
     similar to sendCmds, but just returns any output as a string instead of
     writing to a file. this is normally what you want to call to send a set
     of commands to the agent
     '''
     outputfile = StringIO.StringIO()
-    self.sendCmds(cmdlist, outputfile, timeout, newline)
+    self.sendCmds(cmdlist, outputfile, timeout)
     outputfile.seek(0)
     return outputfile.read()
 
-  def _doCmds(self, cmdlist, outputfile, timeout, newline):
+  def _doCmds(self, cmdlist, outputfile, timeout):
     promptre = re.compile(self.prompt_regex + '$')
     shouldCloseSocket = False
     recvGuard = 1000
 
     if not self._sock:
       try:
         if self.debug >= 1:
           print "reconnecting socket"
@@ -173,59 +170,67 @@ class DeviceManagerSUT(DeviceManager):
         self._sock.connect((self.host, int(self.port)))
         self._sock.recv(1024)
       except socket.error, msg:
         self._sock.close()
         self._sock = None
         raise AgentError("unable to connect socket: "+str(msg))
 
     for cmd in cmdlist:
-      if newline: cmd += '\r\n'
+      cmdline = '%s\r\n' % cmd['cmd']
 
       try:
-        numbytes = self._sock.send(cmd)
-        if (numbytes != len(cmd)):
-          raise AgentError("ERROR: our cmd was %s bytes and we only sent %s" % (len(cmd),
-                                                                                numbytes))
-        if (self.debug >= 4): print "send cmd: " + str(cmd)
+        sent = self._sock.send(cmdline)
+        if sent != len(cmdline):
+          raise AgentError("ERROR: our cmd was %s bytes and we "
+                           "only sent %s" % (len(cmdline), sent))
+        if cmd.get('data'):
+          sent = self._sock.send(cmd['data'])
+          if sent != len(cmd['data']):
+              raise AgentError("ERROR: we had %s bytes of data to send, but "
+                               "only sent %s" % (len(cmd['data'], sent)))
+
+        if (self.debug >= 4): print "sent cmd: " + str(cmd['cmd'])
       except socket.error, msg:
         self._sock.close()
         self._sock = None
         if self.debug >= 1:
-          print "Error sending data to socket. cmd="+str(cmd)+"; err="+str(msg)
+          print "Error sending data to socket. cmd="+str(cmd['cmd'])+"; err="+str(msg)
         return False
 
       # Check if the command should close the socket
-      shouldCloseSocket = self._shouldCmdCloseSocket(cmd)
+      shouldCloseSocket = self._shouldCmdCloseSocket(cmd['cmd'])
 
       # Handle responses from commands
-      if (self._cmdNeedsResponse(cmd)):
+      if (self._cmdNeedsResponse(cmd['cmd'])):
         found = False
         loopguard = 0
         data = ""
 
         while (found == False and (loopguard < recvGuard)):
           temp = ''
           if (self.debug >= 4): print "recv'ing..."
 
           # Get our response
           try:
             temp = self._sock.recv(1024)
             if (self.debug >= 4): print "response: " + str(temp)
           except socket.error, msg:
             self._sock.close()
             self._sock = None
-            raise AgentError("Error receiving data from socket. cmd="+str(cmd)+"; err="+str(msg))
+            raise AgentError("Error receiving data from socket. cmd="+str(cmd['cmd'])+"; err="+str(msg))
 
           data += temp
 
           # If something goes wrong in the agent it will send back a string that
-          # starts with '##AGENT-ERROR##'
-          if self.agentErrorRE.match(data):
-            raise AgentError("Agent Error processing command: %s" % cmd, fatal=True)
+          # starts with '##AGENT-WARNING##'
+          errorMatch = self.agentErrorRE.match(data)
+          if errorMatch:
+            raise AgentError("Agent Error processing command '%s'; err='%s'" %
+                             (cmd['cmd'], errorMatch.group(1)), fatal=True)
 
           for line in data.splitlines():
             if promptre.match(line):
               found = True
               data = self._stripPrompt(data)
               break
 
           # periodically flush data to output file to make sure it doesn't get
@@ -256,19 +261,19 @@ class DeviceManagerSUT(DeviceManager):
   # failure: None
   def shell(self, cmd, outputfile, env=None, cwd=None):
     cmdline = subprocess.list2cmdline(cmd)
     if env:
       cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
 
     try:
       if cwd:
-        self.sendCmds(['execcwd %s %s' % (cwd, cmdline)], outputfile)
+        self.sendCmds([{ 'cmd': 'execcwd %s %s' % (cwd, cmdline) }], outputfile)
       else:
-        self.sendCmds(['exec su -c "%s"' % cmdline], outputfile)
+        self.sendCmds([{ 'cmd': 'exec su -c "%s"' % cmdline }], outputfile)
     except AgentError:
       return None
 
     # dig through the output to get the return code
     lastline = _pop_last_line(outputfile)
     if lastline:
       m = re.search('return code \[([0-9]+)\]', lastline)
       if m:
@@ -301,19 +306,21 @@ class DeviceManagerSUT(DeviceManager):
     if (self.debug >= 3): print "sending: push " + destname
     
     filesize = os.path.getsize(localname)
     f = open(localname, 'rb')
     data = f.read()
     f.close()
 
     try:
-      retVal = self.runCmds(['push ' + destname + ' ' + str(filesize) + '\r\n', data], newline = False)
-    except AgentError:
-      retVal = False
+      retVal = self.runCmds([{ 'cmd': 'push ' + destname + ' ' + str(filesize),
+                               'data': data }])
+    except AgentError, e:
+      print "error pushing file: %s" % e.msg
+      return False
   
     if (self.debug >= 3): print "push returned: " + str(retVal)
 
     validated = False
     if (retVal):
       retline = retVal.strip()
       if (retline == None):
         # Then we failed to get back a hash from agent, try manual validation
@@ -338,17 +345,17 @@ class DeviceManagerSUT(DeviceManager):
   # returns:
   #  success: directory name
   #  failure: None
   def mkDir(self, name):
     if (self.dirExists(name)):
       return name
     else:
       try:
-        retVal = self.runCmds(['mkdr ' + name])
+        retVal = self.runCmds([{ 'cmd': 'mkdr ' + name }])
       except AgentError:
         retVal = None
       return retVal
 
   # make directory structure on the device
   # external function
   # returns:
   #  success: directory structure that we created
@@ -391,17 +398,17 @@ class DeviceManagerSUT(DeviceManager):
   # external function
   # returns:
   #  success: True
   #  failure: False
   def dirExists(self, dirname):
     match = ".*" + dirname.replace('^', '\^') + "$"
     dirre = re.compile(match)
     try:
-      data = self.runCmds(['cd ' + dirname, 'cwd'])
+      data = self.runCmds([ { 'cmd': 'cd ' + dirname }, { 'cmd': 'cwd' }])
     except AgentError:
       return False
 
     found = False
     for d in data.splitlines():
       if (dirre.match(d)):
         found = True
 
@@ -427,59 +434,59 @@ class DeviceManagerSUT(DeviceManager):
   # returns:
   #  success: array of filenames, ['file1', 'file2', ...]
   #  failure: []
   def listFiles(self, rootdir):
     rootdir = rootdir.rstrip('/')
     if (self.dirExists(rootdir) == False):
       return []
     try:
-      data = self.runCmds(['cd ' + rootdir, 'ls'])
+      data = self.runCmds([{ 'cmd': 'cd ' + rootdir }, { 'cmd': 'ls' }])
     except AgentError:
       return []
 
     files = filter(lambda x: x, data.splitlines())
     if len(files) == 1 and files[0] == '<empty>':
       # special case on the agent: empty directories return just the string "<empty>"
       return []
     return files
 
   # external function
   # returns:
   #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
   #  failure: None
   def removeFile(self, filename):
     if (self.debug>= 2): print "removing file: " + filename
     try:
-      retVal = self.runCmds(['rm ' + filename])
+      retVal = self.runCmds([{ 'cmd': 'rm ' + filename }])
     except AgentError:
       return None
 
     return retVal
   
   # does a recursive delete of directory on the device: rm -Rf remoteDir
   # external function
   # returns:
   #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
   #  failure: None
   def removeDir(self, remoteDir):
     try:
-      retVal = self.runCmds(['rmdr ' + remoteDir])
+      retVal = self.runCmds([{ 'cmd': 'rmdr ' + remoteDir }])
     except AgentError:
       return None
 
     return retVal
 
   # external function
   # returns:
   #  success: array of process tuples
   #  failure: []
   def getProcessList(self):
     try:
-      data = self.runCmds(['ps'])
+      data = self.runCmds([{ 'cmd': 'ps' }])
     except AgentError:
       return []
 
     files = []
     for line in data.splitlines():
       if line:
         pidproc = line.strip().split()
         if (len(pidproc) == 2):
@@ -502,17 +509,17 @@ class DeviceManagerSUT(DeviceManager):
     if (self.debug >= 2): print "FIRE PROC: '" + appname + "'"
 
     if (self.processExist(appname) != None):
       print "WARNING: process %s appears to be running already\n" % appname
       if (failIfRunning):
         return None
     
     try:
-      data = self.runCmds(['exec ' + appname])
+      data = self.runCmds([{ 'cmd': 'exec ' + appname }])
     except AgentError:
       return None
 
     # wait up to 30 seconds for process to start up
     timeslept = 0
     while (timeslept <= 30):
       process = self.processExist(appname)
       if (process is not None):
@@ -551,41 +558,41 @@ class DeviceManagerSUT(DeviceManager):
   # external function
   # returns:
   #  success: True
   #  failure: False
   def killProcess(self, appname, forceKill=False):
     if forceKill:
       print "WARNING: killProcess(): forceKill parameter unsupported on SUT"
     try:
-      data = self.runCmds(['kill ' + appname])
+      data = self.runCmds([{ 'cmd': 'kill ' + appname }])
     except AgentError:
       return False
 
     return True
 
   # external function
   # returns:
   #  success: tmpdir, string
   #  failure: None
   def getTempDir(self):
     try:
-      data = self.runCmds(['tmpd'])
+      data = self.runCmds([{ 'cmd': 'tmpd' }])
     except AgentError:
       return None
 
     return data.strip()
 
   # external function
   # returns:
   #  success: filecontents
   #  failure: None
   def catFile(self, remoteFile):
     try:
-      data = self.runCmds(['cat ' + remoteFile])
+      data = self.runCmds([{ 'cmd': 'cat ' + remoteFile }])
     except AgentError:
       return None
 
     return data
 
   # external function
   # returns:
   #  success: output of pullfile, string
@@ -644,17 +651,17 @@ class DeviceManagerSUT(DeviceManager):
     prompt = self.base_prompt + self.prompt_sep
     buffer = ''
     
     # expected return value:
     # <filename>,<filesize>\n<filedata>
     # or, if error,
     # <filename>,-1\n<error message>
     try:
-      data = self.runCmds(['pull ' + remoteFile])
+      data = self.runCmds([{ 'cmd': 'pull ' + remoteFile }])
     except AgentError:
       return None
 
     # read metadata; buffer the rest
     metadata, sep, buffer = read_until_char('\n', buffer, 'could not find metadata')
     if not metadata:
       return None
     if self.debug >= 3:
@@ -763,17 +770,17 @@ class DeviceManagerSUT(DeviceManager):
 
   # external function
   # returns:
   #  success: True
   #  failure: False
   #  Throws a FileError exception when null (invalid dir/filename)
   def isDir(self, remotePath):
     try:
-      data = self.runCmds(['isdir ' + remotePath])
+      data = self.runCmds([{ 'cmd': 'isdir ' + remotePath }])
     except AgentError:
       # normally there should be no error here; a nonexistent file/directory will
       # return the string "<filename>: No such file or directory".
       # However, I've seen AGENT-WARNING returned before. 
       return False
 
     retVal = data.strip()
     if not retVal:
@@ -799,17 +806,17 @@ class DeviceManagerSUT(DeviceManager):
   
   # return the md5 sum of a remote file
   # internal function
   # returns:
   #  success: MD5 hash for given filename
   #  failure: None
   def getRemoteHash(self, filename):
     try:
-      data = self.runCmds(['hash ' + filename])
+      data = self.runCmds([{ 'cmd': 'hash ' + filename }])
     except AgentError:
       return None
 
     retVal = None
     if data:
       retVal = data.strip()
     if (self.debug >= 3): print "remote hash returned: '" + retVal + "'"
     return retVal
@@ -827,32 +834,36 @@ class DeviceManagerSUT(DeviceManager):
   #       /reftest
   #       /mochitest
   #
   # external function
   # returns:
   #  success: path for device root
   #  failure: None
   def getDeviceRoot(self):
-    try:
-      data = self.runCmds(['testroot'])
-    except:
-      return None
+    if self.deviceRoot:
+      deviceRoot = self.deviceRoot
+    else:
+      try:
+        data = self.runCmds([{ 'cmd': 'testroot' }])
+      except:
+        return None
 
-    deviceRoot = data.strip() + '/tests'
+      deviceRoot = data.strip() + '/tests'
 
     if (not self.dirExists(deviceRoot)):
       if (self.mkDir(deviceRoot) == None):
         return None
 
-    return deviceRoot
+    self.deviceRoot = deviceRoot
+    return self.deviceRoot
 
   def getAppRoot(self, packageName):
     try:
-      data = self.runCmds(['getapproot '+packageName])
+      data = self.runCmds([{ 'cmd': 'getapproot '+packageName }])
     except:
       return None
 
     return data.strip()
 
   # external function
   # returns:
   #  success: output of unzip command
@@ -870,17 +881,17 @@ class DeviceManagerSUT(DeviceManager):
     elif self.fileExists('/' + filename):
       dir = '/' + filename
     elif self.fileExists(devroot + '/' + filename):
       dir = devroot + '/' + filename
     else:
       return None
 
     try:
-      data = self.runCmds(['cd ' + dir, 'unzp ' + filename])
+      data = self.runCmds([{ 'cmd': 'cd ' + dir }, { 'cmd': 'unzp ' + filename }])
     except AgentError:
       return None
 
     return data
 
   # external function
   # returns:
   #  success: status from test agent
@@ -891,27 +902,28 @@ class DeviceManagerSUT(DeviceManager):
     if (self.debug > 3): print "INFO: sending rebt command"
     callbacksvrstatus = None    
 
     if (ipAddr is not None):
     #create update.info file:
       try:
         destname = '/data/data/com.mozilla.SUTAgentAndroid/files/update.info'
         data = "%s,%s\rrebooting\r" % (ipAddr, port)
-        self.runCmds(['push ' + destname + ' ' + str(len(data)) + '\r\n', data], newline = False)
+        self.runCmds([{ 'cmd': 'push %s %s' % (destname, len(data)),
+                        'data': data }])
       except AgentError:
         return None
 
       ip, port = self.getCallbackIpAndPort(ipAddr, port)
       cmd += " %s %s" % (ip, port)
       # Set up our callback server
       callbacksvr = callbackServer(ip, port, self.debug)
 
     try:
-      status = self.runCmds([cmd])
+      status = self.runCmds([{ 'cmd': cmd }])
     except AgentError:
       return None
 
     if (ipAddr is not None):
       status = callbacksvr.disconnect()
 
     if (self.debug > 3): print "INFO: rebt- got status back: " + str(status)
     return status
@@ -938,17 +950,17 @@ class DeviceManagerSUT(DeviceManager):
     collapseSpaces = re.compile('  +')
 
     directives = ['os','id','uptime','systime','screen','rotation','memory','process',
                   'disk','power']
     if (directive in directives):
       directives = [directive]
 
     for d in directives:
-      data = self.runCmds(['info ' + d])
+      data = self.runCmds([{ 'cmd': 'info ' + d }])
       if (data is None):
         continue
       data = collapseSpaces.sub(' ', data)
       result[d] = data.split('\n')
 
     # Get rid of any 0 length members of the arrays
     for k, v in result.iteritems():
       result[k] = filter(lambda x: x != '', result[k])
@@ -975,17 +987,17 @@ class DeviceManagerSUT(DeviceManager):
   # returns:
   #  success: output from agent for inst command
   #  failure: None
   def installApp(self, appBundlePath, destPath=None):
     cmd = 'inst ' + appBundlePath
     if destPath:
       cmd += ' ' + destPath
     try:
-      data = self.runCmds([cmd])
+      data = self.runCmds([{ 'cmd': cmd }])
     except AgentError:
       return None
 
     f = re.compile('Failure')
     for line in data.split():
       if (f.match(line)):
         return data
     return None
@@ -1001,17 +1013,17 @@ class DeviceManagerSUT(DeviceManager):
   # returns:
   #  success: True
   #  failure: None
   def uninstallAppAndReboot(self, appName, installPath=None):
     cmd = 'uninst ' + appName
     if installPath:
       cmd += ' ' + installPath
     try:
-      data = self.runCmds([cmd])
+      data = self.runCmds([{ 'cmd': cmd }])
     except AgentError:
       return None
 
     if (self.debug > 3): print "uninstallAppAndReboot: " + str(data)
     return True
 
   """
   Updates the application on the device.
@@ -1046,17 +1058,17 @@ class DeviceManagerSUT(DeviceManager):
       ip, port = self.getCallbackIpAndPort(ipAddr, port)
       cmd += " %s %s" % (ip, port)
       # Set up our callback server
       callbacksvr = callbackServer(ip, port, self.debug)
 
     if (self.debug >= 3): print "INFO: updateApp using command: " + str(cmd)
 
     try:
-      status = self.runCmds([cmd])
+      status = self.runCmds([{ 'cmd': cmd }])
     except AgentError:
       return None
 
     if ipAddr is not None:
       status = callbacksvr.disconnect()
 
     if (self.debug >= 3): print "INFO: updateApp: got status back: " + str(status)
 
@@ -1066,17 +1078,17 @@ class DeviceManagerSUT(DeviceManager):
     return the current time on the device
   """
   # external function
   # returns:
   #  success: time in ms
   #  failure: None
   def getCurrentTime(self):
     try:
-      data = self.runCmds(['clok'])
+      data = self.runCmds([{ 'cmd': 'clok' }])
     except AgentError:
       return None
 
     return data.strip()
 
   """
     Connect the ipaddress and port for a callback ping.  Defaults to current IP address
     And ports starting at 30000.
@@ -1170,30 +1182,30 @@ class DeviceManagerSUT(DeviceManager):
     if (width < 100 or width > 9999):
       return False
 
     if (height < 100 or height > 9999):
       return False
 
     if (self.debug >= 3): print "INFO: adjusting screen resolution to %s, %s and rebooting" % (width, height)
     try:
-      self.runCmds(["exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width)])
-      self.runCmds(["exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height)])
+      self.runCmds([{ 'cmd': "exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width) }])
+      self.runCmds([{ 'cmd': "exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height) }])
     except AgentError:
       return False
 
     return True
 
   # external function
   # returns:
   #  success: True
   #  failure: False
   def chmodDir(self, remoteDir):
     try:
-      self.runCmds(["chmod "+remoteDir])
+      self.runCmds([{ 'cmd': "chmod "+remoteDir }])
     except AgentError:
       return False
     return True
 
 gCallbackData = ''
 
 class myServer(SocketServer.TCPServer):
   allow_reuse_address = True
--- a/build/mobile/sutagent/android/DoCommand.java
+++ b/build/mobile/sutagent/android/DoCommand.java
@@ -78,16 +78,17 @@ import android.content.pm.ServiceInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Debug;
 import android.os.Environment;
 import android.os.StatFs;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Surface;
 import android.view.WindowManager;
 
 public class DoCommand {
 
     String lineSep = System.getProperty("line.separator");
@@ -3479,17 +3480,17 @@ private void CancelNotification()
 
     public String StartPrg(String [] progArray, OutputStream out)
         {
         String sRet = "";
         int    lcv = 0;
 
         try
             {
-            pProc = Runtime.getRuntime().exec(progArray);
+            pProc = Runtime.getRuntime().exec(this.getSuArgs(TextUtils.join(" ", progArray)));
             RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
             outThrd.start();
             while (lcv < 30) {
                 try {
                     outThrd.join(10000);
                     int nRetCode = pProc.exitValue();
                     sRet = "return code [" + nRetCode + "]";
                     break;
--- a/build/mobile/sutagent/android/SUTAgentAndroid.java
+++ b/build/mobile/sutagent/android/SUTAgentAndroid.java
@@ -28,16 +28,17 @@ import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.net.Uri;
 import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
 import android.os.BatteryManager;
+import android.os.Build.VERSION;
 import android.os.Bundle;
 import android.os.Handler;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -120,35 +121,51 @@ public class SUTAgentAndroid extends Act
         SUTAgentAndroid.Pool = dc.GetIniData("Registration Server", "POOL", sIniFile);
 
         tv = (TextView) this.findViewById(R.id.Textview01);
 
         if (getLocalIpAddress() == null)
             setUpNetwork(sIniFile);
 
         String macAddress = "Unknown";
-        try {
-            NetworkInterface iface = NetworkInterface.getByInetAddress(InetAddress.getAllByName(getLocalIpAddress())[0]);
-            if (iface != null)
-                {
-                    byte[] mac = iface.getHardwareAddress();
-                    if (mac != null)
-                        {
-                            StringBuilder sb = new StringBuilder();
-                            Formatter f = new Formatter(sb);
-                            for (int i = 0; i < mac.length; i++)
-                                {
-                                    f.format("%02x%s", mac[i], (i < mac.length - 1) ? ":" : "");
-                                }
-                            macAddress = sUniqueID = sb.toString();
-                        }
-                }
+        if (android.os.Build.VERSION.SDK_INT > 8) {
+            try {
+                NetworkInterface iface = NetworkInterface.getByInetAddress(InetAddress.getAllByName(getLocalIpAddress())[0]);
+                if (iface != null)
+                    {
+                        byte[] mac = iface.getHardwareAddress();
+                        if (mac != null)
+                            {
+                                StringBuilder sb = new StringBuilder();
+                                Formatter f = new Formatter(sb);
+                                for (int i = 0; i < mac.length; i++)
+                                    {
+                                        f.format("%02x%s", mac[i], (i < mac.length - 1) ? ":" : "");
+                                    }
+                                macAddress = sUniqueID = sb.toString();
+                            }
+                    }
+            }
+            catch (UnknownHostException ex) {}
+            catch (SocketException ex) {}
         }
-        catch (UnknownHostException ex) {}
-        catch (SocketException ex) {}
+        else
+            {
+                // Fall back to getting info from wifiman on older versions of Android,
+                // which don't support the NetworkInterface interface
+                WifiManager wifiMan = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+                if (wifiMan != null)
+                    {
+                        WifiInfo wifi = wifiMan.getConnectionInfo();
+                        if (wifi != null)
+                            macAddress = wifi.getMacAddress();
+                        if (macAddress != null)
+                            sUniqueID = macAddress;
+                    }
+            }
 
         if (sUniqueID == null)
             {
             BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
             if ((ba != null) && (ba.isEnabled() != true))
                 {
                 ba.enable();
                 while(ba.getState() != BluetoothAdapter.STATE_ON)
--- a/build/pymake/pymake/process.py
+++ b/build/pymake/pymake/process.py
@@ -203,30 +203,36 @@ class PythonJob(Job):
             if self.module not in sys.modules:
                 load_module_recursive(self.module,
                                       sys.path + self.pycommandpath)
             if self.module not in sys.modules:
                 print >>sys.stderr, "No module named '%s'" % self.module
                 return -127                
             m = sys.modules[self.module]
             if self.method not in m.__dict__:
-                print >>sys.stderr, "No method named '%s' in module %s" % (method, module)
+                print >>sys.stderr, "No method named '%s' in module %s" % (self.method, self.module)
                 return -127
-            m.__dict__[self.method](self.argv)
+            rv = m.__dict__[self.method](self.argv)
+            if rv != 0 and rv is not None:
+                print >>sys.stderr, (
+                    "Native command '%s %s' returned value '%s'" %
+                    (self.module, self.method, rv))
+                return (rv if isinstance(rv, int) else 1)
+
         except PythonException, e:
             print >>sys.stderr, e
             return e.exitcode
         except:
             e = sys.exc_info()[1]
-            if isinstance(e, SystemExit) and (e.code == 0 or e.code == '0'):
+            if isinstance(e, SystemExit) and (e.code == 0 or e.code is None):
                 pass # sys.exit(0) is not a failure
             else:
                 print >>sys.stderr, e
                 print >>sys.stderr, traceback.print_exc()
-                return -127
+                return (e.code if isinstance(e.code, int) else 1)
         finally:
             os.environ = oldenv
         return 0
 
 def job_runner(job):
     """
     Run a job. Called in a Process pool.
     """
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/native-command-return-fail1.mk
@@ -0,0 +1,8 @@
+#T gmake skip
+#T returncode: 2
+
+CMD = %pycmd asplode_return
+PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
+
+all:
+	$(CMD) 1
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/native-command-return-fail2.mk
@@ -0,0 +1,8 @@
+#T gmake skip
+#T returncode: 2
+
+CMD = %pycmd asplode_return
+PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
+
+all:
+	$(CMD) not-an-integer
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/native-command-return.mk
@@ -0,0 +1,11 @@
+#T gmake skip
+
+CMD = %pycmd asplode_return
+PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
+
+all:
+	$(CMD) 0
+	-$(CMD) 1
+	$(CMD) None
+	-$(CMD) not-an-integer
+	@echo TEST-PASS
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/native-command-sys-exit-fail1.mk
@@ -0,0 +1,8 @@
+#T gmake skip
+#T returncode: 2
+
+CMD = %pycmd asplode
+PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
+
+all:
+	$(CMD) 1
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/native-command-sys-exit-fail2.mk
@@ -0,0 +1,8 @@
+#T gmake skip
+#T returncode: 2
+
+CMD = %pycmd asplode
+PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
+
+all:
+	$(CMD) not-an-integer
--- a/build/pymake/tests/native-command-sys-exit.mk
+++ b/build/pymake/tests/native-command-sys-exit.mk
@@ -1,8 +1,11 @@
 #T gmake skip
 
 CMD = %pycmd asplode
 PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
 
 all:
 	$(CMD) 0
+	-$(CMD) 1
+	$(CMD) None
+	-$(CMD) not-an-integer
 	@echo TEST-PASS
--- a/build/pymake/tests/pycmd.py
+++ b/build/pymake/tests/pycmd.py
@@ -3,11 +3,21 @@ import os, sys
 def writetofile(args):
   with open(args[0], 'w') as f:
     f.write(' '.join(args[1:]))
 
 def writeenvtofile(args):
   with open(args[0], 'w') as f:
     f.write(os.environ[args[1]])
 
+def convertasplode(arg):
+  try:
+    return int(arg)
+  except:
+    return (None if arg == "None" else arg)
+
 def asplode(args):
-  sys.exit(0)
-  return 0
+  arg0 = convertasplode(args[0])
+  sys.exit(arg0)
+
+def asplode_return(args):
+  arg0 = convertasplode(args[0])
+  return arg0
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -17,16 +17,18 @@
 #include "nsJSPrincipals.h"
 #include "nsVoidArray.h"
 #include "nsHashtable.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIClassInfoImpl.h"
 #include "nsDOMError.h"
 #include "nsIContentSecurityPolicy.h"
+#include "nsContentUtils.h"
+#include "jswrapper.h"
 
 #include "nsPrincipal.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/HashFunctions.h"
 
 using namespace mozilla;
 
@@ -958,16 +960,28 @@ NS_IMETHODIMP
 nsPrincipal::SetDomain(nsIURI* aDomain)
 {
   mDomain = NS_TryToMakeImmutable(aDomain);
   mDomainImmutable = URIIsImmutable(mDomain);
   
   // Domain has changed, forget cached security policy
   SetSecurityPolicy(nsnull);
 
+  // Recompute all wrappers between compartments using this principal and other
+  // non-chrome compartments.
+  JSContext *cx = nsContentUtils::GetSafeJSContext();
+  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+  JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
+  bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(),
+                                       js::CompartmentsWithPrincipals(principals));
+  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+  success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals),
+                                  js::ContentCompartmentsOnly());
+  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+
   return NS_OK;
 }
 
 nsresult
 nsPrincipal::InitFromPersistent(const char* aPrefName,
                                 const nsCString& aToken,
                                 const nsCString& aSubjectName,
                                 const nsACString& aPrettyName,
--- a/config/JarMaker.py
+++ b/config/JarMaker.py
@@ -273,17 +273,17 @@ class JarMaker(object):
     jarfile is the basename of the jarfile or the directory name for 
     flat output, lines is a pushback_iterator of the lines of jar.mn,
     the remaining options are carried over from makeJar.
     '''
 
     # chromebasepath is used for chrome registration manifests
     # %s is getting replaced with chrome/ for chrome.manifest, and with
     # an empty string for jarfile.manifest
-    chromebasepath = '%s' + jarfile
+    chromebasepath = '%s' + os.path.basename(jarfile)
     if self.outputFormat == 'jar':
       chromebasepath = 'jar:' + chromebasepath + '.jar!'
     chromebasepath += '/'
 
     jarfile = os.path.join(jardir, jarfile)
     jf = None
     if self.outputFormat == 'jar':
       #jar
--- a/configure.in
+++ b/configure.in
@@ -49,17 +49,16 @@ dnl ====================================
 _SUBDIR_HOST_CXXFLAGS="$HOST_CXXFLAGS"
 _SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS"
 _SUBDIR_CONFIG_ARGS="$ac_configure_args"
 
 dnl Set the version number of the libs included with mozilla
 dnl ========================================================
 MOZJPEG=62
 MOZPNG=10511
-MOZZLIB=1.2.3
 NSPR_VERSION=4
 NSS_VERSION=3
 
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=1.2.0
 PERL_VERSION=5.006
 PYTHON_VERSION=2.5
@@ -1080,18 +1079,16 @@ ASM_SUFFIX=s
 IMPORT_LIB_SUFFIX=
 TARGET_MD_ARCH=unix
 DIRENT_INO=d_ino
 WIN_TOP_SRC=
 MOZ_USER_DIR=".mozilla"
 
 MOZ_JPEG_CFLAGS=
 MOZ_JPEG_LIBS='$(call EXPAND_LIBNAME_PATH,mozjpeg,$(DEPTH)/media/libjpeg)'
-MOZ_ZLIB_CFLAGS=
-MOZ_ZLIB_LIBS='$(call EXPAND_LIBNAME_PATH,mozz,$(DEPTH)/modules/zlib/src)'
 MOZ_BZ2_CFLAGS=
 MOZ_BZ2_LIBS='$(call EXPAND_LIBNAME_PATH,bz2,$(DEPTH)/modules/libbz2/src)'
 MOZ_PNG_CFLAGS=
 MOZ_PNG_LIBS='$(call EXPAND_LIBNAME_PATH,mozpng,$(DEPTH)/media/libpng)'
 
 MOZ_JS_STATIC_LIBS='$(call EXPAND_LIBNAME_PATH,js_static,$(LIBXUL_DIST)/lib)'
 MOZ_JS_SHARED_LIBS='$(call EXPAND_LIBNAME_PATH,mozjs,$(LIBXUL_DIST)/lib)'
 DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/bin -lxpcom -lxpcom_core -lmozalloc'
@@ -4029,61 +4026,32 @@ fi
 CFLAGS=$_SAVE_CFLAGS
 LDFLAGS=$_SAVE_LDFLAGS
 LIBS=$_SAVE_LIBS
 
 if test -n "${JPEG_DIR}" -a -d "${JPEG_DIR}" -a "$MOZ_NATIVE_JPEG" = 1; then
     MOZ_JPEG_CFLAGS="-I${JPEG_DIR}/include"
     MOZ_JPEG_LIBS="-L${JPEG_DIR}/lib ${MOZ_JPEG_LIBS}"
 fi
+fi # SKIP_LIBRARY_CHECKS
 
 dnl system ZLIB support
 dnl ========================================================
-MOZ_ARG_WITH_STRING(system-zlib,
-[  --with-system-zlib[=PFX]
-                          Use system libz [installed at prefix PFX]],
-    ZLIB_DIR=$withval)
-
-_SAVE_CFLAGS=$CFLAGS
-_SAVE_LDFLAGS=$LDFLAGS
-_SAVE_LIBS=$LIBS
-if test -n "${ZLIB_DIR}" -a "${ZLIB_DIR}" != "yes"; then
-    CFLAGS="-I${ZLIB_DIR}/include $CFLAGS"
-    LDFLAGS="-L${ZLIB_DIR}/lib $LDFLAGS"
-fi
-if test -z "$ZLIB_DIR" -o "$ZLIB_DIR" = no; then
-    MOZ_NATIVE_ZLIB=
-else
-    AC_CHECK_LIB(z, gzread, [MOZ_NATIVE_ZLIB=1 MOZ_ZLIB_LIBS="-lz"],
-	[MOZ_NATIVE_ZLIB=])
-fi
-if test "$MOZ_NATIVE_ZLIB" = 1; then
-    MOZZLIBNUM=`echo $MOZZLIB | awk -F. '{printf "0x%x\n", ((($1 * 16 + $2) * 16) + $3) * 16 + $4}'`
-    AC_TRY_COMPILE([ #include <stdio.h>
-                     #include <string.h>
-                     #include <zlib.h> ],
-                   [ #if ZLIB_VERNUM < $MOZZLIBNUM
-                     #error "Insufficient zlib version ($MOZZLIBNUM required)."
-                     #endif ],
-                   MOZ_NATIVE_ZLIB=1,
-                   AC_MSG_ERROR([Insufficient zlib version for --with-system-zlib ($MOZZLIB required)]))
-fi
-CFLAGS=$_SAVE_CFLAGS
-LDFLAGS=$_SAVE_LDFLAGS
-LIBS=$_SAVE_LIBS
-
-if test "${ZLIB_DIR}" -a -d "${ZLIB_DIR}" -a "$MOZ_NATIVE_ZLIB" = 1; then
-    MOZ_ZLIB_CFLAGS="-I${ZLIB_DIR}/include"
-    MOZ_ZLIB_LIBS="-L${ZLIB_DIR}/lib ${ZLIB_LIBS}"
+MOZ_ZLIB_CHECK([1.2.3])
+
+if test "$MOZ_NATIVE_ZLIB" != 1; then
+    MOZ_ZLIB_CFLAGS=
+    MOZ_ZLIB_LIBS='$(call EXPAND_LIBNAME_PATH,mozz,'"$_objdir"'/modules/zlib/src)'
 fi
 
 if test "$MOZ_LINKER" = 1 -a "$MOZ_NATIVE_ZLIB" != 1; then
     AC_MSG_ERROR([Custom dynamic linker requires --with-system-zlib])
 fi
 
+if test -z "$SKIP_LIBRARY_CHECKS"; then
 dnl system BZIP2 Support
 dnl ========================================================
 MOZ_ARG_WITH_STRING(system-bz2,
 [  --with-system-bz2[=PFX]
                           Use system libbz2 [installed at prefix PFX]],
     BZ2_DIR=$withval)
 
 _SAVE_CFLAGS=$CFLAGS
@@ -8519,24 +8487,21 @@ COMPILE_CXXFLAGS=`echo \
     $_DEFINES_CXXFLAGS \
 	$_DEPEND_CFLAGS \
     $COMPILE_CXXFLAGS`
 
 AC_SUBST(MOZ_NATIVE_MAKEDEPEND)
 AC_SUBST(SYSTEM_LIBXUL)
 AC_SUBST(MOZ_NATIVE_JPEG)
 AC_SUBST(MOZ_NATIVE_PNG)
-AC_SUBST(MOZ_NATIVE_ZLIB)
 AC_SUBST(MOZ_NATIVE_BZ2)
 
 AC_SUBST(MOZ_FLEXBOX)
 AC_SUBST(MOZ_JPEG_CFLAGS)
 AC_SUBST(MOZ_JPEG_LIBS)
-AC_SUBST(MOZ_ZLIB_CFLAGS)
-AC_SUBST(MOZ_ZLIB_LIBS)
 AC_SUBST(MOZ_BZ2_CFLAGS)
 AC_SUBST(MOZ_BZ2_LIBS)
 AC_SUBST(MOZ_PNG_CFLAGS)
 AC_SUBST(MOZ_PNG_LIBS)
 
 AC_SUBST(NSPR_CFLAGS)
 AC_SUBST(NSPR_LIBS)
 AC_SUBST(MOZ_NATIVE_NSPR)
@@ -9182,16 +9147,22 @@ if test "$MOZ_MEMORY"; then
    ac_configure_args="$ac_configure_args --enable-jemalloc"
 fi
 if test -n "$MOZ_GLUE_LDFLAGS"; then
    export MOZ_GLUE_LDFLAGS
 fi
 if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then
    export MOZ_GLUE_PROGRAM_LDFLAGS
 fi
+if test "$MOZ_NATIVE_ZLIB" != 1 -a "$OS_ARCH" = "WINNT"; then
+   MOZ_ZLIB_LIBS=
+fi
+export MOZ_NATIVE_ZLIB
+export MOZ_ZLIB_CFLAGS
+export MOZ_ZLIB_LIBS
 export MOZ_APP_NAME
 export STLPORT_CPPFLAGS
 export STLPORT_LDFLAGS
 export STLPORT_LIBS
 AC_OUTPUT_SUBDIRS(js/src)
 ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 
 fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -32,31 +32,31 @@ EXPORTS_mozilla/dom = \
 CPPSRCS	= \
 	CanvasImageCache.cpp \
 	CanvasUtils.cpp \
 	nsCanvasRenderingContext2D.cpp \
 	nsCanvasRenderingContext2DAzure.cpp \
 	DocumentRendererParent.cpp \
 	DocumentRendererChild.cpp \
 	ImageData.cpp \
-	WebGLExtensionCompressedTextureS3TC.cpp \
 	$(NULL)
 
 ifdef MOZ_WEBGL
 
 CPPSRCS += \
 	WebGLContext.cpp \
 	WebGLContextGL.cpp \
 	WebGLContextUtils.cpp \
 	WebGLContextReporter.cpp \
 	WebGLContextValidate.cpp \
 	WebGLExtensionStandardDerivatives.cpp \
 	WebGLExtensionTextureFilterAnisotropic.cpp \
 	WebGLExtensionLoseContext.cpp \
 	WebGLTexelConversions.cpp \
+	WebGLExtensionCompressedTextureS3TC.cpp \
 	$(NULL)
 
 DEFINES += -DUSE_ANGLE
 USE_ANGLE=1
 
 else
 
 CPPSRCS += WebGLContextNotSupported.cpp
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -11,18 +11,16 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = content
 LIBRARY_NAME = gkconmedia_s
 LIBXUL_LIBRARY = 1
 
 EXPORTS = \
   AudioSegment.h \
   FileBlockCache.h \
-  MediaEngine.h \
-  MediaEngineDefault.h \
   MediaResource.h \
   MediaSegment.h \
   MediaStreamGraph.h \
   nsAudioAvailableEventManager.h \
   nsBuiltinDecoder.h \
   nsBuiltinDecoderStateMachine.h \
   nsBuiltinDecoderReader.h \
   nsDOMMediaStream.h \
@@ -34,17 +32,16 @@ EXPORTS = \
   VideoFrameContainer.h \
   VideoUtils.h \
   VideoSegment.h \
   $(NULL)
 
 CPPSRCS = \
   AudioSegment.cpp \
   FileBlockCache.cpp \
-  MediaEngineDefault.cpp \
   MediaResource.cpp \
   MediaStreamGraph.cpp \
   nsAudioAvailableEventManager.cpp \
   nsBuiltinDecoder.cpp \
   nsBuiltinDecoderStateMachine.cpp \
   nsBuiltinDecoderReader.cpp \
   nsDOMMediaStream.cpp \
   nsMediaCache.cpp \
@@ -82,16 +79,18 @@ endif
 ifdef MOZ_GSTREAMER
 PARALLEL_DIRS += gstreamer
 endif
 
 ifdef MOZ_MEDIA_PLUGINS
 PARALLEL_DIRS += plugins
 endif
 
+PARALLEL_DIRS += webrtc
+
 TEST_DIRS += test
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/content/media/MediaEngineDefault.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "MediaEngineDefault.h"
-
-#include "nsCOMPtr.h"
-#include "nsDOMFile.h"
-#include "nsILocalFile.h"
-
-#ifdef MOZ_WIDGET_ANDROID
-#include "AndroidBridge.h"
-#include "nsISupportsUtils.h"
-#endif
-
-#define WIDTH 320
-#define HEIGHT 240
-#define FPS 10
-#define CHANNELS 1
-#define RATE USECS_PER_S
-
-namespace mozilla {
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultVideoSource, nsITimerCallback)
-/**
- * Default video source.
- */
-void
-MediaEngineDefaultVideoSource::GetName(nsAString& aName)
-{
-  aName.Assign(NS_LITERAL_STRING("Default Video Device"));
-  return;
-}
-
-void
-MediaEngineDefaultVideoSource::GetUUID(nsAString& aUUID)
-{
-  aUUID.Assign(NS_LITERAL_STRING("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676"));
-  return;
-}
-
-already_AddRefed<nsDOMMediaStream>
-MediaEngineDefaultVideoSource::Allocate()
-{
-  if (mState != kReleased) {
-    return NULL;
-  }
-
-  mState = kAllocated;
-  return nsDOMMediaStream::CreateInputStream();
-}
-
-nsresult
-MediaEngineDefaultVideoSource::Deallocate()
-{
-  if (mState != kStopped && mState != kAllocated) {
-    return NS_ERROR_FAILURE;
-  }
-  mState = kReleased;
-  return NS_OK;
-}
-
-MediaEngineVideoOptions
-MediaEngineDefaultVideoSource::GetOptions()
-{
-  MediaEngineVideoOptions aOpts;
-  aOpts.mWidth = WIDTH;
-  aOpts.mHeight = HEIGHT;
-  aOpts.mMaxFPS = FPS;
-  aOpts.codecType = kVideoCodecI420;
-  return aOpts;
-}
-
-nsresult
-MediaEngineDefaultVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
-{
-  if (mState != kAllocated) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  if (!mTimer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mSource = aStream;
-
-  // Allocate a single blank Image
-  layers::Image::Format format = layers::Image::PLANAR_YCBCR;
-  mImageContainer = layers::LayerManager::CreateImageContainer();
-
-  nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&format, 1);
-
-  int len = ((WIDTH * HEIGHT) * 3 / 2);
-  mImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
-  PRUint8* frame = (PRUint8*) PR_Malloc(len);
-  memset(frame, 0x80, len); // Gray
-
-  const PRUint8 lumaBpp = 8;
-  const PRUint8 chromaBpp = 4;
-
-  layers::PlanarYCbCrImage::Data data;
-  data.mYChannel = frame;
-  data.mYSize = gfxIntSize(WIDTH, HEIGHT);
-  data.mYStride = WIDTH * lumaBpp / 8.0;
-  data.mCbCrStride = WIDTH * chromaBpp / 8.0;
-  data.mCbChannel = frame + HEIGHT * data.mYStride;
-  data.mCrChannel = data.mCbChannel + HEIGHT * data.mCbCrStride / 2;
-  data.mCbCrSize = gfxIntSize(WIDTH / 2, HEIGHT / 2);
-  data.mPicX = 0;
-  data.mPicY = 0;
-  data.mPicSize = gfxIntSize(WIDTH, HEIGHT);
-  data.mStereoMode = layers::STEREO_MODE_MONO;
-
-  // SetData copies data, so we can free the frame
-  mImage->SetData(data);
-  PR_Free(frame);
-
-
-  // AddTrack takes ownership of segment
-  VideoSegment *segment = new VideoSegment();
-  segment->AppendFrame(image.forget(), USECS_PER_S / FPS, gfxIntSize(WIDTH, HEIGHT));
-  mSource->AddTrack(aID, RATE, 0, segment);
-
-  // We aren't going to add any more tracks
-  mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
-
-  // Remember TrackID so we can end it later
-  mTrackID = aID;
-
-  // Start timer for subsequent frames
-  mTimer->InitWithCallback(this, 1000 / FPS, nsITimer::TYPE_REPEATING_SLACK);
-  mState = kStarted;
-
-  return NS_OK;
-}
-
-nsresult
-MediaEngineDefaultVideoSource::Stop()
-{
-  if (mState != kStarted) {
-    return NS_ERROR_FAILURE;
-  }
-  if (!mTimer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mTimer->Cancel();
-  mTimer = NULL;
-
-  mSource->EndTrack(mTrackID);
-  mSource->Finish();
-
-  mState = kStopped;
-  return NS_OK;
-}
-
-nsresult
-MediaEngineDefaultVideoSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
-{
-  *aFile = nsnull;
-
-#ifndef MOZ_WIDGET_ANDROID
-  return NS_ERROR_NOT_IMPLEMENTED;
-#else
-  if (!AndroidBridge::Bridge()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  nsAutoString filePath;
-  AndroidBridge::Bridge()->ShowFilePickerForMimeType(filePath, NS_LITERAL_STRING("image/*"));
-
-  nsCOMPtr<nsIFile> file;
-  nsresult rv = NS_NewLocalFile(filePath, false, getter_AddRefs(file));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NS_ADDREF(*aFile = new nsDOMFileFile(file));
-  return NS_OK;
-#endif
-}
-
-NS_IMETHODIMP
-MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
-{
-  VideoSegment segment;
-
-  nsRefPtr<layers::PlanarYCbCrImage> image = mImage;
-  segment.AppendFrame(image.forget(), USECS_PER_S / FPS, gfxIntSize(WIDTH, HEIGHT));
-  mSource->AppendToTrack(mTrackID, &segment);
-
-  return NS_OK;
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultAudioSource, nsITimerCallback)
-/**
- * Default audio source.
- */
-void
-MediaEngineDefaultAudioSource::GetName(nsAString& aName)
-{
-  aName.Assign(NS_LITERAL_STRING("Default Audio Device"));
-  return;
-}
-
-void
-MediaEngineDefaultAudioSource::GetUUID(nsAString& aUUID)
-{
-  aUUID.Assign(NS_LITERAL_STRING("B7CBD7C1-53EF-42F9-8353-73F61C70C092"));
-  return;
-}
-
-already_AddRefed<nsDOMMediaStream>
-MediaEngineDefaultAudioSource::Allocate()
-{
-  if (mState != kReleased) {
-    return NULL;
-  }
-  mState = kAllocated;
-  return nsDOMMediaStream::CreateInputStream();
-}
-
-nsresult
-MediaEngineDefaultAudioSource::Deallocate()
-{
-  if (mState != kStopped && mState != kAllocated) {
-    return NS_ERROR_FAILURE;
-  }
-  mState = kReleased;
-  return NS_OK;
-}
-
-nsresult
-MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
-{
-  if (mState != kAllocated) {
-    return NULL;
-  }
-
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  if (!mTimer) {
-    return NULL;
-  }
-
-  mSource = aStream;
-
-  // AddTrack will take ownership of segment
-  AudioSegment* segment = new AudioSegment();
-  segment->Init(CHANNELS);
-  mSource->AddTrack(aID, RATE, 0, segment);
-
-  // We aren't going to add any more tracks
-  mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
-
-  // Remember TrackID so we can finish later
-  mTrackID = aID;
-
-  // 1 Audio frame per Video frame
-  mTimer->InitWithCallback(this, 1000 / FPS, nsITimer::TYPE_REPEATING_SLACK);
-  mState = kStarted;
-
-  return NS_OK;
-}
-
-nsresult
-MediaEngineDefaultAudioSource::Stop()
-{
-  if (mState != kStarted) {
-    return NS_ERROR_FAILURE;
-  }
-  if (!mTimer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mTimer->Cancel();
-  mTimer = NULL;
-
-  mSource->EndTrack(mTrackID);
-  mSource->Finish();
-
-  mState = kStopped;
-  return NS_OK;
-}
-
-nsresult
-MediaEngineDefaultAudioSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
-{
-   return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
-{
-  AudioSegment segment;
-  segment.Init(CHANNELS);
-  segment.InsertNullDataAtStart(1);
-
-  mSource->AppendToTrack(mTrackID, &segment);
-
-  return NS_OK;
-}
-
-void
-MediaEngineDefault::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
-  aVSources->AppendElement(mVSource);
-  return;
-}
-
-void
-MediaEngineDefault::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
-  aASources->AppendElement(mASource);
-  return;
-}
-
-} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/Makefile.in
@@ -0,0 +1,45 @@
+# 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/.
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = content
+LIBRARY_NAME = gkconwebrtc_s
+LIBXUL_LIBRARY = 1
+
+EXPORTS	+= \
+  MediaEngine.h \
+  MediaEngineDefault.h \
+  $(NULL)
+
+CPPSRCS	= \
+  MediaEngineDefault.cpp \
+  $(NULL)
+
+ifdef MOZ_WEBRTC
+EXPORTS += \
+  MediaEngineWebRTC.h \
+  $(NULL)
+
+CPPSRCS += \
+  MediaEngineWebRTC.cpp \
+  MediaEngineWebRTCVideo.cpp \
+  MediaEngineWebRTCAudio.cpp \
+  $(NULL)
+endif
+
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/config/rules.mk
+
+ifdef MOZ_WEBRTC
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/media/webrtc/trunk/src \
+  $(NULL)
+endif
rename from content/media/MediaEngine.h
rename to content/media/webrtc/MediaEngine.h
--- a/content/media/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -45,17 +45,17 @@ public:
 
   /* Populate the human readable name of this device in the nsAString */
   virtual void GetName(nsAString&) = 0;
 
   /* Populate the UUID of this device in the nsAString */
   virtual void GetUUID(nsAString&) = 0;
 
   /* This call reserves but does not start the device. */
-  virtual already_AddRefed<nsDOMMediaStream> Allocate() = 0;
+  virtual nsresult Allocate() = 0;
 
   /* Release the device back to the system. */
   virtual nsresult Deallocate() = 0;
 
   /* Start the device and add the track to the provided SourceMediaStream, with
    * the provided TrackID. You may start appending data to the track
    * immediately after. */
   virtual nsresult Start(SourceMediaStream*, TrackID) = 0;
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -0,0 +1,314 @@
+/* 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 "MediaEngineDefault.h"
+
+#include "nsCOMPtr.h"
+#include "nsDOMFile.h"
+#include "nsILocalFile.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "nsISupportsUtils.h"
+#endif
+
+#define WIDTH 320
+#define HEIGHT 240
+#define FPS 10
+#define CHANNELS 1
+#define RATE USECS_PER_S
+
+namespace mozilla {
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultVideoSource, nsITimerCallback)
+/**
+ * Default video source.
+ */
+void
+MediaEngineDefaultVideoSource::GetName(nsAString& aName)
+{
+  aName.Assign(NS_LITERAL_STRING("Default Video Device"));
+  return;
+}
+
+void
+MediaEngineDefaultVideoSource::GetUUID(nsAString& aUUID)
+{
+  aUUID.Assign(NS_LITERAL_STRING("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676"));
+  return;
+}
+
+nsresult
+MediaEngineDefaultVideoSource::Allocate()
+{
+  if (mState != kReleased) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = kAllocated;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultVideoSource::Deallocate()
+{
+  if (mState != kStopped && mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+  mState = kReleased;
+  return NS_OK;
+}
+
+MediaEngineVideoOptions
+MediaEngineDefaultVideoSource::GetOptions()
+{
+  MediaEngineVideoOptions aOpts;
+  aOpts.mWidth = WIDTH;
+  aOpts.mHeight = HEIGHT;
+  aOpts.mMaxFPS = FPS;
+  aOpts.codecType = kVideoCodecI420;
+  return aOpts;
+}
+
+nsresult
+MediaEngineDefaultVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
+{
+  if (mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  if (!mTimer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSource = aStream;
+
+  // Allocate a single blank Image
+  layers::Image::Format format = layers::Image::PLANAR_YCBCR;
+  mImageContainer = layers::LayerManager::CreateImageContainer();
+
+  nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&format, 1);
+
+  int len = ((WIDTH * HEIGHT) * 3 / 2);
+  mImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
+  PRUint8* frame = (PRUint8*) PR_Malloc(len);
+  memset(frame, 0x80, len); // Gray
+
+  const PRUint8 lumaBpp = 8;
+  const PRUint8 chromaBpp = 4;
+
+  layers::PlanarYCbCrImage::Data data;
+  data.mYChannel = frame;
+  data.mYSize = gfxIntSize(WIDTH, HEIGHT);
+  data.mYStride = WIDTH * lumaBpp / 8.0;
+  data.mCbCrStride = WIDTH * chromaBpp / 8.0;
+  data.mCbChannel = frame + HEIGHT * data.mYStride;
+  data.mCrChannel = data.mCbChannel + HEIGHT * data.mCbCrStride / 2;
+  data.mCbCrSize = gfxIntSize(WIDTH / 2, HEIGHT / 2);
+  data.mPicX = 0;
+  data.mPicY = 0;
+  data.mPicSize = gfxIntSize(WIDTH, HEIGHT);
+  data.mStereoMode = layers::STEREO_MODE_MONO;
+
+  // SetData copies data, so we can free the frame
+  mImage->SetData(data);
+  PR_Free(frame);
+
+  // AddTrack takes ownership of segment
+  VideoSegment *segment = new VideoSegment();
+  segment->AppendFrame(image.forget(), USECS_PER_S / FPS, gfxIntSize(WIDTH, HEIGHT));
+  mSource->AddTrack(aID, RATE, 0, segment);
+
+  // We aren't going to add any more tracks
+  mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+  // Remember TrackID so we can end it later
+  mTrackID = aID;
+
+  // Start timer for subsequent frames
+  mTimer->InitWithCallback(this, 1000 / FPS, nsITimer::TYPE_REPEATING_SLACK);
+  mState = kStarted;
+
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultVideoSource::Stop()
+{
+  if (mState != kStarted) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!mTimer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mTimer->Cancel();
+  mTimer = NULL;
+
+  mSource->EndTrack(mTrackID);
+  mSource->Finish();
+
+  mState = kStopped;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultVideoSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
+{
+  *aFile = nsnull;
+
+#ifndef MOZ_WIDGET_ANDROID
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
+  if (!AndroidBridge::Bridge()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoString filePath;
+  AndroidBridge::Bridge()->ShowFilePickerForMimeType(filePath, NS_LITERAL_STRING("image/*"));
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_NewLocalFile(filePath, false, getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ADDREF(*aFile = new nsDOMFileFile(file));
+  return NS_OK;
+#endif
+}
+
+NS_IMETHODIMP
+MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
+{
+  VideoSegment segment;
+
+  nsRefPtr<layers::PlanarYCbCrImage> image = mImage;
+  segment.AppendFrame(image.forget(), USECS_PER_S / FPS, gfxIntSize(WIDTH, HEIGHT));
+  mSource->AppendToTrack(mTrackID, &segment);
+
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultAudioSource, nsITimerCallback)
+/**
+ * Default audio source.
+ */
+void
+MediaEngineDefaultAudioSource::GetName(nsAString& aName)
+{
+  aName.Assign(NS_LITERAL_STRING("Default Audio Device"));
+  return;
+}
+
+void
+MediaEngineDefaultAudioSource::GetUUID(nsAString& aUUID)
+{
+  aUUID.Assign(NS_LITERAL_STRING("B7CBD7C1-53EF-42F9-8353-73F61C70C092"));
+  return;
+}
+
+nsresult
+MediaEngineDefaultAudioSource::Allocate()
+{
+  if (mState != kReleased) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = kAllocated;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultAudioSource::Deallocate()
+{
+  if (mState != kStopped && mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+  mState = kReleased;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
+{
+  if (mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  if (!mTimer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSource = aStream;
+
+  // AddTrack will take ownership of segment
+  AudioSegment* segment = new AudioSegment();
+  segment->Init(CHANNELS);
+  mSource->AddTrack(aID, RATE, 0, segment);
+
+  // We aren't going to add any more tracks
+  mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+  // Remember TrackID so we can finish later
+  mTrackID = aID;
+
+  // 1 Audio frame per Video frame
+  mTimer->InitWithCallback(this, 1000 / FPS, nsITimer::TYPE_REPEATING_SLACK);
+  mState = kStarted;
+
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultAudioSource::Stop()
+{
+  if (mState != kStarted) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!mTimer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mTimer->Cancel();
+  mTimer = NULL;
+
+  mSource->EndTrack(mTrackID);
+  mSource->Finish();
+
+  mState = kStopped;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineDefaultAudioSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
+{
+   return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
+{
+  AudioSegment segment;
+  segment.Init(CHANNELS);
+  segment.InsertNullDataAtStart(1);
+
+  mSource->AppendToTrack(mTrackID, &segment);
+
+  return NS_OK;
+}
+
+void
+MediaEngineDefault::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
+  aVSources->AppendElement(mVSource);
+  return;
+}
+
+void
+MediaEngineDefault::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
+  aASources->AppendElement(mASource);
+  return;
+}
+
+} // namespace mozilla
rename from content/media/MediaEngineDefault.h
rename to content/media/webrtc/MediaEngineDefault.h
--- a/content/media/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -40,17 +40,17 @@ class MediaEngineDefaultVideoSource : pu
 public:
   MediaEngineDefaultVideoSource() : mTimer(nsnull), mState(kReleased) {}
   ~MediaEngineDefaultVideoSource(){};
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual MediaEngineVideoOptions GetOptions();
-  virtual already_AddRefed<nsDOMMediaStream> Allocate();
+  virtual nsresult Allocate();
 
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop();
   virtual nsresult Snapshot(PRUint32 aDuration, nsIDOMFile** aFile);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
@@ -70,17 +70,17 @@ class MediaEngineDefaultAudioSource : pu
 {
 public:
   MediaEngineDefaultAudioSource() : mTimer(nsnull), mState(kReleased) {}
   ~MediaEngineDefaultAudioSource(){};
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
-  virtual already_AddRefed<nsDOMMediaStream> Allocate();
+  virtual nsresult Allocate();
 
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop();
   virtual nsresult Snapshot(PRUint32 aDuration, nsIDOMFile** aFile);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -0,0 +1,121 @@
+/* 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 "MediaEngineWebRTC.h"
+
+namespace mozilla {
+
+void
+MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
+{
+  webrtc::ViEBase* ptrViEBase;
+  webrtc::ViECapture* ptrViECapture;
+
+  if (!mVideoEngine) {
+    if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
+      return;
+    }
+  }
+
+  ptrViEBase = webrtc::ViEBase::GetInterface(mVideoEngine);
+  if (!ptrViEBase) {
+    return;
+  }
+
+  if (!mVideoEngineInit) {
+    if (ptrViEBase->Init() < 0) {
+      return;
+    }
+    mVideoEngineInit = true;
+  }
+
+  ptrViECapture = webrtc::ViECapture::GetInterface(mVideoEngine);
+  if (!ptrViECapture) {
+    return;
+  }
+
+  int num = ptrViECapture->NumberOfCaptureDevices();
+  if (num <= 0) {
+    return;
+  }
+
+  for (int i = 0; i < num; i++) {
+    nsRefPtr<MediaEngineVideoSource> vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i);
+    aVSources->AppendElement(vSource.forget());
+  }
+
+  ptrViEBase->Release();
+  ptrViECapture->Release();
+
+  return;
+}
+
+void
+MediaEngineWebRTC::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources)
+{
+  webrtc::VoEBase* ptrVoEBase = NULL;
+  webrtc::VoEHardware* ptrVoEHw = NULL;
+
+  if (!mVoiceEngine) {
+    mVoiceEngine = webrtc::VoiceEngine::Create();
+    if (!mVoiceEngine) {
+      return;
+    }
+  }
+
+  ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
+  if (!ptrVoEBase) {
+    return;
+  }
+
+  if (!mAudioEngineInit) {
+    if (ptrVoEBase->Init() < 0) {
+      return;
+    }
+    mAudioEngineInit = true;
+  }
+
+  ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
+  if (!ptrVoEHw)  {
+    return;
+  }
+
+  int nDevices = 0;
+  ptrVoEHw->GetNumOfRecordingDevices(nDevices);
+  for (int i = 0; i < nDevices; i++) {
+    // We use constants here because GetRecordingDeviceName takes char[128].
+    char deviceName[128];
+    memset(deviceName, 0, sizeof(deviceName));
+
+    char uniqueID[128];
+    memset(uniqueID, 0, sizeof(uniqueID));
+
+    ptrVoEHw->GetRecordingDeviceName(i, deviceName, uniqueID);
+    nsRefPtr<MediaEngineAudioSource> aSource = new MediaEngineWebRTCAudioSource(
+      mVoiceEngine, i, deviceName, uniqueID
+    );
+    aASources->AppendElement(aSource.forget());
+  }
+
+  ptrVoEHw->Release();
+  ptrVoEBase->Release();
+}
+
+
+void
+MediaEngineWebRTC::Shutdown()
+{
+  if (mVideoEngine) {
+    webrtc::VideoEngine::Delete(mVideoEngine);
+  }
+
+  if (mVoiceEngine) {
+    webrtc::VoiceEngine::Delete(mVoiceEngine);
+  }
+
+  mVideoEngine = NULL;
+  mVoiceEngine = NULL;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -0,0 +1,242 @@
+/* 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/. */
+
+#ifndef MEDIAENGINEWEBRTC_H_
+#define MEDIAENGINEWEBRTC_H_
+
+#include "prmem.h"
+#include "prcvar.h"
+#include "prthread.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+
+#include "nsCOMPtr.h"
+#include "nsDOMFile.h"
+#include "nsThreadUtils.h"
+#include "nsDOMMediaStream.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsComponentManagerUtils.h"
+
+#include "Layers.h"
+#include "VideoUtils.h"
+#include "MediaEngine.h"
+#include "ImageLayers.h"
+#include "VideoSegment.h"
+#include "AudioSegment.h"
+#include "StreamBuffer.h"
+#include "MediaStreamGraph.h"
+
+// WebRTC library includes follow
+
+// Audio Engine
+#include "voice_engine/main/interface/voe_base.h"
+#include "voice_engine/main/interface/voe_codec.h"
+#include "voice_engine/main/interface/voe_hardware.h"
+#include "voice_engine/main/interface/voe_audio_processing.h"
+#include "voice_engine/main/interface/voe_volume_control.h"
+#include "voice_engine/main/interface/voe_external_media.h"
+
+// Video Engine
+#include "video_engine/include/vie_base.h"
+#include "video_engine/include/vie_codec.h"
+#include "video_engine/include/vie_render.h"
+#include "video_engine/include/vie_capture.h"
+#include "video_engine/include/vie_file.h"
+
+
+namespace mozilla {
+
+/**
+ * The WebRTC implementation of the MediaEngine interface.
+ */
+
+enum WebRTCEngineState {
+  kAllocated,
+  kStarted,
+  kStopped,
+  kReleased
+};
+
+class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource,
+                                     public webrtc::ExternalRenderer,
+                                     public nsRunnable
+{
+public:
+  // ViEExternalRenderer.
+  virtual int FrameSizeChange(unsigned int, unsigned int, unsigned int);
+  virtual int DeliverFrame(unsigned char*, int, uint32_t, int64_t);
+
+  MediaEngineWebRTCVideoSource(webrtc::VideoEngine* videoEnginePtr,
+    int index, int aFps = 30)
+    : mVideoEngine(videoEnginePtr)
+    , mCapIndex(index)
+    , mWidth(640)
+    , mHeight(480)
+    , mState(kReleased)
+    , mMonitor("WebRTCCamera.Monitor")
+    , mFps(aFps)
+    , mInitDone(false)
+    , mInSnapshotMode(false)
+    , mSnapshotPath(NULL) { Init(); }
+
+  ~MediaEngineWebRTCVideoSource() { Shutdown(); }
+
+  virtual void GetName(nsAString&);
+  virtual void GetUUID(nsAString&);
+  virtual MediaEngineVideoOptions GetOptions();
+  virtual nsresult Allocate();
+  virtual nsresult Deallocate();
+  virtual nsresult Start(SourceMediaStream*, TrackID);
+  virtual nsresult Stop();
+  virtual nsresult Snapshot(PRUint32 aDuration, nsIDOMFile** aFile);
+
+  NS_DECL_ISUPPORTS
+
+  // This runnable is for creating a temporary file on the main thread.
+  NS_IMETHODIMP
+  Run()
+  {
+    nsCOMPtr<nsIFile> tmp;
+    nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmp));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    tmp->Append(NS_LITERAL_STRING("webrtc_snapshot.jpeg"));
+    rv = tmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mSnapshotPath = new nsString();
+    rv = tmp->GetPath(*mSnapshotPath);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+private:
+  static const unsigned int KMaxDeviceNameLength;
+  static const unsigned int KMaxUniqueIdLength;
+
+  // Initialize the needed Video engine interfaces.
+  void Init();
+  void Shutdown();
+
+  // Engine variables.
+
+  webrtc::VideoEngine* mVideoEngine; // Weak reference, don't free.
+  webrtc::ViEBase* mViEBase;
+  webrtc::ViECapture* mViECapture;
+  webrtc::ViERender* mViERender;
+  webrtc::CaptureCapability mCaps; // Doesn't work on OS X.
+
+  int mCapIndex;
+  int mWidth, mHeight;
+  TrackID mTrackID;
+
+  WebRTCEngineState mState;
+  mozilla::ReentrantMonitor mMonitor; // Monitor for processing WebRTC frames.
+  SourceMediaStream* mSource;
+
+  int mFps; // Track rate (30 fps by default)
+  bool mInitDone;
+  bool mInSnapshotMode;
+  nsString* mSnapshotPath;
+
+  nsRefPtr<layers::ImageContainer> mImageContainer;
+
+  PRLock* mSnapshotLock;
+  PRCondVar* mSnapshotCondVar;
+
+};
+
+class MediaEngineWebRTCAudioSource : public MediaEngineAudioSource,
+                                     public webrtc::VoEMediaProcess
+{
+public:
+  MediaEngineWebRTCAudioSource(webrtc::VoiceEngine* voiceEngine, int aIndex,
+    char* name, char* uuid)
+    : mVoiceEngine(voiceEngine)
+    , mMonitor("WebRTCMic.Monitor")
+    , mCapIndex(aIndex)
+    , mChannel(-1)
+    , mInitDone(false)
+    , mState(kReleased) {
+
+    mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
+    mDeviceName.Assign(NS_ConvertASCIItoUTF16(name));
+    mDeviceUUID.Assign(NS_ConvertASCIItoUTF16(uuid));
+    mInitDone = true;
+  }
+
+  ~MediaEngineWebRTCAudioSource() { Shutdown(); }
+
+  virtual void GetName(nsAString&);
+  virtual void GetUUID(nsAString&);
+
+  virtual nsresult Allocate();
+  virtual nsresult Deallocate();
+  virtual nsresult Start(SourceMediaStream*, TrackID);
+  virtual nsresult Stop();
+  virtual nsresult Snapshot(PRUint32 aDuration, nsIDOMFile** aFile);
+
+  // VoEMediaProcess.
+  void Process(const int channel, const webrtc::ProcessingTypes type,
+               WebRtc_Word16 audio10ms[], const int length,
+               const int samplingFreq, const bool isStereo);
+
+  NS_DECL_ISUPPORTS
+
+private:
+  static const unsigned int KMaxDeviceNameLength;
+  static const unsigned int KMaxUniqueIdLength;
+
+  void Init();
+  void Shutdown();
+
+  webrtc::VoiceEngine* mVoiceEngine;
+  webrtc::VoEBase* mVoEBase;
+  webrtc::VoEExternalMedia* mVoERender;
+
+  mozilla::ReentrantMonitor mMonitor;
+
+  int mCapIndex;
+  int mChannel;
+  TrackID mTrackID;
+  bool mInitDone;
+  WebRTCEngineState mState;
+
+  nsString mDeviceName;
+  nsString mDeviceUUID;
+
+  SourceMediaStream* mSource;
+};
+
+class MediaEngineWebRTC : public MediaEngine
+{
+public:
+  MediaEngineWebRTC()
+  : mVideoEngine(NULL)
+  , mVoiceEngine(NULL)
+  , mVideoEngineInit(false)
+  , mAudioEngineInit(false) {}
+
+  ~MediaEngineWebRTC() { Shutdown(); }
+
+  // Clients should ensure to clean-up sources video/audio sources
+  // before invoking Shutdown on this class.
+  void Shutdown();
+
+  virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
+  virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
+
+private:
+  webrtc::VideoEngine* mVideoEngine;
+  webrtc::VoiceEngine* mVoiceEngine;
+
+  // Need this to avoid unneccesary WebRTC calls while enumerating.
+  bool mVideoEngineInit;
+  bool mAudioEngineInit;
+};
+
+}
+
+#endif /* NSMEDIAENGINEWEBRTC_H_ */
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -0,0 +1,224 @@
+/* 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 "MediaEngineWebRTC.h"
+
+#define CHANNELS 1
+#define ENCODING "L16"
+#define DEFAULT_PORT 5555
+
+#define SAMPLE_RATE 256000
+#define SAMPLE_FREQUENCY 16000
+#define SAMPLE_LENGTH ((SAMPLE_FREQUENCY*10)/1000)
+
+namespace mozilla {
+
+/**
+ * Webrtc audio source.
+ */
+NS_IMPL_THREADSAFE_ISUPPORTS0(MediaEngineWebRTCAudioSource)
+
+void
+MediaEngineWebRTCAudioSource::GetName(nsAString& aName)
+{
+  if (mInitDone) {
+    aName.Assign(mDeviceName);
+  }
+
+  return;
+}
+
+void
+MediaEngineWebRTCAudioSource::GetUUID(nsAString& aUUID)
+{
+  if (mInitDone) {
+    aUUID.Assign(mDeviceUUID);
+  }
+
+  return;
+}
+
+nsresult
+MediaEngineWebRTCAudioSource::Allocate()
+{
+  if (mState != kReleased) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mVoEBase->Init();
+
+  mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
+  if (!mVoERender) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mChannel = mVoEBase->CreateChannel();
+  if (mChannel < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Check for availability.
+  webrtc::VoEHardware* ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
+  if (ptrVoEHw->SetRecordingDevice(mCapIndex)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool avail = false;
+  ptrVoEHw->GetRecordingDeviceStatus(avail);
+  if (!avail) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Set "codec" to PCM, 32kHz on 1 channel
+  webrtc::VoECodec* ptrVoECodec;
+  webrtc::CodecInst codec;
+  ptrVoECodec = webrtc::VoECodec::GetInterface(mVoiceEngine);
+  if (!ptrVoECodec) {
+    return NS_ERROR_FAILURE;
+  }
+
+  strcpy(codec.plname, ENCODING);
+  codec.channels = CHANNELS;
+  codec.rate = SAMPLE_RATE;
+  codec.plfreq = SAMPLE_FREQUENCY;
+  codec.pacsize = SAMPLE_LENGTH;
+  codec.pltype = 0; // Default payload type
+
+  if (ptrVoECodec->SetSendCodec(mChannel, codec)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Audio doesn't play through unless we set a receiver and destination, so
+  // we setup a dummy local destination, and do a loopback.
+  mVoEBase->SetLocalReceiver(mChannel, DEFAULT_PORT);
+  mVoEBase->SetSendDestination(mChannel, DEFAULT_PORT, "127.0.0.1");
+
+  mState = kAllocated;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCAudioSource::Deallocate()
+{
+  if (mState != kStopped && mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mVoEBase->Terminate();
+  mVoERender->Release();
+
+  mState = kReleased;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
+{
+  if (!mInitDone || mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!aStream) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSource = aStream;
+
+  AudioSegment* segment = new AudioSegment();
+  segment->Init(CHANNELS);
+  mSource->AddTrack(aID, SAMPLE_FREQUENCY, 0, segment);
+  mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+  mTrackID = aID;
+
+  if (mVoEBase->StartReceive(mChannel)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (mVoEBase->StartSend(mChannel)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Attach external media processor, so this::Process will be called.
+  mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
+
+  mState = kStarted;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCAudioSource::Stop()
+{
+  if (mState != kStarted) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!mVoEBase) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel);
+
+  if (mVoEBase->StopSend(mChannel)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (mVoEBase->StopReceive(mChannel)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = kStopped;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCAudioSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
+{
+   return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+void
+MediaEngineWebRTCAudioSource::Shutdown()
+{
+  if (!mInitDone) {
+    return;
+  }
+
+  if (mState == kStarted) {
+    Stop();
+  }
+
+  if (mState == kAllocated) {
+    Deallocate();
+  }
+
+  mVoEBase->Release();
+
+  mState = kReleased;
+  mInitDone = false;
+}
+
+typedef WebRtc_Word16 sample;
+
+void
+MediaEngineWebRTCAudioSource::Process(const int channel,
+  const webrtc::ProcessingTypes type, sample* audio10ms,
+  const int length, const int samplingFreq, const bool isStereo)
+{
+  ReentrantMonitorAutoEnter enter(mMonitor);
+
+  nsRefPtr<SharedBuffer> buffer = SharedBuffer::Create(length * sizeof(sample));
+
+  sample* dest = static_cast<sample*>(buffer->Data());
+  for (int i = 0; i < length; i++) {
+    dest[i] = audio10ms[i];
+  }
+
+  AudioSegment segment;
+  segment.Init(CHANNELS);
+  segment.AppendFrames(
+    buffer.forget(), length, 0, length, nsAudioStream::FORMAT_S16_LE
+  );
+  mSource->AppendToTrack(mTrackID, &segment);
+
+  return;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -0,0 +1,370 @@
+/* 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 "MediaEngineWebRTC.h"
+
+namespace mozilla {
+
+/**
+ * Webrtc video source.
+ */
+NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineWebRTCVideoSource, nsIRunnable)
+
+// Static variables to hold device names and UUIDs.
+const unsigned int MediaEngineWebRTCVideoSource::KMaxDeviceNameLength = 128;
+const unsigned int MediaEngineWebRTCVideoSource::KMaxUniqueIdLength = 256;
+
+// ViEExternalRenderer Callback.
+int
+MediaEngineWebRTCVideoSource::FrameSizeChange(
+   unsigned int w, unsigned int h, unsigned int streams)
+{
+  mWidth = w;
+  mHeight = h;
+  return 0;
+}
+
+// ViEExternalRenderer Callback. Process every incoming frame here.
+int
+MediaEngineWebRTCVideoSource::DeliverFrame(
+   unsigned char* buffer, int size, uint32_t time_stamp, int64_t render_time)
+{
+  ReentrantMonitorAutoEnter enter(mMonitor);
+
+  if (mInSnapshotMode) {
+    // Set the condition variable to false and notify Snapshot().
+    PR_Lock(mSnapshotLock);
+    mInSnapshotMode = false;
+    PR_NotifyCondVar(mSnapshotCondVar);
+    PR_Unlock(mSnapshotLock);
+    return 0;
+  }
+
+  // Check for proper state.
+  if (mState != kStarted) {
+    return 0;
+  }
+
+  // Create a video frame and append it to the track.
+  layers::Image::Format format = layers::Image::PLANAR_YCBCR;
+  nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&format, 1);
+
+  layers::PlanarYCbCrImage* videoImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
+
+  PRUint8* frame = static_cast<PRUint8*> (buffer);
+  const PRUint8 lumaBpp = 8;
+  const PRUint8 chromaBpp = 4;
+
+  layers::PlanarYCbCrImage::Data data;
+  data.mYChannel = frame;
+  data.mYSize = gfxIntSize(mWidth, mHeight);
+  data.mYStride = mWidth * lumaBpp/ 8;
+  data.mCbCrStride = mWidth * chromaBpp / 8;
+  data.mCbChannel = frame + mHeight * data.mYStride;
+  data.mCrChannel = data.mCbChannel + mHeight * data.mCbCrStride / 2;
+  data.mCbCrSize = gfxIntSize(mWidth/ 2, mHeight/ 2);
+  data.mPicX = 0;
+  data.mPicY = 0;
+  data.mPicSize = gfxIntSize(mWidth, mHeight);
+  data.mStereoMode = layers::STEREO_MODE_MONO;
+
+  videoImage->SetData(data);
+
+  VideoSegment segment;
+  segment.AppendFrame(image.forget(), 1, gfxIntSize(mWidth, mHeight));
+  mSource->AppendToTrack(mTrackID, &(segment));
+  return 0;
+}
+
+void
+MediaEngineWebRTCVideoSource::GetName(nsAString& aName)
+{
+  char deviceName[KMaxDeviceNameLength];
+  memset(deviceName, 0, KMaxDeviceNameLength);
+
+  char uniqueId[KMaxUniqueIdLength];
+  memset(uniqueId, 0, KMaxUniqueIdLength);
+
+  if (mInitDone) {
+    mViECapture->GetCaptureDevice(
+      mCapIndex, deviceName, KMaxDeviceNameLength, uniqueId, KMaxUniqueIdLength
+    );
+    aName.Assign(NS_ConvertASCIItoUTF16(deviceName));
+  }
+}
+
+void
+MediaEngineWebRTCVideoSource::GetUUID(nsAString& aUUID)
+{
+  char deviceName[KMaxDeviceNameLength];
+  memset(deviceName, 0, KMaxDeviceNameLength);
+
+  char uniqueId[KMaxUniqueIdLength];
+  memset(uniqueId, 0, KMaxUniqueIdLength);
+
+  if (mInitDone) {
+    mViECapture->GetCaptureDevice(
+      mCapIndex, deviceName, KMaxDeviceNameLength, uniqueId, KMaxUniqueIdLength
+    );
+    aUUID.Assign(NS_ConvertASCIItoUTF16(uniqueId));
+  }
+}
+
+nsresult
+MediaEngineWebRTCVideoSource::Allocate()
+{
+  if (mState != kReleased) {
+    return NS_ERROR_FAILURE;
+  }
+
+  char deviceName[KMaxDeviceNameLength];
+  memset(deviceName, 0, KMaxDeviceNameLength);
+
+  char uniqueId[KMaxUniqueIdLength];
+  memset(uniqueId, 0, KMaxUniqueIdLength);
+
+  mViECapture->GetCaptureDevice(
+    mCapIndex, deviceName, KMaxDeviceNameLength, uniqueId, KMaxUniqueIdLength
+  );
+
+  if (mViECapture->AllocateCaptureDevice(uniqueId, KMaxUniqueIdLength, mCapIndex)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mViECapture->StartCapture(mCapIndex) < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = kAllocated;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCVideoSource::Deallocate()
+{
+  if (mState != kStopped && mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mViECapture->StopCapture(mCapIndex);
+  mViECapture->ReleaseCaptureDevice(mCapIndex);
+  mState = kReleased;
+  return NS_OK;
+}
+
+MediaEngineVideoOptions
+MediaEngineWebRTCVideoSource::GetOptions()
+{
+  MediaEngineVideoOptions aOpts;
+  aOpts.mWidth = mWidth;
+  aOpts.mHeight = mHeight;
+  aOpts.mMaxFPS = mFps;
+  aOpts.codecType = kVideoCodecI420;
+  return aOpts;
+}
+
+nsresult
+MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
+{
+  int error = 0;
+  if (!mInitDone || mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!aStream) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mState == kStarted) {
+    return NS_OK;
+  }
+
+  mSource = aStream;
+  mTrackID = aID;
+
+  mImageContainer = layers::LayerManager::CreateImageContainer();
+  mSource->AddTrack(aID, mFps, 0, new VideoSegment());
+  mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+  error = mViERender->AddRenderer(mCapIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
+  if (error == -1) {
+    return NS_ERROR_FAILURE;
+  }
+
+  error = mViERender->StartRender(mCapIndex);
+  if (error == -1) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = kStarted;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCVideoSource::Stop()
+{
+  if (mState != kStarted) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSource->EndTrack(mTrackID);
+  mSource->Finish();
+
+  mViERender->StopRender(mCapIndex);
+  mViERender->RemoveRenderer(mCapIndex);
+
+  mState = kStopped;
+  return NS_OK;
+}
+
+nsresult
+MediaEngineWebRTCVideoSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
+{
+  /**
+   * To get a Snapshot we do the following:
+   * - Set a condition variable (mInSnapshotMode) to true
+   * - Attach the external renderer and start the camera
+   * - Wait for the condition variable to change to false
+   *
+   * Starting the camera has the effect of invoking DeliverFrame() when
+   * the first frame arrives from the camera. We only need one frame for
+   * GetCaptureDeviceSnapshot to work, so we immediately set the condition
+   * variable to false and notify this method.
+   *
+   * This causes the current thread to continue (PR_CondWaitVar will return),
+   * at which point we can grab a snapshot, convert it to a file and
+   * return from this function after cleaning up the temporary stream object
+   * and caling Stop() on the media source.
+   */
+  *aFile = nsnull;
+  if (!mInitDone || mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSnapshotLock = PR_NewLock();
+  mSnapshotCondVar = PR_NewCondVar(mSnapshotLock);
+
+  PR_Lock(mSnapshotLock);
+  mInSnapshotMode = true;
+
+  // Start the rendering (equivalent to calling Start(), but without a track).
+  int error = 0;
+  if (!mInitDone || mState != kAllocated) {
+    return NS_ERROR_FAILURE;
+  }
+  error = mViERender->AddRenderer(mCapIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
+  if (error == -1) {
+    return NS_ERROR_FAILURE;
+  }
+  error = mViERender->StartRender(mCapIndex);
+  if (error == -1) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Wait for the condition variable, will be set in DeliverFrame.
+  // We use a while loop, because even if PR_WaitCondVar returns, it's not
+  // guaranteed that the condition variable changed.
+  while (mInSnapshotMode) {
+    PR_WaitCondVar(mSnapshotCondVar, PR_INTERVAL_NO_TIMEOUT);
+  }
+
+  // If we get here, DeliverFrame received at least one frame.
+  PR_Unlock(mSnapshotLock);
+  PR_DestroyCondVar(mSnapshotCondVar);
+  PR_DestroyLock(mSnapshotLock);
+
+  webrtc::ViEFile* vieFile = webrtc::ViEFile::GetInterface(mVideoEngine);
+  if (!vieFile) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create a temporary file on the main thread and put the snapshot in it.
+  // See Run() in MediaEngineWebRTCVideo.h (sets mSnapshotPath).
+  NS_DispatchToMainThread(this, NS_DISPATCH_SYNC);
+
+  if (!mSnapshotPath) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const char* path = NS_ConvertUTF16toUTF8(*mSnapshotPath).get();
+  if (vieFile->GetCaptureDeviceSnapshot(mCapIndex, path) < 0) {
+    delete mSnapshotPath;
+    mSnapshotPath = NULL;
+    return NS_ERROR_FAILURE;
+  }
+
+  // Stop the camera.
+  mViERender->StopRender(mCapIndex);
+  mViERender->RemoveRenderer(mCapIndex);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_NewLocalFile(*mSnapshotPath, false, getter_AddRefs(file));
+
+  delete mSnapshotPath;
+  mSnapshotPath = NULL;
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ADDREF(*aFile = new nsDOMFileFile(file));
+
+  return NS_OK;
+}
+
+/**
+ * Initialization and Shutdown functions for the video source, called by the
+ * constructor and destructor respectively.
+ */
+
+void
+MediaEngineWebRTCVideoSource::Init()
+{
+  if (mVideoEngine == NULL) {
+    return;
+  }
+
+  mViEBase = webrtc::ViEBase::GetInterface(mVideoEngine);
+  if (mViEBase == NULL) {
+    return;
+  }
+
+  // Get interfaces for capture, render for now
+  mViECapture = webrtc::ViECapture::GetInterface(mVideoEngine);
+  mViERender = webrtc::ViERender::GetInterface(mVideoEngine);
+
+  if (mViECapture == NULL || mViERender == NULL) {
+    return;
+  }
+
+  mInitDone = true;
+}
+
+void
+MediaEngineWebRTCVideoSource::Shutdown()
+{
+  bool continueShutdown = false;
+
+  if (!mInitDone) {
+    return;
+  }
+
+  if (mState == kStarted) {
+    mViERender->StopRender(mCapIndex);
+    mViERender->RemoveRenderer(mCapIndex);
+    continueShutdown = true;
+  }
+
+  if (mState == kAllocated || continueShutdown) {
+    mViECapture->StopCapture(mCapIndex);
+    mViECapture->ReleaseCaptureDevice(mCapIndex);
+    continueShutdown = false;
+  }
+
+  mViECapture->Release();
+  mViERender->Release();
+  mViEBase->Release();
+  mState = kReleased;
+  mInitDone = false;
+}
+
+}
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -46,27 +46,27 @@ function convertAppsArray(aApps, aWindow
 
 function WebappsRegistry() {
 }
 
 WebappsRegistry.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
   __exposedProps__: {
                       install: 'r',
+                      installPackage: 'r',
                       getSelf: 'r',
                       getInstalled: 'r',
                       getNotInstalled: 'r',
                       mgmt: 'r'
                      },
 
   /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
    * only the name property is mandatory
    */
   checkManifest: function(aManifest, aInstallOrigin) {
-    // TODO : check for install_allowed_from
     if (aManifest.name == undefined)
       return false;
 
     if (aManifest.installs_allowed_from) {
       return aManifest.installs_allowed_from.some(function(aOrigin) {
         return aOrigin == "*" || aOrigin == aInstallOrigin;
       });
     }
@@ -82,17 +82,17 @@ WebappsRegistry.prototype = {
       return;
     let app = msg.app;
     switch (aMessage.name) {
       case "Webapps:Install:Return:OK":
         Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
                                                                      app.installOrigin, app.installTime));
         break;
       case "Webapps:Install:Return:KO":
-        Services.DOMRequest.fireError(req, "DENIED");
+        Services.DOMRequest.fireError(req, msg.error || "DENIED");
         break;
       case "Webapps:GetSelf:Return:OK":
         if (msg.apps.length) {
           app = msg.apps[0];
           Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
                                                                        app.installOrigin, app.installTime));
         } else {
           Services.DOMRequest.fireSuccess(req, null);
@@ -179,16 +179,31 @@ WebappsRegistry.prototype = {
   getNotInstalled: function() {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:GetNotInstalled", { origin: this._getOrigin(this._window.location.href),
                                                        oid: this._id,
                                                        requestID: this.getRequestId(request) });
     return request;
   },
 
+  installPackage: function(aPackageURL, aParams) {
+    let request = this.createRequest();
+    let requestID = this.getRequestId(request);
+
+    let receipts = (aParams && aParams.receipts &&
+                    Array.isArray(aParams.receipts)) ? aParams.receipts : [];
+    cpmm.sendAsyncMessage("Webapps:InstallPackage", { url: aPackageURL,
+                                                      receipts: receipts,
+                                                      requestID: requestID,
+                                                      oid: this._id,
+                                                      from: this._window.location.href,
+                                                      installOrigin: this._getOrigin(this._window.location.href) });
+    return request;
+  },
+
   get mgmt() {
     if (!this._mgmt)
       this._mgmt = new WebappsApplicationMgmt(this._window);
     return this._mgmt;
   },
 
   uninit: function() {
     this._mgmt = null;
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -2,36 +2,39 @@
  * 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/. */
 
 "use strict";
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cr = Components.results;
 
 let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org";
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
-  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"]
+         .getService(Ci.nsIFrameMessageManager);
 });
 
 XPCOMUtils.defineLazyGetter(this, "msgmgr", function() {
-  return Cc["@mozilla.org/system-message-internal;1"].getService(Ci.nsISystemMessagesInternal);
+  return Cc["@mozilla.org/system-message-internal;1"]
+         .getService(Ci.nsISystemMessagesInternal);
 });
 
 #ifdef MOZ_WIDGET_GONK
   const DIRECTORY_NAME = "webappsDir";
 #else
   // If we're executing in the context of the webapp runtime, the data files
   // are in a different directory (currently the Firefox profile that installed
   // the webapp); otherwise, they're in the current profile.
@@ -42,25 +45,27 @@ let DOMApplicationRegistry = {
   appsFile: null,
   webapps: { },
   allAppsLaunchable: false,
 
   init: function() {
     this.messages = ["Webapps:Install", "Webapps:Uninstall",
                     "Webapps:GetSelf",
                     "Webapps:GetInstalled", "Webapps:GetNotInstalled",
-                    "Webapps:Launch", "Webapps:GetAll"];
+                    "Webapps:Launch", "Webapps:GetAll",
+                    "Webapps:InstallPackage", "Webapps:GetBasePath"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
 
-    this.appsFile = FileUtils.getFile(DIRECTORY_NAME, ["webapps", "webapps.json"], true);
+    this.appsFile = FileUtils.getFile(DIRECTORY_NAME,
+                                      ["webapps", "webapps.json"], true);
 
     if (this.appsFile.exists()) {
       this._loadJSONAsync(this.appsFile, (function(aData) {
         this.webapps = aData;
         for (let id in this.webapps) {
 #ifdef MOZ_SYS_MSG
           this._registerSystemMessagesForId(id);
 #endif
@@ -69,25 +74,27 @@ let DOMApplicationRegistry = {
           }
         };
       }).bind(this));
     }
 
     try {
       let hosts = Services.prefs.getCharPref("dom.mozApps.whitelist");
       hosts.split(",").forEach(function(aHost) {
-        Services.perms.add(Services.io.newURI(aHost, null, null), "webapps-manage",
+        Services.perms.add(Services.io.newURI(aHost, null, null),
+                           "webapps-manage",
                            Ci.nsIPermissionManager.ALLOW_ACTION);
       });
     } catch(e) { }
   },
 
 #ifdef MOZ_SYS_MSG
   _registerSystemMessages: function(aManifest, aApp) {
-    if (aManifest.messages && Array.isArray(aManifest.messages) && aManifest.messages.length > 0) {
+    if (aManifest.messages && Array.isArray(aManifest.messages) &&
+        aManifest.messages.length > 0) {
       let manifest = new DOMApplicationManifest(aManifest, aApp.origin);
       let launchPath = Services.io.newURI(manifest.fullLaunchPath(), null, null);
       let manifestURL = Services.io.newURI(aApp.manifestURL, null, null);
       aManifest.messages.forEach(function registerPages(aMessage) {
         msgmgr.registerPage(aMessage, launchPath, manifestURL);
       });
     }
   },
@@ -111,37 +118,40 @@ let DOMApplicationRegistry = {
   },
 
   _loadJSONAsync: function(aFile, aCallback) {
     try {
       let channel = NetUtil.newChannel(aFile);
       channel.contentType = "application/json";
       NetUtil.asyncFetch(channel, function(aStream, aResult) {
         if (!Components.isSuccessCode(aResult)) {
-          Cu.reportError("DOMApplicationRegistry: Could not read from json file " + aFile.path);
+          Cu.reportError("DOMApplicationRegistry: Could not read from json file "
+                         + aFile.path);
           if (aCallback)
             aCallback(null);
           return;
         }
 
         // Read json file into a string
         let data = null;
         try {
-          data = JSON.parse(NetUtil.readInputStreamToString(aStream, aStream.available()) || "");
+          data = JSON.parse(NetUtil.readInputStreamToString(aStream,
+                                                            aStream.available()) || "");
           aStream.close();
           if (aCallback)
             aCallback(data);
         } catch (ex) {
           Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " + ex);
           if (aCallback)
             aCallback(null);
         }
       });
     } catch (ex) {
-      Cu.reportError("DOMApplicationRegistry: Could not read from " + aFile.path + " : " + ex);
+      Cu.reportError("DOMApplicationRegistry: Could not read from " +
+                     aFile.path + " : " + ex);
       if (aCallback)
         aCallback(null);
     }
   },
 
   receiveMessage: function(aMessage) {
     // nsIPrefBranch throws if pref does not exist, faster to simply write
     // the pref instead of first checking if it is false.
@@ -170,25 +180,32 @@ let DOMApplicationRegistry = {
         this.getNotInstalled(msg);
         break;
       case "Webapps:GetAll":
         if (msg.hasPrivileges)
           this.getAll(msg);
         else
           ppmm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg);
         break;
+      case "Webapps:InstallPackage":
+        this.installPackage(msg);
+        break;
+      case "Webapps:GetBasePath":
+        return FileUtils.getFile(DIRECTORY_NAME, ["webapps"], true).path;
+        break;
     }
   },
 
   _writeFile: function ss_writeFile(aFile, aData, aCallbak) {
     // Initialize the file output stream.
     let ostream = FileUtils.openSafeFileOutputStream(aFile);
 
     // Obtain a converter to convert our data to a UTF-8 encoded input stream.
-    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                    .createInstance(Ci.nsIScriptableUnicodeConverter);
     converter.charset = "UTF-8";
 
     // Asynchronously copy the data to the file.
     let istream = converter.convertToInputStream(aData);
     NetUtil.asyncCopy(istream, ostream, function(rc) {
       if (aCallbak)
         aCallbak();
     });
@@ -204,47 +221,75 @@ let DOMApplicationRegistry = {
       manifestURL: aApp.manifestURL,
       progress: aApp.progress || 0.0,
       status: aApp.status || "installed"
     };
     return clone;
   },
 
   denyInstall: function(aData) {
+    let packageId = aData.app.packageId;
+    if (packageId) {
+      let dir = FileUtils.getDir("TmpD", ["webapps", packageId],
+                                 true, true);
+      try {
+        dir.remove(true);
+      } catch(e) {
+      }
+    }
     ppmm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
   },
 
   confirmInstall: function(aData, aFromSync, aProfileDir, aOfflineCacheObserver) {
     let app = aData.app;
     let id = app.syncId || this._appId(app.origin);
     let localId = this.getAppLocalIdByManifestURL(app.manifestURL);
 
-    // install an application again is considered as an update
+    // Installing an application again is considered as an update.
     if (id) {
       let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       try {
         dir.remove(true);
       } catch(e) {
       }
     } else {
       id = this.makeAppId();
       localId = this._nextLocalId();
     }
 
+    if (app.packageId) {
+      // Override the origin with the correct id.
+      app.origin = "app://" + id;
+    }
+
     let appObject = this._cloneAppObject(app);
     appObject.installTime = app.installTime = Date.now();
     let appNote = JSON.stringify(appObject);
     appNote.id = id;
 
     appObject.localId = localId;
 
     let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
     let manFile = dir.clone();
     manFile.append("manifest.webapp");
-    this._writeFile(manFile, JSON.stringify(app.manifest));
+    this._writeFile(manFile, JSON.stringify(app.manifest), function() {
+      // If this a packaged app, move the zip file from the temp directory,
+      // and delete the temp directory.
+      if (app.packageId) {
+        let appFile = FileUtils.getFile("TmpD", ["webapps", app.packageId, "application.zip"], 
+                                        true, true);
+        appFile.moveTo(dir, "application.zip");
+        let tmpDir = FileUtils.getDir("TmpD", ["webapps", app.packageId], 
+                                        true, true);
+        try {
+          tmpDir.remove(true);
+        } catch(e) {
+        }
+      }
+    });
     this.webapps[id] = appObject;
 
     appObject.status = "installed";
     
     let manifest = new DOMApplicationManifest(app.manifest, app.origin);
 
     if (!aFromSync)
       this._saveApps((function() {
@@ -324,16 +369,126 @@ let DOMApplicationRegistry = {
       aData[index].manifest = aJSON;
       if (index == aData.length - 1)
         aFinalCallback(aData);
       else
         this._readManifests(aData, aFinalCallback, index + 1);
     }).bind(this));
   },
 
+  installPackage: function(aData) {
+    // Here are the steps when installing a package:
+    // - create a temp directory where to store the app.
+    // - download the zip in this directory.
+    // - extract the manifest from the zip and check it.
+    // - ask confirmation to the user.
+    // - add the new app to the registry.
+    // If we fail at any step, we backout the previous ones and return an error.
+
+    let id;
+    let manifestURL = "jar:" + aData.url + "!manifest.webapp";
+    // Check if we reinstall a known application.
+    for (let appId in this.webapps) {
+      if (this.webapps[appId].manifestURL == manifestURL) {
+        id = appId;
+      }
+    }
+
+    // New application.
+    if (!id) {
+      id = this.makeAppId();
+    }
+
+    let dir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
+
+    /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
+     * only the name property is mandatory
+     */
+    function checkManifest(aManifest) {
+      if (aManifest.name == undefined)
+        return false;
+
+      if (aManifest.installs_allowed_from) {
+        return aManifest.installs_allowed_from.some(function(aOrigin) {
+          return aOrigin == "*" || aOrigin == aData.installOrigin;
+        });
+      }
+      return true;
+    }
+
+    // Removes the directory we created, and sends an error to the DOM side.
+    function cleanup(aError) {
+      try {
+        dir.remove(true);
+      } catch (e) { }
+      ppmm.sendAsyncMessage("Webapps:Install:Return:KO",
+                            { oid: aData.oid,
+                              requestID: aData.requestID,
+                              error: aError });
+    }
+
+    NetUtil.asyncFetch(aData.url, function(aInput, aResult, aRequest) {
+      if (!Components.isSuccessCode(aResult)) {
+        // We failed to fetch the zip.
+        cleanup("NETWORK_ERROR");
+        return;
+      }
+      // Copy the zip on disk.
+      let zipFile = FileUtils.getFile("TmpD",
+                                      ["webapps", id, "application.zip"], true);
+      let ostream = FileUtils.openSafeFileOutputStream(zipFile);
+      NetUtil.asyncCopy(aInput, ostream, function (aResult) {
+        if (!Components.isSuccessCode(aResult)) {
+          // We failed to save the zip.
+          cleanup("DOWNLOAD_ERROR");
+          return;
+        }
+        // Build a data structure to call the webapps confirmation dialog :
+        // - load the manifest from the zip
+        // - set data.app.(origin, install_origin, manifestURL, manifest, receipts)
+        // - call notifyObservers(this, "webapps-ask-install", JSON.stringify(msg));
+        let msg = {
+          from: aData.from,
+          oid: aData.oid,
+          requestId: aData.requestId,
+          app: {
+            packageId: id,
+            installOrigin: aData.installOrigin,
+            origin: "app://" + id,
+            manifestURL: manifestURL,
+            receipts: aData.receipts
+          }
+        }
+        let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
+                        .createInstance(Ci.nsIZipReader);
+        try {
+          zipReader.open(zipFile);
+          if (!zipReader.hasEntry("manifest.webapp")) {
+            throw "No manifest.webapp found.";
+          }
+
+          let istream = zipReader.getInputStream("manifest.webapp");
+          msg.app.manifest = JSON.parse(NetUtil.readInputStreamToString(istream,
+                                        istream.available()) || "");
+          if (!checkManifest(msg.app.manifest)) {
+            throw "Invalid manifest";
+          }
+
+          Services.obs.notifyObservers(this, "webapps-ask-install",
+                                             JSON.stringify(msg));
+        } catch (e) {
+          // XXX we may need new error messages.
+          cleanup("INVALID_MANIFEST");
+        } finally {
+          zipReader.close();
+        }
+      });
+    });
+  },
+
   uninstall: function(aData) {
     let found = false;
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.origin == aData.origin) {
         found = true;
         let appNote = JSON.stringify(this._cloneAppObject(app));
         appNote.id = id;
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3719,16 +3719,42 @@ SetMemoryGCSliceTimePrefChangedCallback(
 {
   PRInt32 pref = Preferences::GetInt(aPrefName, -1);
   // handle overflow and negative pref values
   if (pref > 0 && pref < 100000)
     JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
   return 0;
 }
 
+static int
+SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure)
+{
+  PRInt32 pref = Preferences::GetInt(aPrefName, -1);
+  // handle overflow and negative pref values
+  if (pref > 0 && pref < 10000)
+    JS_SetGCParameter(nsJSRuntime::sRuntime, (JSGCParamKey)(long)aClosure, pref);
+  return 0;
+}
+
+static int
+SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure)
+{
+  bool pref = Preferences::GetBool(aPrefName);
+  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref);
+  return 0;
+}
+
+static int
+SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
+{
+  bool pref = Preferences::GetBool(aPrefName);
+  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
+  return 0;
+}
+
 JSObject*
 NS_DOMReadStructuredClone(JSContext* cx,
                           JSStructuredCloneReader* reader,
                           uint32_t tag,
                           uint32_t data,
                           void* closure)
 {
   if (tag == SCTAG_DOM_IMAGEDATA) {
@@ -3877,16 +3903,56 @@ nsJSRuntime::Init()
   SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_incremental",
                                      nsnull);
 
   Preferences::RegisterCallback(SetMemoryGCSliceTimePrefChangedCallback,
                                 "javascript.options.mem.gc_incremental_slice_ms");
   SetMemoryGCSliceTimePrefChangedCallback("javascript.options.mem.gc_incremental_slice_ms",
                                           nsnull);
 
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.gc_high_frequency_time_limit_ms");
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_time_limit_ms",
+                                 (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
+
+  Preferences::RegisterCallback(SetMemoryGCDynamicMarkSlicePrefChangedCallback,
+                                "javascript.options.mem.gc_dynamic_mark_slice");
+  SetMemoryGCDynamicMarkSlicePrefChangedCallback("javascript.options.mem.gc_dynamic_mark_slice",
+                                                 nsnull);
+
+  Preferences::RegisterCallback(SetMemoryGCDynamicHeapGrowthPrefChangedCallback,
+                                "javascript.options.mem.gc_dynamic_heap_growth");
+  SetMemoryGCDynamicHeapGrowthPrefChangedCallback("javascript.options.mem.gc_dynamic_heap_growth",
+                                                  nsnull);
+
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.gc_low_frequency_heap_growth");
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_low_frequency_heap_growth",
+                                 (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
+
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.gc_high_frequency_heap_growth_min");
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_heap_growth_min",
+                                 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
+
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.gc_high_frequency_heap_growth_max");
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_heap_growth_max",
+                                 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
+
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.gc_high_frequency_low_limit_mb");
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_low_limit_mb",
+                                 (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
+
+  Preferences::RegisterCallback(SetMemoryGCPrefChangedCallback,
+                                "javascript.options.mem.gc_high_frequency_high_limit_mb");
+  SetMemoryGCPrefChangedCallback("javascript.options.mem.gc_high_frequency_high_limit_mb",
+                                 (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
+
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs)
     return NS_ERROR_FAILURE;
 
   Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
                                "javascript.options.gc_on_memory_pressure",
                                true);
 
--- a/dom/contacts/Makefile.in
+++ b/dom/contacts/Makefile.in
@@ -6,38 +6,34 @@ DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH            = \
   $(srcdir)        \
   $(NULL)
 
 include $(DEPTH)/config/autoconf.mk
 
-ifeq ($(MOZ_BUILD_APP), $(filter $(MOZ_BUILD_APP),b2g mail))
 VPATH += $(srcdir)/fallback
-endif
 
 MODULE         = dom
 LIBRARY_NAME   = jsdomcontacts_s
 LIBXUL_LIBRARY = 1
 
 XPIDL_MODULE   = dom_contacts
 GRE_MODULE     = 1
 
 EXTRA_COMPONENTS =        \
   ContactManager.js       \
   ContactManager.manifest \
   $(NULL)
 
-ifeq ($(MOZ_BUILD_APP), $(filter $(MOZ_BUILD_APP),b2g mail))
 EXTRA_JS_MODULES =   \
   ContactService.jsm \
   ContactDB.jsm      \
   $(NULL)
-endif
 
 TEST_DIRS += tests
 
 # Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
 # subdirectory (and the ipc one).
 LOCAL_INCLUDES += $(VPATH:%=-I%)
 
 include $(topsrcdir)/config/config.mk
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -16,16 +16,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 "use strict";
 
+var comp = SpecialPowers.wrap(Components);
+comp.utils.import("resource://gre/modules/ContactService.jsm");
+SpecialPowers.setBoolPref("dom.mozContacts.enabled", true);
 SpecialPowers.addPermission("webcontacts-manage", true, document);
 
 // For Sorting
 var c1 = {
   name: "a",
   familyName: ["a"],
   givenName: ["a"],
 };
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -78,26 +78,26 @@ interface mozIDOMApplicationMgmt : nsISu
   /**
    * event listener to get notified of application uninstalls. Only settable by
    * privileged callers.
    * the event will be a mozIDOMApplicationEvent
    */
   attribute nsIDOMEventListener onuninstall;
 };
 
-[scriptable, uuid(d7b0ff50-e667-4ed2-b996-0eb937763952)]
+[scriptable, uuid(6e3d4cf8-ad45-4a8d-8f94-4527ec68673c)]
 interface mozIDOMApplicationRegistry : nsISupports
 {
   /**
-   * Install a web app. onerror can be used to report errors,
-   * and oninstall if the caller is privileged.
+   * Install a web app.
    *
    * @param manifestUrl : the URL of the webapps manifest.
-   * @param parameters : A structure with optional information. 
-   *                     { receipts: ... } will be used to specify the payment receipts for this installation. 
+   * @param parameters  : A structure with optional information.
+   *                      { receipts: ... } will be used to specify the payment receipts for this installation.
+   * @returns           : A DOMRequest object, returning the app object in |result| if install succeeds.
    */
   nsIDOMDOMRequest install(in DOMString manifestUrl, [optional] in jsval parameters);
 
   /**
    * the request will return the application currently installed, or null.
    */
   nsIDOMDOMRequest getSelf();
 
@@ -107,10 +107,20 @@ interface mozIDOMApplicationRegistry : n
   nsIDOMDOMRequest getInstalled();
 
   /**
    * the request will return the applications acquired from this origin but which
    * are not launchable (e.g. by not being natively installed), or null.
    */
   nsIDOMDOMRequest getNotInstalled();
 
+  /**
+   * Install a packaged web app.
+   *
+   * @param packageUrl : the URL of the webapps manifest.
+   * @param parameters : A structure with optional information.
+   *                     { receipts: ... } will be used to specify the payment receipts for this installation.
+   * @returns          : A DOMRequest object, returning the app object in |result| if install succeeds.
+   */
+  nsIDOMDOMRequest installPackage(in DOMString packageUrl, [optional] in jsval parameters);
+
   readonly attribute mozIDOMApplicationMgmt mgmt;
 };
--- a/dom/media/Makefile.in
+++ b/dom/media/Makefile.in
@@ -29,10 +29,16 @@ EXPORTS_NAMESPACE = mozilla
 EXPORTS_mozilla = \
   MediaManager.h \
   $(NULL)
 
 CPPSRCS = \
   MediaManager.cpp \
   $(NULL)
 
+ifdef MOZ_WEBRTC
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/media/webrtc/trunk/src \
+  $(NULL)
+endif
+
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/config/rules.mk
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1,26 +1,31 @@
 /* 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 "MediaManager.h"
 
 #include "MediaStreamGraph.h"
-#include "MediaEngineDefault.h"
-
 #include "nsIDOMFile.h"
 #include "nsIEventTarget.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPopupWindowManager.h"
 
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
 #include "nsGlobalWindow.h"
 
+/* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
+#if defined(MOZ_WEBRTC)
+#include "MediaEngineWebRTC.h"
+#else
+#include "MediaEngineDefault.h"
+#endif
+
 namespace mozilla {
 
 /**
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread.
  */
 class ErrorCallbackRunnable : public nsRunnable
 {
@@ -58,224 +63,130 @@ class SuccessCallbackRunnable : public n
 {
 public:
   SuccessCallbackRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
     nsIDOMFile* aFile, PRUint64 aWindowID)
     : mSuccess(aSuccess)
     , mFile(aFile)
     , mWindowID(aWindowID) {}
 
-  SuccessCallbackRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
-    nsIDOMMediaStream* aStream, PRUint64 aWindowID)
-    : mSuccess(aSuccess)
-    , mStream(aStream)
-    , mWindowID(aWindowID) {}
-
   NS_IMETHOD
   Run()
   {
     // Only run if the window is still active.
     WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
     if (activeWindows->Get(mWindowID)) {
       // XPConnect is a magical unicorn.
-      if (mFile) {
-        mSuccess->OnSuccess(mFile);
-      } else if (mStream) {
-        mSuccess->OnSuccess(mStream);
-      }
+      mSuccess->OnSuccess(mFile);
     }
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   nsCOMPtr<nsIDOMFile> mFile;
-  nsCOMPtr<nsIDOMMediaStream> mStream;
   PRUint64 mWindowID;
 };
 
 /**
- * This runnable creates a nsDOMMediaStream from a given MediaEngineSource
- * and returns it via a success callback. Both must be done on the main thread.
+ * Creates a MediaStream, attaches a listener and fires off a success callback
+ * to the DOM with the stream.
+ *
+ * All of this must be done on the main thread!
  */
-class GetUserMediaCallbackRunnable : public nsRunnable
+class GetUserMediaStreamRunnable : public nsRunnable
 {
 public:
-  GetUserMediaCallbackRunnable(MediaEngineSource* aSource, TrackID aId,
-    nsIDOMGetUserMediaSuccessCallback* aSuccess,
-    nsIDOMGetUserMediaErrorCallback* aError,
-    PRUint64 aWindowID,
-    StreamListeners* aListeners)
-    : mSource(aSource)
-    , mId(aId)
-    , mSuccess(aSuccess)
-    , mError(aError)
+  GetUserMediaStreamRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
+    MediaEngineSource* aSource, StreamListeners* aListeners,
+    PRUint64 aWindowID, TrackID aTrackID)
+    : mSuccess(aSuccess)
+    , mSource(aSource)
+    , mListeners(aListeners)
     , mWindowID(aWindowID)
-    , mListeners(aListeners) {}
+    , mTrackID(aTrackID) {}
+
+  ~GetUserMediaStreamRunnable() {}
 
   NS_IMETHOD
   Run()
   {
-    /**
-     * Normally we would now get the name & UUID for the device and ask the
-     * user permission. We will do that when we have some UI. Currently,
-     * only the Android {picture:true} backend is functional, which does not
-     * need a permission prompt, as permission is implicit by user action.
-     *
-     * See bug 748835 for progress on the desktop UI.
-     */
-    nsCOMPtr<nsDOMMediaStream> comStream = mSource->Allocate();
-    if (!comStream) {
-      NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
-      ));
-      return NS_OK;
+    // Create a media stream.
+    nsCOMPtr<nsDOMMediaStream> stream = nsDOMMediaStream::CreateInputStream();
+
+    nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
+      (nsGlobalWindow::GetOuterWindowWithId(mWindowID));
+
+    if (window && window->GetExtantDoc()) {
+      stream->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
     }
 
     // Add our listener. We'll call Start() on the source when get a callback
     // that the MediaStream has started consuming. The listener is freed
     // when the page is invalidated (on navigation or close).
     GetUserMediaCallbackMediaStreamListener* listener =
-      new GetUserMediaCallbackMediaStreamListener(mSource, comStream, mId);
-    comStream->GetStream()->AddListener(listener);
+      new GetUserMediaCallbackMediaStreamListener(mSource, stream, mTrackID);
+    stream->GetStream()->AddListener(listener);
 
-    {
-      MutexAutoLock lock(*(MediaManager::Get()->GetLock()));
-      mListeners->AppendElement(listener);
+    // No need for locking because we always do this in the main thread.
+    mListeners->AppendElement(listener);
+
+    // We're in the main thread, so no worries here either.
+    WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
+    if (activeWindows->Get(mWindowID)) {
+      mSuccess->OnSuccess(stream);
     }
 
-    // Add the listener to CallbackRunnables so it can be invalidated.
-    NS_DispatchToMainThread(new SuccessCallbackRunnable(
-      mSuccess, comStream.get(), mWindowID
-    ));
     return NS_OK;
   }
 
 private:
-  nsCOMPtr<MediaEngineSource> mSource;
-  TrackID mId;
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
-  PRUint64 mWindowID;
+  nsRefPtr<MediaEngineSource> mSource;
   StreamListeners* mListeners;
-};
-
-/**
- * This runnable creates a nsIDOMFile from a MediaEngineVideoSource and
- * passes the result back via a SuccessRunnable. Both must be done on the
- * main thread.
- */
-class GetUserMediaSnapshotCallbackRunable : public nsRunnable
-{
-public:
-  GetUserMediaSnapshotCallbackRunable(MediaEngineSource* aSource,
-    PRUint32 aDuration,
-    nsIDOMGetUserMediaSuccessCallback* aSuccessCallback,
-    nsIDOMGetUserMediaErrorCallback* aErrorCallback,
-    nsPIDOMWindow* aWindow)
-    : mSource(aSource)
-    , mDuration(aDuration)
-    , mSuccessCallback(aSuccessCallback)
-    , mErrorCallback(aErrorCallback)
-    , mWindow(aWindow) {}
-
-  NS_IMETHOD
-  Run()
-  {
-    mWindowID = mWindow->WindowID();
-
-    // Before getting a snapshot, check if page is allowed to open a popup.
-    // We do this because {picture:true} on all platforms will open a new
-    // "window" to let the user preview or select an image.
-
-    if (mWindow->GetPopupControlState() <= openControlled) {
-      return NS_OK;
-    }
-    
-    nsCOMPtr<nsIPopupWindowManager> pm =
-      do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
-    if (!pm) {
-      return NS_OK;
-    }
-
-    PRUint32 permission;
-    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
-    pm->TestPermission(doc->GetDocumentURI(), &permission);
-    if (permission == nsIPopupWindowManager::DENY_POPUP) {
-      nsCOMPtr<nsIDOMDocument> domDoc = mWindow->GetExtantDocument();
-      nsGlobalWindow::FirePopupBlockedEvent(
-        domDoc, mWindow, nsnull, EmptyString(), EmptyString()
-      );
-      return NS_OK;
-    }
-
-    nsCOMPtr<nsDOMMediaStream> comStream = mSource->Allocate();
-    if (!comStream) {
-      NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mErrorCallback, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
-      ));
-      return NS_OK;
-    }
-
-    nsCOMPtr<nsIDOMFile> file;
-    mSource->Snapshot(mDuration, getter_AddRefs(file));
-    mSource->Deallocate();
-
-    NS_DispatchToMainThread(new SuccessCallbackRunnable(
-      mSuccessCallback, file, mWindowID
-    ));
-    return NS_OK;
-  }
-
-private:
-  nsCOMPtr<MediaEngineSource> mSource;
-  PRUint32 mDuration;
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccessCallback;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback>  mErrorCallback;
-  nsCOMPtr<nsPIDOMWindow> mWindow;
-
   PRUint64 mWindowID;
+  TrackID mTrackID;
 };
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
- * GetUserMediaCallbackRunnable or GetUserMediaSnapshotCallbackRunnable
- * will be dispatched to the main thread to return the result to DOM.
+ * ProcessGetUserMedia or ProcessGetUserMediaSnapshot is called, and the results
+ * are sent back to the DOM.
  *
- * Do not run this on the main thread.
+ * Do not run this on the main thread. The success and error callbacks *MUST*
+ * be dispatched on the main thread!
  */
 class GetUserMediaRunnable : public nsRunnable
 {
 public:
   GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
     nsIDOMGetUserMediaSuccessCallback* aSuccess,
     nsIDOMGetUserMediaErrorCallback* aError,
-    nsPIDOMWindow* aWindow, StreamListeners* aListeners)
+    StreamListeners* aListeners, PRUint64 aWindowID)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(aPicture)
     , mSuccess(aSuccess)
     , mError(aError)
-    , mWindow(aWindow)
-    , mListeners(aListeners) {}
+    , mListeners(aListeners)
+    , mWindowID(aWindowID) {}
 
   ~GetUserMediaRunnable() {}
 
   // We only support 1 audio and 1 video track for now.
   enum {
     kVideoTrack = 1,
     kAudioTrack = 2
   };
 
   NS_IMETHOD
   Run()
   {
     mManager = MediaManager::Get();
-    mWindowID = mWindow->WindowID();
 
     if (mPicture) {
       SendPicture();
       return NS_OK;
     }
 
     // XXX: Implement merging two streams (See bug 758391).
     if (mAudio && mVideo) {
@@ -293,34 +204,89 @@ public:
     if (mAudio) {
       SendAudio();
       return NS_OK;
     }
 
     return NS_OK;
   }
 
+  /**
+   * Allocates a video or audio device and returns a MediaStream via
+   * a GetUserMediaStreamRunnable. Runs off the main thread.
+   */
+  void
+  ProcessGetUserMedia(MediaEngineSource* aSource, TrackID aTrackID)
+  {
+    /**
+     * Normally we would now get the name & UUID for the device and ask the
+     * user permission. We will do that when we have some UI. Currently,
+     * only the Android {picture:true} backend is functional, which does not
+     * need a permission prompt, as permission is implicit by user action.
+     *
+     * See bug 748835 for progress on the desktop UI.
+     */
+    nsresult rv = aSource->Allocate();
+    if (NS_FAILED(rv)) {
+      NS_DispatchToMainThread(new ErrorCallbackRunnable(
+        mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
+      ));
+      return;
+    }
+
+    NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
+      mSuccess.get(), aSource, mListeners, mWindowID, aTrackID
+    ));
+    return;
+  }
+
+  /**
+   * Allocates a video device, takes a snapshot and returns a DOMFile via
+   * a SuccessRunnable or an error via the ErrorRunnable. Off the main thread.
+   */
+  void
+  ProcessGetUserMediaSnapshot(MediaEngineSource* aSource, int aDuration)
+  {
+    nsresult rv = aSource->Allocate();
+    if (NS_FAILED(rv)) {
+      NS_DispatchToMainThread(new ErrorCallbackRunnable(
+        mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
+      ));
+      return;
+    }
+
+    nsCOMPtr<nsIDOMFile> file;
+    aSource->Snapshot(aDuration, getter_AddRefs(file));
+    aSource->Deallocate();
+
+    NS_DispatchToMainThread(new SuccessCallbackRunnable(
+      mSuccess, file, mWindowID
+    ));
+    return;
+  }
+
   // {picture:true}
   void
   SendPicture()
   {
     nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
     mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
 
     PRUint32 count = videoSources.Length();
     if (!count) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
       ));
       return;
     }
-    MediaEngineVideoSource* videoSource = videoSources[count - 1];
-    NS_DispatchToMainThread(new GetUserMediaSnapshotCallbackRunable(
-      videoSource, 0 /* duration */, mSuccess, mError, mWindow
-    ));
+
+    // We pick the first source as the "default". Work is needed here in the
+    // form of UI to let the user pick a source. (Also true for audio).
+    MediaEngineVideoSource* videoSource = videoSources[0];
+    ProcessGetUserMediaSnapshot(videoSource, 0 /* duration */);
   }
 
   // {video:true}
   void
   SendVideo()
   {
     nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
     mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
@@ -328,20 +294,18 @@ public:
     PRUint32 count = videoSources.Length();
     if (!count) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
       ));
       return;
     }
 
-    MediaEngineVideoSource* videoSource = videoSources[count - 1];
-    NS_DispatchToMainThread(new GetUserMediaCallbackRunnable(
-      videoSource, kVideoTrack, mSuccess, mError, mWindowID, mListeners
-    ));
+    MediaEngineVideoSource* videoSource = videoSources[0];
+    ProcessGetUserMedia(videoSource, kVideoTrack);
   }
 
   // {audio:true}
   void
   SendAudio()
   {
     nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
     mManager->GetBackend()->EnumerateAudioDevices(&audioSources);
@@ -349,34 +313,31 @@ public:
     PRUint32 count = audioSources.Length();
     if (!count) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
       ));
       return;
     }
 
-    MediaEngineAudioSource* audioSource = audioSources[count - 1];
-    NS_DispatchToMainThread(new GetUserMediaCallbackRunnable(
-      audioSource, kAudioTrack, mSuccess, mError, mWindowID, mListeners
-    ));
+    MediaEngineAudioSource* audioSource = audioSources[0];
+    ProcessGetUserMedia(audioSource, kAudioTrack);
   }
 
 private:
   bool mAudio;
   bool mVideo;
   bool mPicture;
 
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
-  nsCOMPtr<nsPIDOMWindow> mWindow;
   StreamListeners* mListeners;
+  PRUint64 mWindowID;
 
   MediaManager* mManager;
-  PRUint64 mWindowID;
 };
 
 
 nsRefPtr<MediaManager> MediaManager::sSingleton;
 
 NS_IMPL_ISUPPORTS1(MediaManager, nsIObserver)
 
 /**
@@ -385,21 +346,54 @@ NS_IMPL_ISUPPORTS1(MediaManager, nsIObse
  * for handling all incoming getUserMedia calls from every window.
  */
 nsresult
 MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
   nsIDOMGetUserMediaSuccessCallback* onSuccess,
   nsIDOMGetUserMediaErrorCallback* onError)
 {
   NS_ENSURE_TRUE(aParams, NS_ERROR_NULL_POINTER);
+  NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
 
   bool audio, video, picture;
+
   nsresult rv = aParams->GetPicture(&picture);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  /**
+   * If we were asked to get a picture, before getting a snapshot, we check if
+   * the calling page is allowed to open a popup. We do this because
+   * {picture:true} will open a new "window" to let the user preview or select
+   * an image, on Android. The desktop UI for {picture:true} is TBD, at which
+   * may point we can decide whether to extend this test there as well.
+   */
+#if !defined(MOZ_WEBRTC)
+  if (picture) {
+    if (aWindow->GetPopupControlState() <= openControlled) {
+      return NS_ERROR_FAILURE;
+    }
+    nsCOMPtr<nsIPopupWindowManager> pm =
+      do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
+    if (!pm) {
+      return NS_ERROR_FAILURE;
+    }
+
+    PRUint32 permission;
+    nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
+    pm->TestPermission(doc->GetDocumentURI(), &permission);
+    if (aWindow && (permission == nsIPopupWindowManager::DENY_POPUP)) {
+      nsCOMPtr<nsIDOMDocument> domDoc = aWindow->GetExtantDocument();
+      nsGlobalWindow::FirePopupBlockedEvent(
+        domDoc, aWindow, nsnull, EmptyString(), EmptyString()
+      );
+      return NS_ERROR_FAILURE;
+    }
+  }
+#endif
+
   rv = aParams->GetAudio(&audio);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aParams->GetVideo(&video);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
   nsString cameraType;
@@ -410,40 +404,45 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   // when this window is closed or navigated away from.
   PRUint64 windowID = aWindow->WindowID();
   StreamListeners* listeners = mActiveWindows.Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
     mActiveWindows.Put(windowID, listeners);
   }
 
-  // Pass runanbles along to GetUserMediaRunnable so it can add the
+  // Pass runnables along to GetUserMediaRunnable so it can add the
   // MediaStreamListener to the runnable list.
   nsCOMPtr<nsIRunnable> gUMRunnable = new GetUserMediaRunnable(
-    audio, video, picture, onSuccess, onError, aWindow, listeners
+    audio, video, picture, onSuccess, onError, listeners, windowID
   );
 
   // Reuse the same thread to save memory.
   if (!mMediaThread) {
     rv = NS_NewThread(getter_AddRefs(mMediaThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mMediaThread->Dispatch(gUMRunnable, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 MediaEngine*
 MediaManager::GetBackend()
 {
-  // Plugin backends as appropriate. Only default is available for now, which
-  // also includes picture support for Android.
+  // Plugin backends as appropriate. The default engine also currently
+  // includes picture support for Android.
   if (!mBackend) {
+#if defined(MOZ_WEBRTC)
+    mBackend = new MediaEngineWebRTC();
+#else
     mBackend = new MediaEngineDefault();
+#endif
   }
+
   return mBackend;
 }
 
 WindowTable*
 MediaManager::GetActiveWindows()
 {
   return &mActiveWindows;
 }
@@ -453,17 +452,16 @@ MediaManager::OnNavigation(PRUint64 aWin
 {
   // Invalidate this window. The runnables check this value before making
   // a call to content.
   StreamListeners* listeners = mActiveWindows.Get(aWindowID);
   if (!listeners) {
     return;
   }
 
-  MutexAutoLock lock(*mLock);
   PRUint32 length = listeners->Length();
   for (PRUint32 i = 0; i < length; i++) {
     nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
       listeners->ElementAt(i);
     listener->Invalidate();
     listener = nsnull;
   }
   listeners->Clear();
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -1,16 +1,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 "MediaEngine.h"
 #include "mozilla/Services.h"
 
 #include "nsHashKeys.h"
+#include "nsGlobalWindow.h"
 #include "nsClassHashtable.h"
 #include "nsObserverService.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
@@ -22,17 +23,17 @@ namespace mozilla {
  */
 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
 {
 public:
   GetUserMediaCallbackMediaStreamListener(MediaEngineSource* aSource,
     nsDOMMediaStream* aStream, TrackID aListenId)
     : mSource(aSource)
     , mStream(aStream)
-    , mId(aListenId)
+    , mID(aListenId)
     , mValid(true) {}
 
   void
   Invalidate()
   {
     if (!mValid) {
       return;
     }
@@ -41,38 +42,37 @@ public:
     mSource->Stop();
     mSource->Deallocate();
   }
 
   void
   NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming)
   {
     if (aConsuming == CONSUMED) {
-      nsRefPtr<SourceMediaStream> stream = mStream->GetStream()->AsSourceStream();
-      mSource->Start(stream, mId);
+      SourceMediaStream* stream = mStream->GetStream()->AsSourceStream();
+      mSource->Start(stream, mID);
       return;
     }
 
     // NOT_CONSUMED
     Invalidate();
     return;
   }
 
   void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
   void NotifyOutput(MediaStreamGraph* aGraph) {}
   void NotifyFinished(MediaStreamGraph* aGraph) {}
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
     TrackRate aTrackRate, TrackTicks aTrackOffset,
     PRUint32 aTrackEvents, const MediaSegment& aQueuedMedia) {}
-  nsresult Run() { return NS_OK; }
 
 private:
-  nsCOMPtr<MediaEngineSource> mSource;
+  nsRefPtr<MediaEngineSource> mSource;
   nsCOMPtr<nsDOMMediaStream> mStream;
-  TrackID mId;
+  TrackID mID;
   bool mValid;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaManager MOZ_FINAL : public nsIObserver {
 public:
@@ -84,45 +84,38 @@ public:
       obs->AddObserver(sSingleton, "xpcom-shutdown", false);
     }
     return sSingleton;
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
-  Mutex* GetLock() {
-    return mLock;
-  }
-
   MediaEngine* GetBackend();
   WindowTable* GetActiveWindows();
 
   nsresult GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
   void OnNavigation(PRUint64 aWindowID);
 
 private:
   // Make private because we want only one instance of this class
   MediaManager()
   : mBackend(nsnull)
   , mMediaThread(nsnull) {
-    mLock = new mozilla::Mutex("MediaManager::StreamListenersLock");
     mActiveWindows.Init();
   };
   MediaManager(MediaManager const&) {};
 
   ~MediaManager() {
-    delete mLock;
     delete mBackend;
   };
 
   MediaEngine* mBackend;
   nsCOMPtr<nsIThread> mMediaThread;
 
-  Mutex* mLock;
   WindowTable mActiveWindows;
 
   static nsRefPtr<MediaManager> sSingleton;
 };
 
 } // namespace mozilla
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -330,20 +330,17 @@ private:
   PRInt32                                   mInCGPaintLevel;
   nsRefPtr<nsIOSurface>                     mIOSurface;
   nsCARenderer                              mCARenderer;
   CGColorSpaceRef                           mColorProfile;
   static nsCOMPtr<nsITimer>                *sCATimer;
   static nsTArray<nsPluginInstanceOwner*>  *sCARefreshListeners;
   bool                                      mSentInitialTopLevelWindowEvent;
 #endif
-  // We need to know if async hide window is required since we
-  // can not check UseAsyncRendering when executing StopPlugin
-  bool                                      mAsyncHidePluginWindow;
-  
+
   // Initially, the event loop nesting level we were created on, it's updated
   // if we detect the appshell is on a lower level as long as we're not stopped.
   // We delay DoStopPlugin() until the appshell reaches this level or lower.
   PRUint32                    mLastEventloopNestingLevel;
   bool                        mContentFocused;
   bool                        mWidgetVisible;    // used on Mac to store our widget's visible state
 #ifdef XP_MACOSX
   bool                        mPluginPortChanged;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -228,17 +228,17 @@ PluginInstanceChild::InternalGetNPObject
 
         default:
             NS_NOTREACHED("Don't know what to do with this value type!");
     }
 
 #ifdef DEBUG
     {
         NPError currentResult;
-        PPluginScriptableObjectChild* currentActor;
+        PPluginScriptableObjectChild* currentActor = nsnull;
 
         switch (aValue) {
             case NPNVWindowNPObject:
                 CallNPN_GetValue_NPNVWindowNPObject(&currentActor,
                                                     &currentResult);
                 break;
             case NPNVPluginElementNPObject:
                 CallNPN_GetValue_NPNVPluginElementNPObject(&currentActor,
--- a/dom/settings/tests/test_settings_basics.html
+++ b/dom/settings/tests/test_settings_basics.html
@@ -16,16 +16,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 "use strict";
 
+var comp = SpecialPowers.wrap(Components);
+comp.utils.import("resource://gre/modules/SettingsChangeNotifier.jsm");
+SpecialPowers.setBoolPref("dom.mozSettings.enabled", true);
 SpecialPowers.addPermission("websettings-read", true, document);
 SpecialPowers.addPermission("websettings-readwrite", true, document);
 
 function onUnwantedSuccess() {
   ok(false, "onUnwantedSuccess: shouldn't get here");
 }
 
 function onFailure() {
--- a/dom/tests/mochitest/webapps/test_list_api.xul
+++ b/dom/tests/mochitest/webapps/test_list_api.xul
@@ -23,16 +23,17 @@
 <script type="text/javascript">
 <![CDATA[
 
 var expectedMethods = ['navigator.mozApps.QueryInterface',
                        'navigator.mozApps.getInstalled',
                        'navigator.mozApps.getNotInstalled',
                        'navigator.mozApps.getSelf',
                        'navigator.mozApps.install',
+                       'navigator.mozApps.installPackage',
                        'navigator.mozApps.mgmt.QueryInterface',
                        'navigator.mozApps.mgmt.getAll'];
 var actualMethods = [];
 
 actualMethods = actualMethods.concat(iterateMethods("navigator.mozApps.", navigator.mozApps, ['setApplicationChooser', 'setRepoOrigin', 'setMockResponse']));
 actualMethods = actualMethods.concat(iterateMethods("navigator.mozApps.mgmt.", navigator.mozApps.mgmt));
 actualMethods = actualMethods.sort();
 ok(JSON.stringify(actualMethods) == JSON.stringify(expectedMethods), actualMethods);
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -7,18 +7,23 @@ webidl_base = $(topsrcdir)/dom/webidl
 webidl_files = \
   CanvasRenderingContext2D.webidl \
   Function.webidl \
   EventListener.webidl \
   EventTarget.webidl \
   XMLHttpRequest.webidl \
   XMLHttpRequestEventTarget.webidl \
   XMLHttpRequestUpload.webidl \
+  $(NULL)
+
+ifdef MOZ_WEBGL
+webidl_files += \
   WebGLRenderingContext.webidl \
   $(NULL)
+endif
 
 ifdef ENABLE_TESTS
 test_webidl_files := \
   TestCodeGen.webidl \
   TestDictionary.webidl \
   $(NULL)
 else
 test_webidl_files := $(NULL)
deleted file mode 100644
--- a/editor/libeditor/base/nsIEditorSupport.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsIEditorSupport_h__
-#define nsIEditorSupport_h__
-#include "nsISupports.h"
-
-class nsIDOMNode;
-
-/*
-Private Editor interface for a class that can provide helper functions
-*/
-
-#define NS_IEDITORSUPPORT_IID \
-{/* c4cbcda8-58ec-4f03-9c99-5e46b6828b7a*/ \
-0xc4cbcda8, 0x58ec, 0x4f03, \
-{0x0c, 0x99, 0x5e, 0x46, 0xb6, 0x82, 0x8b, 0x7a} }
-
-
-/**
- */
-class nsIEditorSupport  : public nsISupports {
-
-public:
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEDITORSUPPORT_IID)
-
-  /** 
-   * SplitNode() creates a new node identical to an existing node, and split the contents between the two nodes
-   * @param aExistingRightNode   the node to split.  It will become the new node's next sibling.
-   * @param aOffset              the offset of aExistingRightNode's content|children to do the split at
-   * @param aNewLeftNode         [OUT] the new node resulting from the split, becomes aExistingRightNode's previous sibling.
-   * @param aParent              the parent of aExistingRightNode
-   */
-  NS_IMETHOD SplitNodeImpl(nsIDOMNode * aExistingRightNode,
-                           PRInt32      aOffset,
-                           nsIDOMNode * aNewLeftNode,
-                           nsIDOMNode * aParent)=0;
-
-  /** 
-   * JoinNodes() takes 2 nodes and merge their content|children.
-   * @param aNodeToKeep   The node that will remain after the join.
-   * @param aNodeToJoin   The node that will be joined with aNodeToKeep.
-   *                      There is no requirement that the two nodes be of the same type.
-   * @param aParent       The parent of aExistingRightNode
-   * @param aNodeToKeepIsFirst  if true, the contents|children of aNodeToKeep come before the
-   *                            contents|children of aNodeToJoin, otherwise their positions are switched.
-   */
-  NS_IMETHOD JoinNodesImpl(nsIDOMNode *aNodeToKeep,
-                           nsIDOMNode  *aNodeToJoin,
-                           nsIDOMNode  *aParent,
-                           bool         aNodeToKeepIsFirst)=0;
-
-  static PRInt32 GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent);
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIEditorSupport, NS_IEDITORSUPPORT_IID)
-
-#endif //nsIEditorSupport_h__
-
--- a/extensions/auth/nsAuthSambaNTLM.cpp
+++ b/extensions/auth/nsAuthSambaNTLM.cpp
@@ -41,17 +41,17 @@ nsAuthSambaNTLM::Shutdown()
         PR_WaitProcess(mChildPID, &exitCode);
         mChildPID = nsnull;
     }
 }
 
 NS_IMPL_ISUPPORTS1(nsAuthSambaNTLM, nsIAuthModule)
 
 static bool
-SpawnIOChild(char** aArgs, PRProcess** aPID,
+SpawnIOChild(char* const* aArgs, PRProcess** aPID,
              PRFileDesc** aFromChildFD, PRFileDesc** aToChildFD)
 {
     PRFileDesc* toChildPipeRead;
     PRFileDesc* toChildPipeWrite;
     if (PR_CreatePipe(&toChildPipeRead, &toChildPipeWrite) != PR_SUCCESS)
         return false;
     PR_SetFDInheritable(toChildPipeRead, true);
     PR_SetFDInheritable(toChildPipeWrite, false);
@@ -167,25 +167,25 @@ static PRUint8* ExtractMessage(const nsA
 
 nsresult
 nsAuthSambaNTLM::SpawnNTLMAuthHelper()
 {
     const char* username = PR_GetEnv("USER");
     if (!username)
         return NS_ERROR_FAILURE;
 
-    char* args[] = {
+    const char* const args[] = {
         "ntlm_auth",
         "--helper-protocol", "ntlmssp-client-1",
         "--use-cached-creds",
-        "--username", const_cast<char*>(username),
+        "--username", username,
         nsnull
     };
 
-    bool isOK = SpawnIOChild(args, &mChildPID, &mFromChildFD, &mToChildFD);
+    bool isOK = SpawnIOChild(const_cast<char* const*>(args), &mChildPID, &mFromChildFD, &mToChildFD);
     if (!isOK)  
         return NS_ERROR_FAILURE;
 
     if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n")))
         return NS_ERROR_FAILURE;
     nsCString line;
     if (!ReadLine(mFromChildFD, line))
         return NS_ERROR_FAILURE;
--- a/extensions/spellcheck/src/mozSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozSpellChecker.cpp
@@ -328,16 +328,19 @@ mozSpellChecker::GetCurrentDictionary(ns
   mSpellCheckingEngine->GetDictionary(getter_Copies(dictname));
   aDictionary = dictname;
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
 {
+  // Calls to mozISpellCheckingEngine::SetDictionary might destroy us
+  nsRefPtr<mozSpellChecker> kungFuDeathGrip = this;
+
   mSpellCheckingEngine = nsnull;
 
   if (aDictionary.IsEmpty()) {
     return NS_OK;
   }
 
   nsresult rv;
   nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -660,16 +660,19 @@ ifdef MOZ_ETW
 RESFILE = ETWProvider.res
 endif
 
 # HP-UX does not require the extra linking of "-lm"
 ifeq (,$(filter HP-UX WINNT OS2,$(OS_ARCH)))
 EXTRA_LIBS	+= -lm
 endif
 
+CFLAGS += $(MOZ_ZLIB_CFLAGS)
+EXTRA_LIBS += $(MOZ_ZLIB_LIBS)
+
 # Prevent floating point errors caused by VC++ optimizations
 ifdef _MSC_VER
 # XXX We should add this to CXXFLAGS, too?
 CFLAGS += -fp:precise
 
 ifeq ($(CPU_ARCH),x86)
 # Workaround compiler bug on PGO (Bug 721284)
 MonoIC.$(OBJ_SUFFIX): CXXFLAGS += -GL-
--- a/js/src/aclocal.m4
+++ b/js/src/aclocal.m4
@@ -13,10 +13,11 @@ builtin(include, build/autoconf/mozcommo
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
 builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
 builtin(include, build/autoconf/expandlibs.m4)dnl
 builtin(include, build/autoconf/arch.m4)dnl
 builtin(include, build/autoconf/android.m4)dnl
+builtin(include, build/autoconf/zlib.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
new file mode 100644
--- /dev/null
+++ b/js/src/build/autoconf/zlib.m4
@@ -0,0 +1,54 @@
+dnl This Source Code Form is subject to the terms of the Mozilla Public
+dnl License, v. 2.0. If a copy of the MPL was not distributed with this
+dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+dnl Usage: MOZ_ZLIB_CHECK([version])
+
+AC_DEFUN([MOZ_ZLIB_CHECK],
+[
+
+MOZZLIB=$1
+
+MOZ_ARG_WITH_STRING(system-zlib,
+[  --with-system-zlib[=PFX]
+                          Use system libz [installed at prefix PFX]],
+    ZLIB_DIR=$withval)
+
+if test -z "$MOZ_ZLIB_LIBS$MOZ_ZLIB_CFLAGS$SKIP_LIBRARY_CHECKS"; then
+    _SAVE_CFLAGS=$CFLAGS
+    _SAVE_LDFLAGS=$LDFLAGS
+    _SAVE_LIBS=$LIBS
+
+    if test -n "${ZLIB_DIR}" -a "${ZLIB_DIR}" != "yes"; then
+        MOZ_ZLIB_CFLAGS="-I${ZLIB_DIR}/include"
+        MOZ_ZLIB_LIBS="-L${ZLIB_DIR}/lib"
+        CFLAGS="$MOZ_ZLIB_CFLAGS $CFLAGS"
+        LDFLAGS="$MOZ_ZLIB_LIBS $LDFLAGS"
+    fi
+    if test -z "$ZLIB_DIR" -o "$ZLIB_DIR" = no; then
+        MOZ_NATIVE_ZLIB=
+    else
+        AC_CHECK_LIB(z, gzread, [MOZ_NATIVE_ZLIB=1 MOZ_ZLIB_LIBS="$MOZ_ZLIB_LIBS -lz"],
+            [MOZ_NATIVE_ZLIB=])
+        if test "$MOZ_NATIVE_ZLIB" = 1; then
+            MOZZLIBNUM=`echo $MOZZLIB | awk -F. changequote(<<, >>)'{printf "0x%x\n", (((<<$>>1 * 16 + <<$>>2) * 16) + <<$>>3) * 16 + <<$>>4}'changequote([, ])`
+            AC_TRY_COMPILE([ #include <stdio.h>
+                             #include <string.h>
+                             #include <zlib.h> ],
+                           [ #if ZLIB_VERNUM < $MOZZLIBNUM
+                             #error "Insufficient zlib version ($MOZZLIBNUM required)."
+                             #endif ],
+                           MOZ_NATIVE_ZLIB=1,
+                           AC_MSG_ERROR([Insufficient zlib version for --with-system-zlib ($MOZZLIB required)]))
+        fi
+    fi
+    CFLAGS=$_SAVE_CFLAGS
+    LDFLAGS=$_SAVE_LDFLAGS
+    LIBS=$_SAVE_LIBS
+fi
+
+AC_SUBST(MOZ_ZLIB_CFLAGS)
+AC_SUBST(MOZ_ZLIB_LIBS)
+AC_SUBST(MOZ_NATIVE_ZLIB)
+
+])
--- a/js/src/config/Makefile.in
+++ b/js/src/config/Makefile.in
@@ -37,16 +37,21 @@ DIRS		+= mkdepend
 endif
 endif
 
 include $(topsrcdir)/config/config.mk
 
 # Do not install util programs
 NO_INSTALL=1
 
+# Force wrap zlib system header if building js as a shared library.
+ifdef JS_SHARED_LIBRARY
+DEFINES += -DMOZ_NATIVE_ZLIB=1
+endif
+
 include $(topsrcdir)/config/rules.mk
 
 HOST_CFLAGS += -DUNICODE -D_UNICODE
 
 export:: $(TARGETS)
 ifdef HOST_PROGRAM
 	$(INSTALL) $(HOST_PROGRAM) $(DIST)/bin
 endif
--- a/js/src/config/autoconf.mk.in
+++ b/js/src/config/autoconf.mk.in
@@ -187,16 +187,19 @@ DOXYGEN		= @DOXYGEN@
 PBBUILD_BIN	= @PBBUILD_BIN@
 SDP		= @SDP@
 NSINSTALL_BIN	= @NSINSTALL_BIN@
 
 NSPR_CONFIG	= @NSPR_CONFIG@
 NSPR_CFLAGS	= @NSPR_CFLAGS@
 NSPR_LIBS	= @NSPR_LIBS@
 
+MOZ_ZLIB_LIBS   = @MOZ_ZLIB_LIBS@
+MOZ_ZLIB_CFLAGS = @MOZ_ZLIB_CFLAGS@
+
 MOZ_NATIVE_FFI	= @MOZ_NATIVE_FFI@
 MOZ_FFI_LIBS	= @MOZ_FFI_LIBS@
 MOZ_FFI_CFLAGS	= @MOZ_FFI_CFLAGS@
 
 USE_DEPENDENT_LIBS = @USE_DEPENDENT_LIBS@
 
 JS_NATIVE_EDITLINE = @JS_NATIVE_EDITLINE@
 JS_DISABLE_SHELL   = @JS_DISABLE_SHELL@
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3320,16 +3320,24 @@ if test -n "$MOZ_NATIVE_NSPR"; then
                  #error PR_STATIC_ASSERT not defined
                  #endif],
                 [MOZ_NATIVE_NSPR=1],
                 AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT]))
     CFLAGS=$_SAVE_CFLAGS
 fi
 
 dnl ========================================================
+dnl system zlib Support
+dnl ========================================================
+dnl Standalone js defaults to system zlib
+ZLIB_DIR=yes
+
+MOZ_ZLIB_CHECK([1.2.3])
+
+dnl ========================================================
 dnl system libffi Support
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(system-ffi,
 [  --enable-system-ffi       Use system libffi (located with pkgconfig)],
     MOZ_NATIVE_FFI=1 )
 
 if test -n "$MOZ_NATIVE_FFI"; then
     # Vanilla libffi 3.0.9 needs a few patches from upcoming version 3.0.10
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1888,17 +1888,17 @@ Parser::processDirectives(ParseNode *stm
     bool gotStrictMode = false;
     for (TokenKind tt = tokenStream.getToken(TSF_OPERAND); tt == TOK_STRING; tt = tokenStream.getToken(TSF_OPERAND)) {
         ParseNode *stringNode = atomNode(PNK_STRING, JSOP_STRING);
         if (!stringNode)
             return false;
         const Token directive = tokenStream.currentToken();
         bool isDirective = IsEscapeFreeStringLiteral(directive);
         JSAtom *atom = directive.atom();
-        TokenKind next = tokenStream.peekTokenSameLine(TSF_OPERAND);
+        TokenKind next = tokenStream.peekTokenSameLine();
         if (next != TOK_EOF && next != TOK_EOL && next != TOK_SEMI && next != TOK_RC) {
             freeTree(stringNode);
             if (next == TOK_ERROR)
                 return false;
             break;
         }
         tokenStream.matchToken(TOK_SEMI);
         if (isDirective) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug773153.js
@@ -0,0 +1,3 @@
+function f() {
+    "" < "";
+}
--- a/js/src/jsapi-tests/Makefile.in
+++ b/js/src/jsapi-tests/Makefile.in
@@ -77,16 +77,16 @@ CSRCS = \
 #  testRegExpInstanceProperties.cpp \
 #  $(NULL)
 
 DEFINES         += -DEXPORT_JS_API
 # Building against js_static requires that we declare mfbt sybols "exported"
 # on its behalf.
 DEFINES         += -DIMPL_MFBT
 
-LIBS      = $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) $(NSPR_LIBS)
+LIBS      = $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) $(NSPR_LIBS) $(MOZ_ZLIB_LIBS)
 
 LOCAL_INCLUDES += -I$(topsrcdir) -I..
 
 include $(topsrcdir)/config/rules.mk
 
 check::
 	$(wildcard $(RUN_TEST_PROGRAM)) $(DIST)/bin/jsapi-tests$(BIN_SUFFIX)
--- a/js/src/jsapi-tests/testProfileStrings.cpp
+++ b/js/src/jsapi-tests/testProfileStrings.cpp
@@ -17,16 +17,18 @@ static uint32_t size = 0;
 static uint32_t max_stack = 0;
 
 static void
 reset(JSContext *cx)
 {
     size = max_stack = 0;
     memset(stack, 0, sizeof(stack));
     cx->runtime->spsProfiler.stringsReset();
+    cx->runtime->spsProfiler.enableSlowAssertions(true);
+    js::EnableRuntimeProfilingStack(cx->runtime, true);
 }
 
 static JSClass ptestClass = {
     "Prof", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
     JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 static JSBool
@@ -39,36 +41,44 @@ test_fn(JSContext *cx, unsigned argc, js
 static JSBool
 test_fn2(JSContext *cx, unsigned argc, jsval *vp)
 {
     jsval r;
     return JS_CallFunctionName(cx, JS_GetGlobalObject(cx), "d", 0, NULL, &r);
 }
 
 static JSBool
-test_fn3(JSContext *cx, unsigned argc, jsval *vp)
+enable(JSContext *cx, unsigned argc, jsval *vp)
 {
-    js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 10);
+    js::EnableRuntimeProfilingStack(cx->runtime, true);
+    return JS_TRUE;
+}
+
+static JSBool
+disable(JSContext *cx, unsigned argc, jsval *vp)
+{
+    js::EnableRuntimeProfilingStack(cx->runtime, false);
     return JS_TRUE;
 }
 
 static JSBool
 Prof(JSContext* cx, unsigned argc, jsval *vp)
 {
     JSObject *obj = JS_NewObjectForConstructor(cx, &ptestClass, vp);
     if (!obj)
         return JS_FALSE;
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
     return JS_TRUE;
 }
 
 static JSFunctionSpec ptestFunctions[] = {
     JS_FS("test_fn", test_fn, 0, 0),
     JS_FS("test_fn2", test_fn2, 0, 0),
-    JS_FS("test_fn3", test_fn3, 0, 0),
+    JS_FS("enable", enable, 0, 0),
+    JS_FS("disable", disable, 0, 0),
     JS_FS_END
 };
 
 static JSObject*
 initialize(JSContext *cx)
 {
     js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 10);
     return JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ptestClass, Prof, 0,
@@ -107,20 +117,21 @@ BEGIN_TEST(testProfileStrings_isCalled)
     reset(cx);
     {
         jsvalRoot rval(cx);
         CHECK(JS_CallFunctionName(cx, global, "check2", 0, NULL, rval.addr()));
         CHECK(cx->runtime->spsProfiler.stringsCount() == 5);
         CHECK(max_stack == 7);
         CHECK(size == 0);
     }
+    js::EnableRuntimeProfilingStack(cx->runtime, false);
+    js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 3);
     reset(cx);
     {
         jsvalRoot rval(cx);
-        js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 3);
         stack[3].string = (char*) 1234;
         CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
         CHECK((size_t) stack[3].string == 1234);
         CHECK(max_stack == 9);
         CHECK(size == 0);
     }
     return true;
 }
@@ -153,21 +164,23 @@ BEGIN_TEST(testProfileStrings_isCalledWi
         /* Make sure the stack resets and we added no new entries */
         uint32_t cnt = cx->runtime->spsProfiler.stringsCount();
         max_stack = 0;
         CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
         CHECK(size == 0);
         CHECK(cx->runtime->spsProfiler.stringsCount() == cnt);
         CHECK(max_stack == 9);
     }
+
+    js::EnableRuntimeProfilingStack(cx->runtime, false);
+    js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 3);
     reset(cx);
     {
         /* Limit the size of the stack and make sure we don't overflow */
         jsvalRoot rval(cx);
-        js::SetRuntimeProfilingStack(cx->runtime, stack, &size, 3);
         stack[3].string = (char*) 1234;
         CHECK(JS_CallFunctionName(cx, global, "check", 0, NULL, rval.addr()));
         CHECK(size == 0);
         CHECK(max_stack == 9);
         CHECK((size_t) stack[3].string == 1234);
     }
     return true;
 }
@@ -192,24 +205,58 @@ BEGIN_TEST(testProfileStrings_isCalledWh
     return true;
 }
 END_TEST(testProfileStrings_isCalledWhenError)
 
 BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
 {
     CHECK(initialize(cx));
     JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_METHODJIT |
-                        JSOPTION_METHODJIT_ALWAYS);
+                      JSOPTION_METHODJIT_ALWAYS);
 
-    EXEC("function b() { }");
-    EXEC("function a() { var p = new Prof(); p.test_fn3(); b(); }");
-
+    EXEC("function b(p) { p.test_fn(); }");
+    EXEC("function a() { var p = new Prof(); p.enable(); b(p); }");
     reset(cx);
-    js::SetRuntimeProfilingStack(cx->runtime, NULL, NULL, 10);
+    js::EnableRuntimeProfilingStack(cx->runtime, false);
     {
+        /* enable it in the middle of JS and make sure things check out */
         jsvalRoot rval(cx);
         JS_CallFunctionName(cx, global, "a", 0, NULL, rval.addr());
         CHECK(size == 0);
+        CHECK(max_stack == 1);
         CHECK(cx->runtime->spsProfiler.stringsCount() == 1);
     }
+
+    EXEC("function d(p) { p.disable(); }");
+    EXEC("function c() { var p = new Prof(); d(p); }");
+    reset(cx);
+    {
+        /* now disable in the middle of js */
+        jsvalRoot rval(cx);
+        JS_CallFunctionName(cx, global, "c", 0, NULL, rval.addr());
+        CHECK(size == 0);
+    }
+
+    EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }");
+    reset(cx);
+    {
+        /* now disable in the middle of js, but re-enable before final exit */
+        jsvalRoot rval(cx);
+        JS_CallFunctionName(cx, global, "e", 0, NULL, rval.addr());
+        CHECK(size == 0);
+        CHECK(max_stack == 3);
+    }
+
+    EXEC("function h() { }");
+    EXEC("function g(p) { p.disable(); for (var i = 0; i < 100; i++) i++; }");
+    EXEC("function f() { g(new Prof()); }");
+    reset(cx);
+    cx->runtime->spsProfiler.enableSlowAssertions(false);
+    {
+        jsvalRoot rval(cx);
+        /* disable, and make sure that if we try to re-enter the JIT the pop
+         * will still happen */
+        JS_CallFunctionName(cx, global, "f", 0, NULL, rval.addr());
+        CHECK(size == 0);
+    }
     return true;
 }
 END_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -727,18 +727,28 @@ JSRuntime::JSRuntime()
     gcKeepAtoms(0),
     gcBytes(0),
     gcMaxBytes(0),
     gcMaxMallocBytes(0),
     gcNumArenasFreeCommitted(0),
     gcVerifyData(NULL),
     gcChunkAllocationSinceLastGC(false),
     gcNextFullGCTime(0),
+    gcLastGCTime(0),
     gcJitReleaseTime(0),
     gcMode(JSGC_MODE_GLOBAL),
+    gcHighFrequencyGC(false),
+    gcHighFrequencyTimeThreshold(1000),
+    gcHighFrequencyLowLimitBytes(100 * 1024 * 1024),
+    gcHighFrequencyHighLimitBytes(500 * 1024 * 1024),
+    gcHighFrequencyHeapGrowthMax(3.0),
+    gcHighFrequencyHeapGrowthMin(1.5),
+    gcLowFrequencyHeapGrowth(1.5),
+    gcDynamicHeapGrowth(false),
+    gcDynamicMarkSlice(false),
     gcShouldCleanUpEverything(false),
     gcIsNeeded(0),
     gcWeakMapList(NULL),
     gcStats(thisFromCtor()),
     gcNumber(0),
     gcStartNumber(0),
     gcIsFull(false),
     gcTriggerReason(gcreason::NO_REASON),
@@ -769,16 +779,17 @@ JSRuntime::JSRuntime()
     gcGrayRootsData(NULL),
     autoGCRooters(NULL),
     scriptAndCountsVector(NULL),
     NaNValue(UndefinedValue()),
     negativeInfinityValue(UndefinedValue()),
     positiveInfinityValue(UndefinedValue()),
     emptyString(NULL),
     debugMode(false),
+    spsProfiler(thisFromCtor()),
     profilingScripts(false),
     alwaysPreserveCode(false),
     hadOutOfMemory(false),
     debugScopes(NULL),
     data(NULL),
     gcLock(NULL),
     gcHelperThread(thisFromCtor()),
     defaultFreeOp_(thisFromCtor(), false, false),
@@ -2928,16 +2939,40 @@ JS_SetGCParameter(JSRuntime *rt, JSGCPar
         rt->setGCMaxMallocBytes(value);
         break;
       case JSGC_SLICE_TIME_BUDGET:
         rt->gcSliceBudget = SliceBudget::TimeBudget(value);
         break;
       case JSGC_MARK_STACK_LIMIT:
         js::SetMarkStackLimit(rt, value);
         break;
+      case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
+        rt->gcHighFrequencyTimeThreshold = value;
+        break;
+      case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
+        rt->gcHighFrequencyLowLimitBytes = value * 1024 * 1024;
+        break;
+      case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
+        rt->gcHighFrequencyHighLimitBytes = value * 1024 * 1024;
+        break;
+      case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
+        rt->gcHighFrequencyHeapGrowthMax = value / 100.0;
+        break;
+      case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
+        rt->gcHighFrequencyHeapGrowthMin = value / 100.0;
+        break;
+      case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
+        rt->gcLowFrequencyHeapGrowth = value / 100.0;
+        break;
+      case JSGC_DYNAMIC_HEAP_GROWTH:
+        rt->gcDynamicHeapGrowth = value;
+        break;
+      case JSGC_DYNAMIC_MARK_SLICE:
+        rt->gcDynamicMarkSlice = value;
+        break;
       default:
         JS_ASSERT(key == JSGC_MODE);
         rt->gcMode = JSGCMode(value);
         JS_ASSERT(rt->gcMode == JSGC_MODE_GLOBAL ||
                   rt->gcMode == JSGC_MODE_COMPARTMENT ||
                   rt->gcMode == JSGC_MODE_INCREMENTAL);
         return;
     }
@@ -2958,16 +2993,32 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
       case JSGC_UNUSED_CHUNKS:
         return uint32_t(rt->gcChunkPool.getEmptyCount());
       case JSGC_TOTAL_CHUNKS:
         return uint32_t(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount());
       case JSGC_SLICE_TIME_BUDGET:
         return uint32_t(rt->gcSliceBudget > 0 ? rt->gcSliceBudget / PRMJ_USEC_PER_MSEC : 0);
       case JSGC_MARK_STACK_LIMIT:
         return rt->gcMarker.sizeLimit();
+      case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
+        return rt->gcHighFrequencyTimeThreshold;
+      case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
+        return rt->gcHighFrequencyLowLimitBytes / 1024 / 1024;
+      case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
+        return rt->gcHighFrequencyHighLimitBytes / 1024 / 1024;
+      case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
+        return uint32_t(rt->gcHighFrequencyHeapGrowthMax * 100);
+      case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
+        return uint32_t(rt->gcHighFrequencyHeapGrowthMin * 100);
+      case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
+        return uint32_t(rt->gcLowFrequencyHeapGrowth * 100);
+      case JSGC_DYNAMIC_HEAP_GROWTH:
+        return rt->gcDynamicHeapGrowth;
+      case JSGC_DYNAMIC_MARK_SLICE:
+        return rt->gcDynamicMarkSlice;
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return uint32_t(rt->gcNumber);
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3712,17 +3712,47 @@ typedef enum JSGCParamKey {
 
     /* Total number of allocated GC chunks. */
     JSGC_TOTAL_CHUNKS = 8,
 
     /* Max milliseconds to spend in an incremental GC slice. */
     JSGC_SLICE_TIME_BUDGET = 9,
 
     /* Maximum size the GC mark stack can grow to. */
-    JSGC_MARK_STACK_LIMIT = 10
+    JSGC_MARK_STACK_LIMIT = 10,
+
+    /*
+     * GCs less than this far apart in time will be considered 'high-frequency GCs'.
+     * See setGCLastBytes in jsgc.cpp.
+     */
+    JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
+
+    /* Start of dynamic heap growth. */
+    JSGC_HIGH_FREQUENCY_LOW_LIMIT = 12,
+
+    /* End of dynamic heap growth. */
+    JSGC_HIGH_FREQUENCY_HIGH_LIMIT = 13,
+
+    /* Upper bound of heap growth. */
+    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX = 14,
+
+    /* Lower bound of heap growth. */
+    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN = 15,
+
+    /* Heap growth for low frequency GCs. */
+    JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16,
+
+    /*
+     * If false, the heap growth factor is fixed at 3. If true, it is determined
+     * based on whether GCs are high- or low- frequency.
+     */
+    JSGC_DYNAMIC_HEAP_GROWTH = 17,
+
+    /* If true, high-frequency GCs will use a longer mark slice. */
+    JSGC_DYNAMIC_MARK_SLICE = 18
 } JSGCParamKey;
 
 typedef enum JSGCMode {
     /* Perform only global GCs. */
     JSGC_MODE_GLOBAL = 0,
 
     /* Perform per-compartment GCs until too much garbage has accumulated. */
     JSGC_MODE_COMPARTMENT = 1,
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -487,18 +487,28 @@ struct JSRuntime : js::RuntimeFriendFiel
      * The counter is volatile as it is read without the GC lock, see comments
      * in MaybeGC.
      */
     volatile uint32_t   gcNumArenasFreeCommitted;
     js::GCMarker        gcMarker;
     void                *gcVerifyData;
     bool                gcChunkAllocationSinceLastGC;
     int64_t             gcNextFullGCTime;
+    int64_t             gcLastGCTime;
     int64_t             gcJitReleaseTime;
     JSGCMode            gcMode;
+    bool                gcHighFrequencyGC;
+    uint64_t            gcHighFrequencyTimeThreshold;
+    uint64_t            gcHighFrequencyLowLimitBytes;
+    uint64_t            gcHighFrequencyHighLimitBytes;
+    double              gcHighFrequencyHeapGrowthMax;
+    double              gcHighFrequencyHeapGrowthMin;
+    double              gcLowFrequencyHeapGrowth;
+    bool                gcDynamicHeapGrowth;
+    bool                gcDynamicMarkSlice;
 
     /* During shutdown, the GC needs to clean up every possible object. */
     bool                gcShouldCleanUpEverything;
 
     /*
      * These flags must be kept separate so that a thread requesting a
      * compartment GC doesn't cancel another thread's concurrent request for a
      * full GC.
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -41,16 +41,17 @@ JSCompartment::JSCompartment(JSRuntime *
   : rt(rt),
     principals(NULL),
     global_(NULL),
     needsBarrier_(false),
     gcState(NoGCScheduled),
     gcPreserveCode(false),
     gcBytes(0),
     gcTriggerBytes(0),
+    gcHeapGrowthFactor(3.0),
     hold(false),
     isSystemCompartment(false),
     lastCodeRelease(0),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     data(NULL),
     active(false),
     lastAnimationTime(0),
     regExps(rt),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -218,16 +218,17 @@ struct JSCompartment
 
     void setPreservingCode(bool preserving) {
         gcPreserveCode = preserving;
     }
 
     size_t                       gcBytes;
     size_t                       gcTriggerBytes;
     size_t                       gcMaxMallocBytes;
+    double                       gcHeapGrowthFactor;
 
     bool                         hold;
     bool                         isSystemCompartment;
 
     int64_t                      lastCodeRelease;
 
     /*
      * Pool for analysis and intermediate type information in this compartment.
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -866,12 +866,17 @@ GetTestingFunctions(JSContext *cx)
     return obj;
 }
 
 JS_FRIEND_API(void)
 SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
                          uint32_t max)
 {
     rt->spsProfiler.setProfilingStack(stack, size, max);
-    ReleaseAllJITCode(rt->defaultFreeOp());
+}
+
+JS_FRIEND_API(void)
+EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled)
+{
+    rt->spsProfiler.enable(enabled);
 }
 
 } // namespace js
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -544,16 +544,19 @@ struct ProfileEntry {
     const char * volatile string;
     void * volatile sp;
 };
 
 JS_FRIEND_API(void)
 SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
                          uint32_t max);
 
+JS_FRIEND_API(void)
+EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
+
 #ifdef JS_THREADSAFE
 JS_FRIEND_API(void *)
 GetOwnerThread(const JSContext *cx);
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx);
 #endif
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -107,27 +107,22 @@ using namespace js::gc;
 namespace js {
 namespace gc {
 
 /*
  * Lower limit after which we limit the heap growth
  */
 const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024;
 
-/*
- * A GC is triggered once the number of newly allocated arenas is
- * GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC
- * starting after the lower limit of GC_ALLOCATION_THRESHOLD. This number is
- * used for non-incremental GCs.
- */
-const float GC_HEAP_GROWTH_FACTOR = 3.0f;
-
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
+/* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
+const int IGC_MARK_SLICE_MULTIPLIER = 2;
+
 #ifdef JS_GC_ZEAL
 static void
 StartVerifyBarriers(JSRuntime *rt);
 
 static void
 EndVerifyBarriers(JSRuntime *rt);
 
 void
@@ -522,16 +517,31 @@ ChunkPool::expireAndFree(JSRuntime *rt, 
 {
     FreeChunkList(expire(rt, releaseAll));
 }
 
 /* static */ Chunk *
 Chunk::allocate(JSRuntime *rt)
 {
     Chunk *chunk = static_cast<Chunk *>(AllocChunk());
+
+#ifdef JSGC_ROOT_ANALYSIS
+    // Our poison pointers are not guaranteed to be invalid on 64-bit
+    // architectures, and often are valid. We can't just reserve the full
+    // poison range, because it might already have been taken up by something
+    // else (shared library, previous allocation). So we'll just loop and
+    // discard poison pointers until we get something valid.
+    //
+    // This leaks all of these poisoned pointers. It would be better if they
+    // were marked as uncommitted, but it's a little complicated to avoid
+    // clobbering pre-existing unrelated mappings.
+    while (IsPoisonedPtr(chunk))
+        chunk = static_cast<Chunk *>(AllocChunk());
+#endif
+
     if (!chunk)
         return NULL;
     chunk->init();
     rt->gcStats.count(gcstats::STAT_NEW_CHUNK);
     return chunk;
 }
 
 /* Must be called with the GC lock taken. */
@@ -736,17 +746,17 @@ Chunk::releaseArena(ArenaHeader *aheader
     AutoLockGC maybeLock;
     if (rt->gcHelperThread.sweeping())
         maybeLock.lock(rt);
 
     Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes - ArenaSize);
     JS_ASSERT(rt->gcBytes >= ArenaSize);
     JS_ASSERT(comp->gcBytes >= ArenaSize);
     if (rt->gcHelperThread.sweeping())
-        comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
+        comp->reduceGCTriggerBytes(comp->gcHeapGrowthFactor * ArenaSize);
     rt->gcBytes -= ArenaSize;
     comp->gcBytes -= ArenaSize;
 
     aheader->setAsNotAllocated();
     addArenaToFreeList(rt, aheader);
 
     if (info.numArenasFree == 1) {
         JS_ASSERT(!info.prevp);
@@ -1322,36 +1332,72 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMap
         if (mapflags & JS_MAP_GCROOT_STOP)
             break;
     }
 
     return ct;
 }
 
 static size_t
-ComputeTriggerBytes(size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
+ComputeTriggerBytes(JSCompartment *comp, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
 {
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
-    float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
+    float trigger = float(base) * comp->gcHeapGrowthFactor;
     return size_t(Min(float(maxBytes), trigger));
 }
 
 void
 JSCompartment::setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, JSGCInvocationKind gckind)
 {
-    gcTriggerBytes = ComputeTriggerBytes(lastBytes, rt->gcMaxBytes, gckind);
-    gcTriggerMallocAndFreeBytes = ComputeTriggerBytes(lastMallocBytes, SIZE_MAX, gckind);
+    /*
+     * The heap growth factor depends on the heap size after a GC and the GC frequency.
+     * For low frequency GCs (more than 1sec between GCs) we let the heap grow to 150%.
+     * For high frequency GCs we let the heap grow depending on the heap size:
+     *   lastBytes < highFrequencyLowLimit: 300%
+     *   lastBytes > highFrequencyHighLimit: 150%
+     *   otherwise: linear interpolation between 150% and 300% based on lastBytes
+     */
+
+    if (!rt->gcDynamicHeapGrowth) {
+        gcHeapGrowthFactor = 3.0;
+    } else if (lastBytes < 1 * 1024 * 1024) {
+        gcHeapGrowthFactor = rt->gcLowFrequencyHeapGrowth;
+    } else {
+        JS_ASSERT(rt->gcHighFrequencyHighLimitBytes > rt->gcHighFrequencyLowLimitBytes);
+        uint64_t now = PRMJ_Now();
+        if (rt->gcLastGCTime && rt->gcLastGCTime + rt->gcHighFrequencyTimeThreshold * PRMJ_USEC_PER_MSEC > now) {
+            if (lastBytes <= rt->gcHighFrequencyLowLimitBytes) {
+                gcHeapGrowthFactor = rt->gcHighFrequencyHeapGrowthMax;
+            } else if (lastBytes >= rt->gcHighFrequencyHighLimitBytes) {
+                gcHeapGrowthFactor = rt->gcHighFrequencyHeapGrowthMin;
+            } else {
+                double k = (rt->gcHighFrequencyHeapGrowthMin - rt->gcHighFrequencyHeapGrowthMax)
+                           / (double)(rt->gcHighFrequencyHighLimitBytes - rt->gcHighFrequencyLowLimitBytes);
+                gcHeapGrowthFactor = (k * (lastBytes - rt->gcHighFrequencyLowLimitBytes)
+                                     + rt->gcHighFrequencyHeapGrowthMax);
+                JS_ASSERT(gcHeapGrowthFactor <= rt->gcHighFrequencyHeapGrowthMax
+                          && gcHeapGrowthFactor >= rt->gcHighFrequencyHeapGrowthMin);
+                
+            }
+            rt->gcHighFrequencyGC = true;
+        } else {
+            gcHeapGrowthFactor = rt->gcLowFrequencyHeapGrowth;
+            rt->gcHighFrequencyGC = false;
+        }
+    }
+    gcTriggerBytes = ComputeTriggerBytes(this, lastBytes, rt->gcMaxBytes, gckind);
+    gcTriggerMallocAndFreeBytes = ComputeTriggerBytes(this, lastMallocBytes, SIZE_MAX, gckind);
 }
 
 void
 JSCompartment::reduceGCTriggerBytes(size_t amount)
 {
     JS_ASSERT(amount > 0);
     JS_ASSERT(gcTriggerBytes >= amount);
-    if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
+    if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * gcHeapGrowthFactor)
         return;
     gcTriggerBytes -= amount;
 }
 
 namespace js {
 namespace gc {
 
 inline void
@@ -2473,20 +2519,21 @@ MaybeGC(JSContext *cx)
         GC(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
     if (rt->gcIsNeeded) {
         GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
+    double factor = rt->gcHighFrequencyGC ? 0.75 : 0.9;
 
     JSCompartment *comp = cx->compartment;
-    if (comp->gcBytes > 8192 &&
-        comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4) &&
+    if (comp->gcBytes > 1024 * 1024 &&
+        comp->gcBytes >= factor * comp->gcTriggerBytes &&
         rt->gcIncrementalState == NO_INCREMENTAL &&
         !rt->gcHelperThread.sweeping())
     {
         PrepareCompartmentForGC(comp);
         GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
@@ -3423,16 +3470,17 @@ SweepPhase(JSRuntime *rt, JSGCInvocation
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_END);
         if (rt->gcFinalizeCallback)
             rt->gcFinalizeCallback(&fop, JSFINALIZE_END, !isFull);
     }
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->setGCLastBytes(c->gcBytes, c->gcMallocAndFreeBytes, gckind);
+    rt->gcLastGCTime = PRMJ_Now();
 }
 
 /*
  * This class should be used by any code that needs to exclusive access to the
  * heap in order to trace through it...
  */
 class AutoTraceSession {
   public:
@@ -3932,17 +3980,20 @@ void
 GC(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason)
 {
     Collect(rt, false, SliceBudget::Unlimited, gckind, reason);
 }
 
 void
 GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason)
 {
-    Collect(rt, true, rt->gcSliceBudget, gckind, reason);
+    int sliceBudget = rt->gcHighFrequencyGC && rt->gcDynamicMarkSlice
+                      ? rt->gcSliceBudget * IGC_MARK_SLICE_MULTIPLIER
+                      : rt->gcSliceBudget;
+    Collect(rt, true, sliceBudget, gckind, reason);
 }
 
 void
 GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason)
 {
     Collect(rt, true, SliceBudget::Unlimited, gckind, reason);
 }
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -285,17 +285,17 @@ js::RunScript(JSContext *cx, JSScript *s
     } check(cx);
 #endif
 
     SPSEntryMarker marker(cx->runtime);
 
 #ifdef JS_METHODJIT
     mjit::CompileStatus status;
     status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(),
-                                mjit::CompileRequest_Interpreter);
+                                mjit::CompileRequest_Interpreter, fp);
     if (status == mjit::Compile_Error)
         return false;
 
     if (status == mjit::Compile_Okay)
         return mjit::JaegerStatusToSuccess(mjit::JaegerShot(cx, false));
 #endif
 
     return Interpret(cx, fp);
@@ -1483,17 +1483,17 @@ check_backedge:
     if (op != JSOP_LOOPHEAD)
         DO_OP();
 
 #ifdef JS_METHODJIT
     if (!useMethodJIT)
         DO_OP();
     mjit::CompileStatus status =
         mjit::CanMethodJIT(cx, script, regs.pc, regs.fp()->isConstructing(),
-                           mjit::CompileRequest_Interpreter);
+                           mjit::CompileRequest_Interpreter, regs.fp());
     if (status == mjit::Compile_Error)
         goto error;
     if (status == mjit::Compile_Okay) {
         void *ncode =
             script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);
         JS_ASSERT(ncode);
         mjit::JaegerStatus status = mjit::JaegerShotAtSafePoint(cx, ncode, true);
         if (status == mjit::Jaeger_ThrowBeforeEnter)
@@ -2470,17 +2470,18 @@ BEGIN_CASE(JSOP_FUNCALL)
 
     bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
 
 #ifdef JS_METHODJIT
     if (!newType) {
         /* Try to ensure methods are method JIT'd.  */
         mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, script->code,
                                                         construct,
-                                                        mjit::CompileRequest_Interpreter);
+                                                        mjit::CompileRequest_Interpreter,
+                                                        regs.fp());
         if (status == mjit::Compile_Error)
             goto error;
         if (status == mjit::Compile_Okay) {
             mjit::JaegerStatus status = mjit::JaegerShot(cx, true);
             CHECK_PARTIAL_METHODJIT(status);
             interpReturnOK = mjit::JaegerStatusToSuccess(status);
             CHECK_INTERRUPT_HANDLER();
             goto jit_return;
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -420,17 +420,17 @@ Probes::exitScript(JSContext *cx, JSScri
     cx->doFunctionCallback(maybeFun, script, 0);
 #endif
 #ifdef MOZ_ETW
     if (ProfilingActive && !ETWExitJSFun(cx, maybeFun, script, 0))
         ok = false;
 #endif
 
     JSRuntime *rt = cx->runtime;
-    if (rt->spsProfiler.enabled() && fp->hasPushedSPSFrame())
+    if (fp->hasPushedSPSFrame())
         rt->spsProfiler.exit(cx, script, maybeFun);
     return ok;
 }
 
 inline bool
 Probes::resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize)
 {
     bool ok = true;
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -75,21 +75,16 @@ Wrapper::Wrapper(unsigned flags) : mFlag
 
 bool
 Wrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
 {
     *bp = true;
     return true;
 }
 
-void
-Wrapper::leave(JSContext *cx, JSObject *wrapper)
-{
-}
-
 JS_FRIEND_API(JSObject *)
 js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
 {
     unsigned flags = 0;
     while (wrapped->isWrapper() &&
            !JS_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject)) {
         flags |= Wrapper::wrapperHandler(wrapped)->flags();
         wrapped = GetProxyPrivate(wrapped).toObjectOrNull();
@@ -107,17 +102,16 @@ js::UnwrapObjectChecked(JSContext *cx, J
         JSObject *wrapper = obj;
         Wrapper *handler = Wrapper::wrapperHandler(obj);
         bool rvOnFailure;
         if (!handler->enter(cx, wrapper, JSID_VOID,
                             Wrapper::PUNCTURE, &rvOnFailure))
             return rvOnFailure ? obj : NULL;
         obj = Wrapper::wrappedObject(obj);
         JS_ASSERT(obj);
-        handler->leave(cx, wrapper);
     }
     return obj;
 }
 
 bool
 js::IsCrossCompartmentWrapper(const JSObject *wrapper)
 {
     return wrapper->isWrapper() &&
@@ -129,19 +123,17 @@ IndirectWrapper::IndirectWrapper(unsigne
 {
 }
 
 #define CHECKED(op, act)                                                     \
     JS_BEGIN_MACRO                                                           \
         bool status;                                                         \
         if (!enter(cx, wrapper, id, act, &status))                           \
             return status;                                                   \
-        bool ok = (op);                                                      \
-        leave(cx, wrapper);                                                  \
-        return ok;                                                           \
+        return (op);                                                         \
     JS_END_MACRO
 
 #define SET(action) CHECKED(action, SET)
 #define GET(action) CHECKED(action, GET)
 
 bool
 IndirectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
                                        jsid id, bool set,
@@ -333,17 +325,16 @@ DirectWrapper::obj_toString(JSContext *c
     if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
         if (status) {
             // Perform some default behavior that doesn't leak any information.
             return JS_NewStringCopyZ(cx, "[object Object]");
         }
         return NULL;
     }
     JSString *str = IndirectProxyHandler::obj_toString(cx, wrapper);
-    leave(cx, wrapper);
     return str;
 }
 
 JSString *
 DirectWrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent)
 {
     bool status;
     if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
@@ -352,17 +343,16 @@ DirectWrapper::fun_toString(JSContext *c
             if (wrapper->isCallable())
                 return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
             ReportIsNotFunction(cx, ObjectValue(*wrapper));
             return NULL;
         }
         return NULL;
     }
     JSString *str = IndirectProxyHandler::fun_toString(cx, wrapper, indent);
-    leave(cx, wrapper);
     return str;
 }
 
 DirectWrapper DirectWrapper::singleton((unsigned)0);
 
 /* Compartments. */
 
 namespace js {
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -89,20 +89,16 @@ class JS_FRIEND_API(Wrapper)
     virtual BaseProxyHandler *toBaseProxyHandler() = 0;
 
     /* Policy enforcement traps.
      *
      * enter() allows the policy to specify whether the caller may perform |act|
      * on the underlying object's |id| property. In the case when |act| is CALL,
      * |id| is generally JSID_VOID.
      *
-     * leave() allows the policy to undo various scoped state changes taken in
-     * enter(). If enter() succeeds, leave() must be called upon completion of
-     * the approved action.
-     *
      * The |act| parameter to enter() specifies the action being performed. GET,
      * SET, and CALL are self-explanatory, but PUNCTURE requires more
      * explanation:
      *
      * GET and SET allow for a very fine-grained security membrane, through
      * which access can be granted or denied on a per-property, per-object, and
      * per-action basis. Sometimes though, we just want to asks if we can access
      * _everything_ behind the wrapper barrier. For example, when the structured
@@ -114,18 +110,16 @@ class JS_FRIEND_API(Wrapper)
      * be lifted - that is to say, whether the caller is allowed to access
      * anything that the wrapped object could access. This is a very powerful
      * permission, and thus should generally be denied for security wrappers
      * except under very special circumstances. When |act| is PUNCTURE, |id|
      * should be JSID_VOID.
      */
     virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act,
                        bool *bp);
-
-    virtual void leave(JSContext *cx, JSObject *wrapper);
 };
 
 /*
  * IndirectWrapper forwards its traps by forwarding them to
  * IndirectProxyHandler. In effect, IndirectWrapper behaves the same as
  * IndirectProxyHandler, except that it adds policy enforcement checks to each
  * fundamental trap.
  */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -906,22 +906,25 @@ MakeJITScript(JSContext *cx, JSScript *s
         edge.shimLabel = shimCode + (size_t) edge.shimLabel;
     }
 
     return jit;
 }
 
 CompileStatus
 mjit::CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
-                   bool construct, CompileRequest request)
+                   bool construct, CompileRequest request, StackFrame *frame)
 {
   restart:
     if (!cx->methodJitEnabled)
         return Compile_Abort;
 
+    if (frame->hasPushedSPSFrame())
+        return Compile_Skipped;
+
     if (script->hasJITInfo()) {
         JSScript::JITScriptHandle *jith = script->jitHandle(construct, cx->compartment->needsBarrier());
         if (jith->isUnjittable())
             return Compile_Abort;
     }
 
     if (!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
         (cx->typeInferenceEnabled()
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -254,17 +254,18 @@ UncachedInlineCall(VMFrame &f, InitialFr
 
     bool newType = construct && cx->typeInferenceEnabled() &&
         types::UseNewType(cx, f.script(), f.pc());
 
     if (!types::TypeMonitorCall(cx, args, construct))
         return false;
 
     /* Try to compile if not already compiled. */
-    CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct, CompileRequest_Interpreter);
+    CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct,
+                                        CompileRequest_Interpreter, f.fp());
     if (status == Compile_Error) {
         /* A runtime exception was thrown, get out. */
         return false;
     }
     if (status == Compile_Abort)
         *unjittable = true;
 
     /*
@@ -652,17 +653,17 @@ stubs::CrossChunkShim(VMFrame &f, void *
 
     mjit::ExpandInlineFrames(f.cx->compartment);
 
     JSScript *script = f.script();
     JS_ASSERT(edge->target < script->length);
     JS_ASSERT(script->code + edge->target == f.pc());
 
     CompileStatus status = CanMethodJIT(f.cx, script, f.pc(), f.fp()->isConstructing(),
-                                        CompileRequest_Interpreter);
+                                        CompileRequest_Interpreter, f.fp());
     if (status == Compile_Error)
         THROW();
 
     void **addr = f.returnAddressLocation();
     *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
 
     f.fp()->setRejoin(StubRejoin(REJOIN_RESUME));
 }
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -860,17 +860,17 @@ ProfileStubCall(VMFrame &f);
 enum CompileRequest
 {
     CompileRequest_Interpreter,
     CompileRequest_JIT
 };
 
 CompileStatus
 CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
-             bool construct, CompileRequest request);
+             bool construct, CompileRequest request, StackFrame *sp);
 
 inline void
 ReleaseScriptCode(FreeOp *fop, JSScript *script)
 {
     if (!script->hasJITInfo())
         return;
 
     for (int constructing = 0; constructing <= 1; constructing++) {
--- a/js/src/shell/Makefile.in
+++ b/js/src/shell/Makefile.in
@@ -18,17 +18,17 @@ CPPSRCS		= \
   jsheaptools.cpp \
   $(NULL)
 
 DEFINES         += -DEXPORT_JS_API
 # Building against js_static requires that we declare mfbt sybols "exported"
 # on its behalf.
 DEFINES         += -DIMPL_MFBT
 
-LIBS      = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
+LIBS      = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX) $(MOZ_ZLIB_LIBS)
 ifdef MOZ_NATIVE_FFI
 EXTRA_LIBS += $(MOZ_FFI_LIBS)
 endif
 
 LOCAL_INCLUDES += -I$(topsrcdir) -I..
 
 ifeq ($(OS_ARCH),Darwin)
 ifeq ($(TARGET_CPU),x86_64)
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -18,28 +18,40 @@ SPSProfiler::~SPSProfiler()
         for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront())
             js_free((void*) e.front().value);
     }
 }
 
 void
 SPSProfiler::setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max)
 {
+    JS_ASSERT(!enabled());
     if (!strings.initialized())
         strings.init(max);
     stack_ = stack;
     size_  = size;
     max_   = max;
 }
 
+void
+SPSProfiler::enable(bool enabled)
+{
+    JS_ASSERT(installed());
+    enabled_ = enabled;
+    /*
+     * Ensure all future generated code will be instrumented, or that all
+     * currently instrumented code is discarded
+     */
+    ReleaseAllJITCode(rt->defaultFreeOp());
+}
+
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
 SPSProfiler::profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun)
 {
-    JS_ASSERT(enabled());
     JS_ASSERT(strings.initialized());
     ProfileStringMap::AddPtr s = strings.lookupForAdd(script);
     if (s)
         return s->value;
     const char *str = allocProfileString(cx, script, maybeFun);
     if (str == NULL)
         return NULL;
     if (!strings.add(s, script, str)) {
@@ -66,49 +78,61 @@ SPSProfiler::onScriptFinalized(JSScript 
         strings.remove(entry);
         js_free((void*) tofree);
     }
 }
 
 bool
 SPSProfiler::enter(JSContext *cx, JSScript *script, JSFunction *maybeFun)
 {
-    JS_ASSERT(enabled());
     const char *str = profileString(cx, script, maybeFun);
     if (str == NULL)
         return false;
 
-    if (*size_ < max_) {
-        stack_[*size_].string = str;
-        stack_[*size_].sp     = NULL;
-    }
-    (*size_)++;
+    push(str, NULL);
     return true;
 }
 
 void
 SPSProfiler::exit(JSContext *cx, JSScript *script, JSFunction *maybeFun)
 {
-    JS_ASSERT(enabled());
-    (*size_)--;
-    JS_ASSERT(*(int*)size_ >= 0);
+    pop();
 
 #ifdef DEBUG
     /* Sanity check to make sure push/pop balanced */
     if (*size_ < max_) {
         const char *str = profileString(cx, script, maybeFun);
         /* Can't fail lookup because we should already be in the set */
         JS_ASSERT(str != NULL);
         JS_ASSERT(strcmp((const char*) stack_[*size_].string, str) == 0);
         stack_[*size_].string = NULL;
         stack_[*size_].sp     = NULL;
     }
 #endif
 }
 
+void
+SPSProfiler::push(const char *string, void *sp)
+{
+    JS_ASSERT(enabled());
+    if (*size_ < max_) {
+        stack_[*size_].string = string;
+        stack_[*size_].sp = sp;
+    }
+    (*size_)++;
+}
+
+void
+SPSProfiler::pop()
+{
+    JS_ASSERT(installed());
+    (*size_)--;
+    JS_ASSERT(*(int*)size_ >= 0);
+}
+
 /*
  * Serializes the script/function pair into a "descriptive string" which is
  * allowed to fail. This function cannot trigger a GC because it could finalize
  * some scripts, resize the hash table of profile strings, and invalidate the
  * AddPtr held while invoking allocProfileString.
  */
 const char*
 SPSProfiler::allocProfileString(JSContext *cx, JSScript *script, JSFunction *maybeFun)
@@ -144,29 +168,24 @@ SPSProfiler::allocProfileString(JSContex
     for (size_t i = 0; i < len; i++)
         cstr[i] = ptr[i];
     cstr[len] = 0;
 
     JS_ASSERT(gcBefore == cx->runtime->gcNumber);
     return cstr;
 }
 
-SPSEntryMarker::SPSEntryMarker(JSRuntime *rt) : profiler(&rt->spsProfiler), pushed(false)
+SPSEntryMarker::SPSEntryMarker(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
+    : profiler(&rt->spsProfiler)
 {
-    if (!profiler->enabled())
+    JS_GUARD_OBJECT_NOTIFIER_INIT;
+    if (!profiler->enabled()) {
+        profiler = NULL;
         return;
-    uint32_t *size = profiler->size_;
-    size_before = *size;
-    if (*size < profiler->max_) {
-        profiler->stack_[*size].string = "js::RunScript";
-        profiler->stack_[*size].sp = this;
     }
-    (*size)++;
-    pushed = true;
+    profiler->push("js::RunScript", this);
 }
 
 SPSEntryMarker::~SPSEntryMarker()
 {
-    if (!pushed || !profiler->enabled())
-        return;
-    (*profiler->size_)--;
-    JS_ASSERT(*profiler->size_ == size_before);
+    if (profiler != NULL)
+        profiler->pop();
 }
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -88,33 +88,47 @@ typedef HashMap<JSScript*, const char*, 
         ProfileStringMap;
 
 class SPSEntryMarker;
 
 class SPSProfiler
 {
     friend class SPSEntryMarker;
 
+    JSRuntime            *rt;
     ProfileStringMap     strings;
     ProfileEntry         *stack_;
     uint32_t             *size_;
     uint32_t             max_;
     bool                 slowAssertions;
+    bool                 enabled_;
 
     static const char *allocProfileString(JSContext *cx, JSScript *script,
                                           JSFunction *function);
+    void push(const char *string, void *sp);
+    void pop();
+
   public:
-    SPSProfiler() : stack_(NULL), size_(NULL), max_(0), slowAssertions(false) {}
+    SPSProfiler(JSRuntime *rt)
+        : rt(rt),
+          stack_(NULL),
+          size_(NULL),
+          max_(0),
+          slowAssertions(false),
+          enabled_(false)
+    {}
     ~SPSProfiler();
 
     uint32_t *size() { return size_; }
     uint32_t maxSize() { return max_; }
     ProfileEntry *stack() { return stack_; }
 
-    bool enabled() { return stack_ != NULL; }
+    bool enabled() { JS_ASSERT_IF(enabled_, installed()); return enabled_; }
+    bool installed() { return stack_ != NULL && size_ != NULL; }
+    void enable(bool enabled);
     void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
     bool slowAssertionsEnabled() { return slowAssertions; }
     bool enter(JSContext *cx, JSScript *script, JSFunction *maybeFun);
     void exit(JSContext *cx, JSScript *script, JSFunction *maybeFun);
     void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
     const char *profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun);
     void onScriptFinalized(JSScript *script);
 
@@ -126,19 +140,17 @@ class SPSProfiler
 /*
  * This class is used in RunScript() to push the marker onto the sampling stack
  * that we're about to enter JS function calls. This is the only time in which a
  * valid stack pointer is pushed to the sampling stack.
  */
 class SPSEntryMarker
 {
     SPSProfiler *profiler;
-    bool pushed;
-    DebugOnly<uint32_t> size_before;
-
+    JS_DECL_USE_GUARD_OBJECT_NOTIFIER;
   public:
-    SPSEntryMarker(JSRuntime *rt);
+    SPSEntryMarker(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM);
     ~SPSEntryMarker();
 };
 
 } /* namespace js */
 
 #endif /* SPSProfiler_h__ */
--- a/js/xpconnect/tests/chrome/Makefile.in
+++ b/js/xpconnect/tests/chrome/Makefile.in
@@ -45,16 +45,17 @@ MOCHITEST_CHROME_FILES = \
 		test_weakmaps.xul \
 		test_exnstack.xul \
 		test_weakref.xul \
 		test_bug726949.xul \
 		test_expandosharing.xul \
 		file_expandosharing.jsm \
 		test_bug758563.xul \
 		test_bug760076.xul \
+		test_documentdomain.xul \
 		$(NULL)
 
 # Disabled until this test gets updated to test the new proxy based
 # wrappers.
 #		test_wrappers-2.xul \
 
 # Disabled due to apparent conservative stack scanner false positives on Linux64 debug.
 #		test_watchpoints.xul \
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_documentdomain.xul
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=601277
+-->
+<window title="Mozilla Bug 601277"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601277"
+     target="_blank">Mozilla Bug 601277</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Tests for document.domain. **/
+
+  SimpleTest.waitForExplicitFinish();
+
+  // Wait for the frames to load.
+  var gFramesLoaded = 0;
+  function frameLoaded() {
+    gFramesLoaded++;
+    if (gFramesLoaded == document.getElementsByTagName('iframe').length)
+      startTest();
+  }
+
+  function startTest() {
+
+    // Grab all the content windows and waive Xray. Xray waivers only apply to
+    // chrome, so we can pass these references directly to content.
+    var win1A = document.getElementById('test1A').contentWindow.wrappedJSObject;
+    var win1B = document.getElementById('test1B').contentWindow.wrappedJSObject;
+    var win2 = document.getElementById('test2').contentWindow.wrappedJSObject;
+    var winBase = document.getElementById('base').contentWindow.wrappedJSObject;
+
+    // Check the basics.
+    ok(win1A.tryToAccess(win1B),
+       "Same-origin windows should grant access");
+    ok(!win1A.tryToAccess(win2),
+       "Cross-origin windows should not grant access");
+    ok(!win1A.tryToAccess(winBase),
+       "Subdomain windows should not receive access");
+
+    // Store references now, while test1A and test1B are same-origin.
+    win1A.storeReference(win1B);
+    win1B.storeReference(win1A);
+    ok(win1A.tryToAccessStored(), "Stored references work when same-origin");
+
+    // Set document.domain on test1A. This should grant no access, since nobody
+    // else set it.
+    win1A.setDomain('example.org');
+    ok(!win1A.tryToAccess(winBase), "base must collaborate too");
+    ok(!winBase.tryToAccess(win1A), "base must collaborate too");
+    ok(!win1A.tryToAccess(win1B), "No longer same-origin");
+    ok(!win1A.tryToAccessStored(), "No longer same-origin");
+    ok(!win1B.tryToAccess(win1A), "No longer same-origin");
+    ok(!win1B.tryToAccessStored(), "No longer same-origin");
+
+    // Set document.domain on test1B. Now we're cooking with gas.
+    win1B.setDomain('example.org');
+    ok(!win1B.tryToAccess(winBase), "base must collaborate too");
+    ok(!winBase.tryToAccess(win1B), "base must collaborate too");
+    ok(win1A.tryToAccess(win1B), "same-origin");
+    ok(win1A.tryToAccessStored(), "same-origin");
+    ok(win1B.tryToAccess(win1A), "same-origin");
+    ok(win1B.tryToAccessStored(), "same-origin");
+
+    // Explicitly collaborate with base.
+    winBase.setDomain('example.org');
+    ok(winBase.tryToAccess(win1A), "base collaborates");
+    ok(win1A.tryToAccess(winBase), "base collaborates");
+
+    // All done.
+    SimpleTest.finish();
+  }
+
+
+  ]]>
+  </script>
+
+  <iframe id="test1A" onload="frameLoaded();" type="content"
+          src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+  <iframe id="test1B" onload="frameLoaded();" type="content"
+          src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+  <iframe id="test2" onload="frameLoaded();" type="content"
+          src="http://test2.example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+  <iframe id="base" onload="frameLoaded();" type="content"
+          src="http://example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+</window>
--- a/js/xpconnect/tests/mochitest/Makefile.in
+++ b/js/xpconnect/tests/mochitest/Makefile.in
@@ -65,16 +65,17 @@ MOCHITEST_FILES =	bug500931_helper.html 
 		file_bug758563.html \
 		test_bug764389.html \
 		file_nodelists.html \
 		file_bug706301.html \
 		file_exnstack.html \
 		file_expandosharing.html \
 		file_bug760131.html \
 		file_empty.html \
+		file_documentdomain.html \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES	= \
 		test_bug361111.xul \
 		test_bug760131.html \
 		$(NULL)
 
 ifneq ($(OS_TARGET),Android)
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_documentdomain.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="application/javascript">
+
+  function setDomain(domain) {
+    document.domain = domain;
+  }
+
+  function tryToAccess(otherWin) {
+    try {
+      var text = otherWin.document.getElementById('foo').innerHTML;
+      return /Better Late/.exec(text);
+    } catch (e) { return false; }
+  }
+
+  var gRef = null;
+  function storeReference(otherWin) {
+    gRef = otherWin.document.getElementById('foo');
+  }
+
+  function tryToAccessStored() {
+    try {
+      return /Better Late/.exec(gRef.innerHTML);
+    } catch (e) { return false; }
+  }
+
+
+</script>
+</head>
+<body>
+<span id="foo">Better Late than Never</span>
+</body>
+</html>
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -27,36 +27,34 @@ using namespace js;
 namespace xpc {
 
 nsIPrincipal *
 GetCompartmentPrincipal(JSCompartment *compartment)
 {
     return nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
 }
 
+// Does the principal of compartment a subsume the principal of compartment b?
 bool
-AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b)
+AccessCheck::subsumes(JSCompartment *a, JSCompartment *b)
 {
     nsIPrincipal *aprin = GetCompartmentPrincipal(a);
     nsIPrincipal *bprin = GetCompartmentPrincipal(b);
 
     // If either a or b doesn't have principals, we don't have enough
     // information to tell. Seeing as how this is Gecko, we are default-unsafe
     // in this case.
     if (!aprin || !bprin)
         return true;
 
-    bool equals;
-    nsresult rv = aprin->EqualsIgnoringDomain(bprin, &equals);
-    if (NS_FAILED(rv)) {
-        NS_ERROR("unable to ask about equality");
-        return false;
-    }
+    bool subsumes;
+    nsresult rv = aprin->Subsumes(bprin, &subsumes);
+    NS_ENSURE_SUCCESS(rv, false);
 
-    return equals;
+    return subsumes;
 }
 
 bool
 AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper)
 {
     // The caller must ensure that the given wrapper wraps a Location object.
     MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
 
@@ -71,20 +69,18 @@ AccessCheck::isLocationObjectSameOrigin(
         obj = js::UnwrapObject(obj);
         JS_ASSERT(js::GetObjectClass(obj)->ext.innerObject);
     }
 
     // Now innerize it to find the *current* inner window for our outer.
     obj = JS_ObjectToInnerObject(cx, obj);
 
     // Which lets us compare the current compartment against the old one.
-    return obj &&
-           (isSameOrigin(js::GetObjectCompartment(wrapper),
-                         js::GetObjectCompartment(obj)) ||
-            documentDomainMakesSameOrigin(cx, obj));
+    return obj && subsumes(js::GetObjectCompartment(wrapper),
+                           js::GetObjectCompartment(obj));
 }
 
 bool
 AccessCheck::isChrome(JSCompartment *compartment)
 {
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (!ssm) {
         return false;
@@ -190,96 +186,31 @@ IsFrameId(JSContext *cx, JSObject *obj, 
 }
 
 static bool
 IsWindow(const char *name)
 {
     return name[0] == 'W' && !strcmp(name, "Window");
 }
 
-static bool
-IsLocation(const char *name)
-{
-    return name[0] == 'L' && !strcmp(name, "Location");
-}
-
-static nsIPrincipal *
-GetPrincipal(JSObject *obj)
-{
-    NS_ASSERTION(!IS_SLIM_WRAPPER(obj), "global object is a slim wrapper?");
-    NS_ASSERTION(js::GetObjectClass(obj)->flags & JSCLASS_IS_GLOBAL,
-                 "Not a global object?");
-    NS_ASSERTION(!(js::GetObjectClass(obj)->flags & JSCLASS_IS_DOMJSCLASS),
-                 "Not sure what we should do with these yet!");
-    if (!IS_WN_WRAPPER(obj)) {
-        NS_ASSERTION(!(~js::GetObjectClass(obj)->flags &
-                       (JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE)),
-                     "bad object");
-        nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
-            do_QueryInterface((nsISupports*)xpc_GetJSPrivate(obj));
-        NS_ASSERTION(objPrin, "global isn't nsIScriptObjectPrincipal?");
-        return objPrin->GetPrincipal();
-    }
-
-    nsIXPConnect *xpc = nsXPConnect::GetRuntimeInstance()->GetXPConnect();
-    return xpc->GetPrincipal(obj, true);
-}
-
-bool
-AccessCheck::documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj)
-{
-    JSObject *scope = JS_GetScriptedGlobal(cx);
-
-    nsIPrincipal *subject;
-    nsIPrincipal *object;
-
-    {
-        JSAutoEnterCompartment ac;
-
-        if (!ac.enter(cx, scope))
-            return false;
-
-        subject = GetPrincipal(scope);
-    }
-
-    if (!subject)
-        return false;
-
-    {
-        JSAutoEnterCompartment ac;
-
-        if (!ac.enter(cx, obj))
-            return false;
-
-        object = GetPrincipal(JS_GetGlobalForObject(cx, obj));
-    }
-
-    bool subsumes;
-    return NS_SUCCEEDED(subject->Subsumes(object, &subsumes)) && subsumes;
-}
-
 bool
 AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id,
                                           Wrapper::Action act)
 {
     if (!XPCWrapper::GetSecurityManager())
         return true;
 
     if (act == Wrapper::CALL)
         return true;
 
     JSObject *obj = Wrapper::wrappedObject(wrapper);
 
-    // LocationPolicy checks PUNCTURE first, so we should never get here for
-    // Location wrappers. For all other wrappers interested in cross-origin
-    // semantics, we want to allow puncturing only for the same-origin
-    // document.domain case.
+    // PUNCTURE Is always denied for cross-origin access.
     if (act == Wrapper::PUNCTURE) {
-        MOZ_ASSERT(!WrapperFactory::IsLocationObject(obj));
-        return documentDomainMakesSameOrigin(cx, obj);
+        return nsContentUtils::CallerHasUniversalXPConnect();
     }
 
     const char *name;
     js::Class *clasp = js::GetObjectClass(obj);
     NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here");
     if (clasp->ext.innerObject)
         name = "Window";
     else
@@ -288,26 +219,16 @@ AccessCheck::isCrossOriginAccessPermitte
     if (JSID_IS_STRING(id)) {
         if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
             return true;
     }
 
     if (IsWindow(name) && IsFrameId(cx, obj, id))
         return true;
 
-    // Do the dynamic document.domain check.
-    //
-    // Location also needs a dynamic access check, but it's a different one, and
-    // we do it in LocationPolicy::check. Before LocationPolicy::check does that
-    // though, it first calls this function to check whether the property is
-    // accessible to anyone regardless of origin. So make sure not to do the
-    // document.domain check in that case.
-    if (!IsLocation(name) && documentDomainMakesSameOrigin(cx, obj))
-        return true;
-
     return (act == Wrapper::SET)
            ? nsContentUtils::IsCallerTrustedForWrite()
            : nsContentUtils::IsCallerTrustedForRead();
 }
 
 bool
 AccessCheck::isSystemOnlyAccessPermitted(JSContext *cx)
 {
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -10,25 +10,24 @@
 #include "WrapperFactory.h"
 
 class nsIPrincipal;
 
 namespace xpc {
 
 class AccessCheck {
   public:
-    static bool isSameOrigin(JSCompartment *a, JSCompartment *b);
+    static bool subsumes(JSCompartment *a, JSCompartment *b);
     static bool isChrome(JSCompartment *compartment);
     static bool callerIsChrome();
     static nsIPrincipal *getPrincipal(JSCompartment *compartment);
     static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id,
                                              js::Wrapper::Action act);
     static bool isSystemOnlyAccessPermitted(JSContext *cx);
     static bool isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper);
-    static bool documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj);
 
     static bool needsSystemOnlyWrapper(JSObject *obj);
 
     static bool isScriptAccessOnly(JSContext *cx, JSObject *wrapper);
 
     static void deny(JSContext *cx, jsid id);
 };
 
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -363,17 +363,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (IsComponentsObject(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ComponentsObjectPolicy>::singleton;
         } else {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ExposedPropertiesOnly>::singleton;
         }
-    } else if (AccessCheck::isSameOrigin(origin, target)) {
+    } else if (AccessCheck::subsumes(target, origin)) {
         // For the same-origin case we use a transparent wrapper, unless one
         // of the following is true:
         // * The object is flagged as needing a SOW.
         // * The object is a Location object.
         // * The object is a Components object.
         // * The context compartment specifically requested Xray vision into
         //   same-origin compartments.
         //
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -771,34 +771,16 @@ ContentScriptHasUniversalXPConnect()
 
         bool privileged;
         if (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged)
             return true;
     }
     return false;
 }
 
-class AutoLeaveHelper
-{
-  public:
-    AutoLeaveHelper(js::Wrapper &xray, JSContext *cx, JSObject *wrapper)
-      : xray(xray), cx(cx), wrapper(wrapper)
-    {
-    }
-    ~AutoLeaveHelper()
-    {
-        xray.leave(cx, wrapper);
-    }
-
-  private:
-    js::Wrapper &xray;
-    JSContext *cx;
-    JSObject *wrapper;
-};
-
 bool
 XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper,
                                                JSObject *wrapper, JSObject *holder, jsid id,
                                                bool set, PropertyDescriptor *desc)
 {
     // Xray wrappers don't use the regular wrapper hierarchy, so we should be
     // in the wrapper's compartment here, not the wrappee.
     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
@@ -811,18 +793,16 @@ XPCWrappedNativeXrayTraits::resolveOwnPr
           Is<nsIDocument>(wrapper))) &&
         (AccessCheck::callerIsChrome() || ContentScriptHasUniversalXPConnect())) {
         bool status;
         Wrapper::Action action = set ? Wrapper::SET : Wrapper::GET;
         desc->obj = NULL; // default value
         if (!jsWrapper.enter(cx, wrapper, id, action, &status))
             return status;
 
-        AutoLeaveHelper helper(jsWrapper, cx, wrapper);
-
         desc->obj = wrapper;
         desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED;
         if (id == rt->GetStringID(XPCJSRuntime::IDX_BASEURIOBJECT))
             desc->getter = baseURIObject_getter;
         else if (id == rt->GetStringID(XPCJSRuntime::IDX_DOCUMENTURIOBJECT))
             desc->getter = documentURIObject_getter;
         else
             desc->getter = nodePrincipal_getter;
@@ -1170,20 +1150,17 @@ IsTransparent(JSContext *cx, JSObject *w
         return true;
 
     if (!WrapperFactory::IsPartiallyTransparent(wrapper))
         return false;
 
     // Redirect access straight to the wrapper if UniversalXPConnect is enabled.
     // We don't need to check for system principal here, because only content
     // scripts have Partially Transparent wrappers.
-    if (ContentScriptHasUniversalXPConnect())
-        return true;
-
-    return AccessCheck::documentDomainMakesSameOrigin(cx, UnwrapObject(wrapper));
+    return ContentScriptHasUniversalXPConnect();
 }
 
 JSObject *
 GetNativePropertiesObject(JSContext *cx, JSObject *wrapper)
 {
     NS_ASSERTION(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper),
                  "bad object passed in");
 
@@ -1258,18 +1235,16 @@ XrayWrapper<Base, Traits>::getPropertyDe
     }
 
     bool status;
     Wrapper::Action action = set ? Wrapper::SET : Wrapper::GET;
     desc->obj = NULL; // default value
     if (!this->enter(cx, wrapper, id, action, &status))
         return status;
 
-    AutoLeaveHelper helper(*this, cx, wrapper);
-
     typename Traits::ResolvingId resolving(wrapper, id);
 
     // Redirect access straight to the wrapper if we should be transparent.
     if (XrayUtils::IsTransparent(cx, wrapper)) {
         JSObject *obj = Traits::getInnerObject(wrapper);
         {
             JSAutoEnterCompartment ac;
             if (!ac.enter(cx, obj))
@@ -1296,18 +1271,16 @@ XrayWrapper<Base, Traits>::getPropertyDe
     if (!WrapperFactory::IsPartiallyTransparent(wrapper) &&
         id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) {
         bool status;
         Wrapper::Action action = set ? Wrapper::SET : Wrapper::GET;
         desc->obj = NULL; // default value
         if (!this->enter(cx, wrapper, id, action, &status))
             return status;
 
-        AutoLeaveHelper helper(*this, cx, wrapper);
-
         desc->obj = wrapper;
         desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED;
         desc->getter = wrappedJSObject_getter;
         desc->setter = NULL;
         desc->shortid = 0;
         desc->value = JSVAL_VOID;
         return true;
     }
@@ -1364,18 +1337,16 @@ XrayWrapper<Base, Traits>::getOwnPropert
     }
 
     bool status;
     Wrapper::Action action = set ? Wrapper::SET : Wrapper::GET;
     desc->obj = NULL; // default value
     if (!this->enter(cx, wrapper, id, action, &status))
         return status;
 
-    AutoLeaveHelper helper(*this, cx, wrapper);
-
     typename Traits::ResolvingId resolving(wrapper, id);
 
     // NB: Nothing we do here acts on the wrapped native itself, so we don't
     // enter our policy.
     // Redirect access straight to the wrapper if we should be transparent.
     if (XrayUtils::IsTransparent(cx, wrapper)) {
         JSObject *obj = Traits::getInnerObject(wrapper);
         {
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -125,16 +125,17 @@ SHARED_LIBRARY_LIBS	+= \
   $(DEPTH)/dom/system/gonk/$(LIB_PREFIX)domsystemgonk_s.$(LIB_SUFFIX) \
   $(DEPTH)/dom/telephony/$(LIB_PREFIX)domtelephony_s.$(LIB_SUFFIX) \
   $(NULL)
 endif #}
 
 ifdef MOZ_MEDIA
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/$(LIB_PREFIX)gkconmedia_s.$(LIB_SUFFIX) \
+	$(DEPTH)/content/media/webrtc/$(LIB_PREFIX)gkconwebrtc_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
 ifdef MOZ_OGG
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/ogg/$(LIB_PREFIX)gkconogg_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
--- a/layout/media/Makefile.in
+++ b/layout/media/Makefile.in
@@ -121,19 +121,16 @@ endif
 ifdef MOZ_ENABLE_SKIA
 SHARED_LIBRARY_LIBS += $(MOZ_SKIA_LIBS)
 endif
 
 ifeq (WINNT,$(OS_TARGET))
 EXTRA_DSO_LDOPTS = $(MOZALLOC_LIB) $(NSPR_LIBS)
 OS_LIBS += $(call EXPAND_LIBNAME,usp10 ole32)
 
-# OTS uses uncompress2() from libz, so we need to link with this
-EXTRA_DSO_LDOPTS += $(MOZ_ZLIB_LIBS)
-
 ifdef MOZ_WEBRTC
 EXTRA_DSO_LDOPTS += \
   -LIBPATH:"$(MOZ_DIRECTX_SDK_PATH)/lib/$(MOZ_DIRECTX_SDK_CPU_SUFFIX)" \
   $(NULL)
 OS_LIBS += $(call EXPAND_LIBNAME,secur32 crypt32 iphlpapi strmiids dmoguids wmcodecdspuuid amstrmid msdmo wininet)
 endif
 
 DEFFILE = symbols.def
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -182,17 +182,17 @@ MOCHITEST_FILES =	test_acid3_test46.html
 		visited_image_loading.sjs \
 		visited_image_loading_frame.html \
 		visited_image_loading_frame_empty.html \
 		test_load_events_on_stylesheets.html \
 		test_bug721136.html \
 		$(NULL)
 
 ifdef MOZ_FLEXBOX
-_TEST_FILES +=	\
+MOCHITEST_FILES +=	\
 		test_flexbox_align_self_auto.html \
 		test_flexbox_flex_grow_and_shrink.html \
 		test_flexbox_flex_shorthand.html \
 		$(NULL)
 endif
 
 _VISITED_REFTEST_FILES = \
 		$(shell find $(topsrcdir)/layout/reftests/css-visited/ -name '*.html' -o -name '*.xhtml') \
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -437,20 +437,32 @@ nsSVGGlyphFrame::UpdateBounds()
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingUpdateBounds(this),
                "This call is probaby a wasteful mistake");
 
   NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
                     "UpdateBounds mechanism not designed for this");
 
   mRect.SetEmpty();
 
-  gfxRect extent = GetBBoxContribution(gfxMatrix(),
-    nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIgnoreFillIfNone |
-    nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIgnoreStrokeIfNone |
-    nsSVGUtils::eBBoxIncludeMarkers);
+  PRUint32 flags = nsSVGUtils::eBBoxIncludeFill |
+                   nsSVGUtils::eBBoxIncludeStroke |
+                   nsSVGUtils::eBBoxIncludeMarkers;
+  // Our "visual" overflow rect needs to be valid for building display lists
+  // for hit testing, which means that for certain values of 'pointer-events'
+  // it needs to include the geometry of the fill or stroke even when the fill/
+  // stroke don't actually render (e.g. when stroke="none" or
+  // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
+  PRUint16 hitTestFlags = GetHitTestFlags();
+  if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
+   flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
+  }
+  if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
+   flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
+  }
+  gfxRect extent = GetBBoxContribution(gfxMatrix(), flags);
 
   if (!extent.IsEmpty()) {
     mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, 
               PresContext()->AppUnitsPerCSSPixel());
   }
 
   // See bug 614732 comment 32.
   mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
@@ -585,25 +597,25 @@ nsSVGGlyphFrame::GetBBoxContribution(con
   // gfxContext has SetLineWidth() called on it, so if we want to ask the
   // gfxContext for *stroke* extents, we'll need to wrap the
   // AddBoundingBoxesToPath() call with CurrentLineWidth()/SetLineWidth()
   // calls to record and then reset the stroke width.
 
   gfxRect pathExtents = tmpCtx->GetUserPathExtent();
 
   // Account for fill:
-  if ((aFlags & nsSVGUtils::eBBoxIncludeFill) != 0 &&
-      ((aFlags & nsSVGUtils::eBBoxIgnoreFillIfNone) == 0 ||
+  if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
+      ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
        GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
     bbox = pathExtents;
   }
 
   // Account for stroke:
-  if ((aFlags & nsSVGUtils::eBBoxIncludeStroke) != 0 &&
-      ((aFlags & nsSVGUtils::eBBoxIgnoreStrokeIfNone) == 0 || HasStroke())) {
+  if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
+      ((aFlags & nsSVGUtils::eBBoxIncludeStroke) && HasStroke())) {
     bbox =
       bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
                                                            this,
                                                            aToBBoxUserspace));
   }
 
   return bbox;
 }
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -220,27 +220,27 @@ nsSVGPathGeometryFrame::UpdateBounds()
 
   if (!nsSVGUtils::NeedsUpdatedBounds(this)) {
     return;
   }
 
   PRUint32 flags = nsSVGUtils::eBBoxIncludeFill |
                    nsSVGUtils::eBBoxIncludeStroke |
                    nsSVGUtils::eBBoxIncludeMarkers;
-  PRUint32 pointerEvents = GetStyleVisibility()->mPointerEvents;
-  if (pointerEvents == NS_STYLE_POINTER_EVENTS_AUTO ||
-      pointerEvents == NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED ||
-      pointerEvents == NS_STYLE_POINTER_EVENTS_PAINTED ||
-      pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
-    // We can only ignore 'none' stroke if the value of the pointer-events
-    // property doesn't require us to include it in our "visual" overflow rect
-    // so that our visual overflow rect is valid for building display lists for
-    // hit-testing.
-    flags |= nsSVGUtils::eBBoxIgnoreStrokeIfNone |
-             nsSVGUtils::eBBoxIgnoreFillIfNone;
+  // Our "visual" overflow rect needs to be valid for building display lists
+  // for hit testing, which means that for certain values of 'pointer-events'
+  // it needs to include the geometry of the fill or stroke even when the fill/
+  // stroke don't actually render (e.g. when stroke="none" or
+  // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
+  PRUint16 hitTestFlags = GetHitTestFlags();
+  if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
+   flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
+  }
+  if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
+   flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
   }
   gfxRect extent = GetBBoxContribution(gfxMatrix(), flags);
   mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
             PresContext()->AppUnitsPerCSSPixel());
 
   // See bug 614732 comment 32.
   mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
     mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
@@ -319,25 +319,25 @@ nsSVGPathGeometryFrame::GetBBoxContribut
   // # Due to stroke dashing, in certain cases the fill extents could actually
   //   extend outside the stroke extents.
   // # If the stroke is very thin, cairo won't paint any stroke, and so the
   //   stroke bounds that it will return will be empty.
 
   gfxRect pathExtents = tmpCtx->GetUserPathExtent();
 
   // Account for fill:
-  if ((aFlags & nsSVGUtils::eBBoxIncludeFill) != 0 &&
-      ((aFlags & nsSVGUtils::eBBoxIgnoreFillIfNone) == 0 ||
+  if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
+      ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
        GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
     bbox = pathExtents;
   }
 
   // Account for stroke:
-  if ((aFlags & nsSVGUtils::eBBoxIncludeStroke) != 0 &&
-      ((aFlags & nsSVGUtils::eBBoxIgnoreStrokeIfNone) == 0 || HasStroke())) {
+  if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
+      ((aFlags & nsSVGUtils::eBBoxIncludeStroke) && HasStroke())) {
     // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for
     // device space extents. Instead we approximate the stroke extents from
     // pathExtents using PathExtentsToMaxStrokeExtents.
     if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) {
       // We have a zero length path, but it may still have non-empty stroke
       // bounds depending on the value of stroke-linecap. We need to fix up
       // pathExtents before it can be used with PathExtentsToMaxStrokeExtents
       // though, because if pathExtents is empty, its position will not have
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -586,27 +586,28 @@ public:
    * If the bbox is empty, this will return a singular matrix.
    */
   static gfxMatrix
   AdjustMatrixForUnits(const gfxMatrix &aMatrix,
                        nsSVGEnum *aUnits,
                        nsIFrame *aFrame);
 
   enum BBoxFlags {
-    eBBoxIncludeFill          = 1 << 0,
-    eBBoxIgnoreFillIfNone     = 1 << 1,
-    eBBoxIncludeStroke        = 1 << 2,
-    eBBoxIgnoreStrokeIfNone   = 1 << 3,
-    eBBoxIncludeMarkers       = 1 << 4
+    eBBoxIncludeFill           = 1 << 0,
+    eBBoxIncludeFillGeometry   = 1 << 1,
+    eBBoxIncludeStroke         = 1 << 2,
+    eBBoxIncludeStrokeGeometry = 1 << 3,
+    eBBoxIncludeMarkers        = 1 << 4
   };
   /**
    * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in
    * aFrame's userspace.
    */
-  static gfxRect GetBBox(nsIFrame *aFrame, PRUint32 aFlags = eBBoxIncludeFill);
+  static gfxRect GetBBox(nsIFrame *aFrame,
+                         PRUint32 aFlags = eBBoxIncludeFillGeometry);
 
   /**
    * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified
    * using four nsSVGLength2 values into a user unit rectangle in user space.
    *
    * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing
    * the x, y, width and height values in that order
    * @param aBBox the bounding box of the object the rect is relative to;
--- a/media/libpng/Makefile.in
+++ b/media/libpng/Makefile.in
@@ -47,13 +47,12 @@ EXPORTS		= png.h pngconf.h mozpngconf.h
 
 LOCAL_INCLUDES	= -I$(srcdir)
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
-DEFINES		+= -DZLIB_DLL
 ifdef BUILD_DEBUG_GC
 DEFINES		+= -DDEBUG_GC
 endif
 endif
--- a/memory/build/extraMallocFuncs.c
+++ b/memory/build/extraMallocFuncs.c
@@ -101,16 +101,15 @@ const char *je_malloc_conf = "narenas:1,
 static void
 _je_malloc_message(void *cbopaque, const char *s)
 {
   __android_log_print(ANDROID_LOG_INFO, "GeckoJemalloc", "%s", s);
 }
 
 void (*je_malloc_message)(void *, const char *s) = _je_malloc_message;
 #endif
+#endif /* MOZ_JEMALLOC */
 
 #include <mozilla/Assertions.h>
 
 void moz_abort() {
   MOZ_CRASH();
 }
-
-#endif /* MOZ_JEMALLOC */
--- a/memory/mozjemalloc/Makefile.in
+++ b/memory/mozjemalloc/Makefile.in
@@ -39,9 +39,11 @@ endif
 
 # For non release/esr builds, enable (some) fatal jemalloc assertions.  This
 # helps us catch memory errors.  See bug 764192 for details on what
 # MOZ_TEMP_INVESTIGATION is for.
 ifeq (,$(filter release esr,$(MOZ_UPDATE_CHANNEL)))
 DEFINES	+= -DMOZ_JEMALLOC_HARD_ASSERTS -DMOZ_TEMP_INVESTIGATION
 endif
 
+DEFINES += -Dabort=moz_abort
+
 include $(topsrcdir)/config/rules.mk
--- a/mobile/android/app/Makefile.in
+++ b/mobile/android/app/Makefile.in
@@ -27,33 +27,16 @@ LOCAL_INCLUDES += -I$(DEPTH)/build
 DEFINES += -DXPCOM_GLUE
 STL_FLAGS=
 
 LIBS += \
   $(EXTRA_DSO_LIBS) \
   $(XPCOM_STANDALONE_GLUE_LDOPTS) \
   $(NULL)
 
-ifeq ($(MOZ_PLATFORM_MAEMO),6)
-LIBS += \
-  $(LIBXUL_DIST)/../widget/qt/faststartupqt/$(LIB_PREFIX)faststartupqt.$(LIB_SUFFIX) \
-  $(MOZ_QT_LIBS) \
-  $(NULL)
-LOCAL_INCLUDES += -I$(topsrcdir)/widget/qt/faststartupqt $(TK_CFLAGS)
-endif
-
-ifeq ($(OS_ARCH),WINNT)
-OS_LIBS += $(call EXPAND_LIBNAME,version)
-endif
-
-ifdef _MSC_VER
-# Always enter a Windows program through wmain, whether or not we're
-# a console application.
-WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
-endif
 endif
 endif #LIBXUL_SDK
 
 # Make sure the standalone glue doesn't try to get libxpcom.so from mobile/app.
 NSDISTMODE = copy
 
 include $(topsrcdir)/config/rules.mk
 
@@ -64,84 +47,22 @@ DEFINES += \
   -DAPP_VERSION=$(MOZ_APP_VERSION) \
   -DMOZ_UPDATER=$(MOZ_UPDATER) \
   $(NULL)
 
 ifdef MOZ_PKG_SPECIAL
 DEFINES += -DMOZ_PKG_SPECIAL=$(MOZ_PKG_SPECIAL)
 endif
 
-ifeq ($(OS_ARCH),WINNT)
-REDIT_PATH = $(LIBXUL_DIST)/bin
-endif
-
 APP_BINARY = $(MOZ_APP_NAME)$(BIN_SUFFIX)
 
-ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) #{
-
-APP_NAME = $(MOZ_APP_DISPLAYNAME)
-APP_VERSION = $(MOZ_APP_VERSION)
-
-ifdef MOZ_DEBUG
-APP_NAME := $(APP_NAME)Debug
-endif
-
-AB_CD = $(MOZ_UI_LOCALE)
-
-AB := $(firstword $(subst -, ,$(AB_CD)))
-
-clean clobber repackage::
-	$(RM) -r $(dist_dest)
-
-ifdef LIBXUL_SDK
-APPFILES = Resources
-else
-APPFILES = MacOS
-endif
-
-libs-preqs = \
-  $(call mkdir_deps,$(dist_dest)/Contents/MacOS) \
-  $(call mkdir_deps,$(dist_dest)/Contents/Resources/$(AB).lproj) \
-  $(NULL)
-
-.PHONY: repackage
-libs repackage:: $(libs-preqs)
-	rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj
-	rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/Contents/Resources/$(AB).lproj
-	sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" -e "s/%APP_BINARY%/$(APP_BINARY)/" $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
-	sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/Contents/Resources/$(AB).lproj/InfoPlist.strings
-	rsync -a $(DIST)/bin/ $(dist_dest)/Contents/$(APPFILES)
-	$(RM) $(dist_dest)/Contents/$(APPFILES)/mangle $(dist_dest)/Contents/$(APPFILES)/shlibsign
-ifdef LIBXUL_SDK
-	cp $(LIBXUL_DIST)/bin/xulrunner$(BIN_SUFFIX) $(dist_dest)/Contents/MacOS/$(APP_BINARY)
-	rsync -a --exclude nsinstall --copy-unsafe-links $(LIBXUL_DIST)/XUL.framework $(dist_dest)/Contents/Frameworks
-else
-	$(RM) $(dist_dest)/Contents/MacOS/$(PROGRAM)
-	rsync -aL $(PROGRAM) $(dist_dest)/Contents/MacOS
-endif
-	printf "APPLMOZB" > $(dist_dest)/Contents/PkgInfo
-
-else # MOZ_WIDGET_TOOLKIT != cocoa
-
 libs::
 ifdef LIBXUL_SDK
 	cp $(LIBXUL_DIST)/bin/xulrunner-stub$(BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY)
 endif
 ifndef SKIP_COPY_XULRUNNER #{
 ifdef LIBXUL_SDK
 	$(NSINSTALL) -D $(DIST)/bin/xulrunner
 	(cd $(LIBXUL_SDK)/bin && tar $(TAR_CREATE_FLAGS) - .) | (cd $(DIST)/bin/xulrunner && tar -xf -)
 endif
 endif #} SKIP_COPY_XULRUNNER
 
-ifeq ($(MOZ_PLATFORM_MAEMO),6)
-	$(NSINSTALL) -D $(DIST)/bin/res/drawable
-	cp $(topsrcdir)/mobile/app/maemo/* $(DIST)/bin/res/drawable/
-	cp $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/favicon32.png $(DIST)/bin/res/drawable/
-endif
 	$(NSINSTALL) -D $(DIST)/bin/chrome/icons/default
-
-ifeq ($(OS_ARCH),WINNT)
-	cp $(srcdir)/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/$(APP_ICON).ico
-	$(REDIT_PATH)/redit$(HOST_BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY) $(srcdir)/$(APP_ICON).ico
-endif
-
-endif #}
deleted file mode 100644
--- a/mobile/android/app/macbuild/Contents/Info.plist.in
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>English</string>
-	<key>CFBundleDocumentTypes</key>
-	<array>
-		<dict>
-			<key>CFBundleTypeExtensions</key>
-			<array>
-				<string>html</string>
-				<string>htm</string>
-				<string>shtml</string>
-				<string>xht</string>
-				<string>xhtml</string>
-			</array>
-			<key>CFBundleTypeIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleTypeName</key>
-			<string>HTML Document</string>
-			<key>CFBundleTypeOSTypes</key>
-			<array>
-				<string>HTML</string>
-			</array>
-			<key>CFBundleTypeRole</key>
-			<string>Viewer</string>
-		</dict>
-		<dict>
-			<key>CFBundleTypeExtensions</key>
-			<array>
-			    <string>text</string>
-				<string>txt</string>
-				<string>js</string>
-				<string>log</string>
-				<string>css</string>
-				<string>xul</string>
-				<string>rdf</string>
-			</array>
-			<key>CFBundleTypeIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleTypeName</key>
-			<string>Text Document</string>
-			<key>CFBundleTypeOSTypes</key>
-			<array>
-				<string>TEXT</string>
-				<string>utxt</string>
-			</array>
-			<key>CFBundleTypeRole</key>
-			<string>Viewer</string>
-		</dict>
-		<dict>
-			<key>CFBundleTypeExtensions</key>
-			<array>
-				<string>jpeg</string>
-				<string>jpg</string>
-				<string>png</string>
-				<string>gif</string>
-			</array>
-			<key>CFBundleTypeIconFile</key>
-			<string>fileBookmark.icns</string>
-			<key>CFBundleTypeName</key>
-			<string>document.icns</string>
-			<key>CFBundleTypeOSTypes</key>
-			<array>
-				<string>GIFf</string>
-				<string>JPEG</string>
-				<string>PNGf</string>
-			</array>
-			<key>CFBundleTypeRole</key>
-			<string>Viewer</string>
-		</dict>
-	</array>
-	<key>CFBundleExecutable</key>
-	<string>fennec</string>
-	<key>CFBundleGetInfoString</key>
-	<string>%APP_NAME% %APP_VERSION%</string>
-	<key>CFBundleIconFile</key>
-	<string>fennec</string>
-	<key>CFBundleIdentifier</key>
-	<string>org.mozilla.fennec</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>%APP_NAME%</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>%APP_VERSION%</string>
-	<key>CFBundleSignature</key>
-	<string>MOZB</string>
-	<key>CFBundleURLTypes</key>
-	<array>
-		<dict>
-			<key>CFBundleURLIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleURLName</key>
-			<string>http URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>http</string>
-			</array>
-		</dict>
-		<dict>
-			<key>CFBundleURLIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleURLName</key>
-			<string>https URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>https</string>
-			</array>
-		</dict>
-		<dict>
-			<key>CFBundleURLName</key>
-			<string>ftp URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>ftp</string>
-			</array>
-		</dict>
-		<dict>
-			<key>CFBundleURLName</key>
-			<string>file URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>file</string>
-			</array>
-		</dict>
-	</array>
-	<key>CFBundleVersion</key>
-	<string>%APP_VERSION%</string>
-	<key>NSAppleScriptEnabled</key>
-	<true/>
-	<key>CGDisableCoalescedUpdates</key>
-	<true/>
-</dict>
-</plist>
deleted file mode 100644
--- a/mobile/android/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
+++ /dev/null
@@ -1,5 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-CFBundleName = "%APP_NAME%";
deleted file mode 100644
index 331fa75db266faf47e13364e0f7d808b13699efe..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -35,25 +35,16 @@ pref("toolkit.zoomManager.zoomValues", "
 
 // Mobile will use faster, less durable mode.
 pref("toolkit.storage.synchronous", 0);
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 pref("browser.viewport.desktopWidth", 980);
 
-#ifndef ANDROID
-#ifndef MOZ_PLATFORM_MAEMO
-// On desktop builds, simulate an MDPI tablet by default.
-pref("layout.css.dpi", 160);
-#else
-// Maemo X11 lies about its dpi
-pref("layout.css.dpi", 240);
-#endif
-#endif
 /* allow scrollbars to float above chrome ui */
 pref("ui.scrollbarsCanOverlapContent", 1);
 
 /* cache prefs */
 pref("browser.cache.disk.enable", true);
 pref("browser.cache.disk.capacity", 20480); // kilobytes
 pref("browser.cache.disk.max_entry_size", 4096); // kilobytes
 pref("browser.cache.disk.smart_size.enabled", true);
@@ -85,19 +76,16 @@ pref("network.http.pipelining", true);
 pref("network.http.pipelining.ssl", true);
 pref("network.http.proxy.pipelining", true);
 pref("network.http.pipelining.maxrequests" , 6);
 pref("network.http.keep-alive.timeout", 600);
 pref("network.http.max-connections", 20);
 pref("network.http.max-connections-per-server", 15);
 pref("network.http.max-persistent-connections-per-server", 6);
 pref("network.http.max-persistent-connections-per-proxy", 8);
-#ifdef MOZ_PLATFORM_MAEMO
-pref("network.autodial-helper.enabled", true);
-#endif
 
 // See bug 545869 for details on why these are set the way they are
 pref("network.buffer.cache.count", 24);
 pref("network.buffer.cache.size",  16384);
 
 /* history max results display */
 pref("browser.display.history.maxresults", 100);
 
@@ -328,17 +316,16 @@ pref("places.frecency.permRedirectVisitB
 pref("places.frecency.tempRedirectVisitBonus", 0);
 pref("places.frecency.defaultVisitBonus", 0);
 pref("places.frecency.unvisitedBookmarkBonus", 140);
 pref("places.frecency.unvisitedTypedBonus", 200);
 
 // disable color management
 pref("gfx.color_management.mode", 0);
 
-#ifdef ANDROID
 // 0=fixed margin, 1=velocity bias, 2=dynamic resolution, 3=no margins, 4=prediction bias
 pref("gfx.displayport.strategy", 1);
 // all of the following displayport strategy prefs will be divided by 1000
 // to obtain some multiplier which is then used in the strategy.
 // fixed margin strategy options
 pref("gfx.displayport.strategy_fm.multiplier", -1); // displayport dimension multiplier
 pref("gfx.displayport.strategy_fm.danger_x", -1); // danger zone on x-axis when multiplied by viewport width
 pref("gfx.displayport.strategy_fm.danger_y", -1); // danger zone on y-axis when multiplied by viewport height
@@ -347,17 +334,16 @@ pref("gfx.displayport.strategy_vb.multip
 pref("gfx.displayport.strategy_vb.threshold", -1); // velocity threshold in inches/frame
 pref("gfx.displayport.strategy_vb.reverse_buffer", -1); // fraction of buffer to keep in reverse direction from scroll
 pref("gfx.displayport.strategy_vb.danger_x_base", -1); // danger zone on x-axis when multiplied by viewport width
 pref("gfx.displayport.strategy_vb.danger_y_base", -1); // danger zone on y-axis when multiplied by viewport height
 pref("gfx.displayport.strategy_vb.danger_x_incr", -1); // additional danger zone on x-axis when multiplied by viewport width and velocity
 pref("gfx.displayport.strategy_vb.danger_y_incr", -1); // additional danger zone on y-axis when multiplied by viewport height and velocity
 // prediction bias strategy options
 pref("gfx.displayport.strategy_pb.threshold", -1); // velocity threshold in inches/frame
-#endif
 
 // don't allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize", true);
 
 // prevent click image resizing for nsImageDocument
 pref("browser.enable_click_image_resizing", false);
 
 // open in tab preferences
@@ -375,20 +361,16 @@ pref("privacy.item.history", true);
 pref("privacy.item.formdata", true);
 pref("privacy.item.downloads", true);
 pref("privacy.item.passwords", true);
 pref("privacy.item.sessions", true);
 pref("privacy.item.geolocation", true);
 pref("privacy.item.siteSettings", true);
 pref("privacy.item.syncAccount", true);
 
-#ifdef MOZ_PLATFORM_MAEMO
-pref("plugins.force.wmode", "opaque");
-#endif
-
 // URL to the Learn More link XXX this is the firefox one.  Bug 495578 fixes this.
 pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
 
 // enable geo
 pref("geo.enabled", true);
 
 // content sink control -- controls responsiveness during page load
 // see https://bugzilla.mozilla.org/show_bug.cgi?id=481566#c9
@@ -421,41 +403,25 @@ pref("browser.ui.zoom.force-user-scalabl
 // in 1/240-inch pixels:
 pref("browser.ui.touch.left", 32);
 pref("browser.ui.touch.right", 32);
 pref("browser.ui.touch.top", 48);
 pref("browser.ui.touch.bottom", 16);
 pref("browser.ui.touch.weight.visited", 120); // percentage
 
 // plugins
-#if MOZ_PLATFORM_MAEMO == 6
-pref("plugin.disable", false);
-pref("dom.ipc.plugins.enabled", true);
-#elifdef ANDROID
 pref("plugin.disable", false);
 pref("dom.ipc.plugins.enabled", false);
-#else
-pref("plugin.disable", true);
-pref("dom.ipc.plugins.enabled", true);
-#endif
 
 pref("plugins.click_to_play", true);
 // Disabled because of thread safety problem
 // in getting the bits from the surface.
 // Bug 756253
 pref("plugins.use_placeholder", 0);
 
-// process priority
-// higher values give content process less CPU time
-#if MOZ_PLATFORM_MAEMO == 5
-pref("dom.ipc.content.nice", 10);
-#else
-pref("dom.ipc.content.nice", 1);
-#endif
-
 // product URLs
 // The breakpad report server to link to in about:crashes
 pref("breakpad.reportURL", "http://crash-stats.mozilla.com/report/index/");
 pref("app.support.baseURL", "http://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/");
 pref("app.feedbackURL", "http://input.mozilla.com/feedback/");
 pref("app.privacyURL", "http://www.mozilla.com/%LOCALE%/m/privacy.html");
 pref("app.creditsURL", "http://www.mozilla.org/credits/");
 pref("app.channelURL", "http://www.mozilla.org/%LOCALE%/firefox/channel/");
@@ -542,26 +508,16 @@ pref("app.update.interval", 28800);
 pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/mobile/");
 pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/");
 #endif
 #endif
 
 // replace newlines with spaces on paste into single-line text boxes
 pref("editor.singleLine.pasteNewlines", 2);
 
-#ifdef MOZ_PLATFORM_MAEMO
-// update fonts for better readability
-pref("font.default.x-baltic", "SwissA");
-pref("font.default.x-central-euro", "SwissA");
-pref("font.default.x-cyrillic", "SwissA");
-pref("font.default.x-unicode", "SwissA");
-pref("font.default.x-user-def", "SwissA");
-pref("font.default.x-western", "SwissA");
-#endif
-
 #ifdef MOZ_SERVICES_SYNC
 // sync service
 pref("services.sync.client.type", "mobile");
 pref("services.sync.registerEngines", "Tab,Bookmarks,Form,History,Password,Prefs");
 pref("services.sync.autoconnectDelay", 5);
 
 // prefs to sync by default
 pref("services.sync.prefs.sync.browser.startup.homepage.title", true);
@@ -577,24 +533,17 @@ pref("services.sync.prefs.sync.privacy.d
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 #endif
 
 // threshold where a tap becomes a drag, in 1/240" reference pixels
 // The names of the preferences are to be in sync with nsEventStateManager.cpp
 pref("ui.dragThresholdX", 25);
 pref("ui.dragThresholdY", 25);
 
-#if MOZ_PLATFORM_MAEMO == 6
 pref("layers.acceleration.disabled", false);
-#elifdef ANDROID
-pref("layers.acceleration.disabled", false);
-#else
-pref("layers.acceleration.disabled", true);
-#endif
-
 pref("layers.offmainthreadcomposition.enabled", true);
 
 pref("notification.feature.enabled", true);
 
 // prevent tooltips from showing up
 pref("browser.chrome.toolbar_tips", false);
 pref("indexedDB.feature.enabled", true);
 pref("dom.indexedDB.warningQuota", 5);
--- a/mobile/android/base/AwesomeBar.java
+++ b/mobile/android/base/AwesomeBar.java
@@ -60,34 +60,34 @@ public class AwesomeBar extends GeckoAct
     static final String CURRENT_URL_KEY = "currenturl";
     static final String TARGET_KEY = "target";
     static final String SEARCH_KEY = "search";
     static final String USER_ENTERED_KEY = "user_entered";
     static enum Target { NEW_TAB, CURRENT_TAB };
 
     private String mTarget;
     private AwesomeBarTabs mAwesomeTabs;
-    private AwesomeBarEditText mText;
+    private CustomEditText mText;
     private ImageButton mGoButton;
     private ContentResolver mResolver;
     private ContextMenuSubject mContextMenuSubject;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         Log.d(LOGTAG, "creating awesomebar");
 
         mResolver = Tabs.getInstance().getContentResolver();
         LayoutInflater.from(this).setFactory(GeckoViewsFactory.getInstance());
 
         setContentView(R.layout.awesomebar);
 
         mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
-        mText = (AwesomeBarEditText) findViewById(R.id.awesomebar_text);
+        mText = (CustomEditText) findViewById(R.id.awesomebar_text);
 
         TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs);
         tabWidget.setDividerDrawable(null);
 
         mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
         mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
             public void onUrlOpen(String url) {
                 openUrlAndFinish(url);
@@ -119,17 +119,17 @@ public class AwesomeBar extends GeckoAct
         Intent intent = getIntent();
         String currentUrl = intent.getStringExtra(CURRENT_URL_KEY);
         mTarget = intent.getStringExtra(TARGET_KEY);
         if (currentUrl != null) {
             mText.setText(currentUrl);
             mText.selectAll();
         }
 
-        mText.setOnKeyPreImeListener(new AwesomeBarEditText.OnKeyPreImeListener() {
+        mText.setOnKeyPreImeListener(new CustomEditText.OnKeyPreImeListener() {
             public boolean onKeyPreIme(View v, int keyCode, KeyEvent event) {
                 // We only want to process one event per tap
                 if (event.getAction() != KeyEvent.ACTION_DOWN)
                     return false;
 
                 if (keyCode == KeyEvent.KEYCODE_ENTER) {
                     // If the AwesomeBar has a composition string, don't submit the text yet.
                     // ENTER is needed to commit the composition string.
@@ -586,33 +586,9 @@ public class AwesomeBar extends GeckoAct
                     // Found composition string.
                     return true;
                 }
             }
         }
         return false;
     }
 
-    public static class AwesomeBarEditText extends EditText {
-        OnKeyPreImeListener mOnKeyPreImeListener;
-
-        public interface OnKeyPreImeListener {
-            public boolean onKeyPreIme(View v, int keyCode, KeyEvent event);
-        }
-
-        public AwesomeBarEditText(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            mOnKeyPreImeListener = null;
-        }
-
-        @Override
-        public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-            if (mOnKeyPreImeListener != null)
-                return mOnKeyPreImeListener.onKeyPreIme(this, keyCode, event);
-
-            return false;
-        }
-
-        public void setOnKeyPreImeListener(OnKeyPreImeListener listener) {
-            mOnKeyPreImeListener = listener;
-        }
-    }
 }
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -344,16 +344,19 @@ abstract public class BrowserApp extends
         return mTabsPanel.isShown();
     }
 
     @Override
     public void onTabsLayoutChange(int width, int height) {
         if (mMainLayoutAnimator != null)
             mMainLayoutAnimator.stop();
 
+        if (mTabsPanel.isShown())
+            mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+
         mMainLayoutAnimator = new PropertyAnimator(150);
         mMainLayoutAnimator.setPropertyAnimationListener(this);
 
         if (isTablet()) {
             mMainLayoutAnimator.attach(mBrowserToolbar.getLayout(),
                                        PropertyAnimator.Property.SHRINK_LEFT,
                                        width);
 
@@ -392,18 +395,20 @@ abstract public class BrowserApp extends
             public void run() {
                 if (isTablet() && mTabsPanel.isShown()) {
                     // Fake the gecko layout to have been shrunk, instead of sliding.
                     ((LinearLayout.LayoutParams) mGeckoLayout.getLayoutParams()).setMargins(mTabsPanel.getWidth(), 0, 0, 0);
                     mGeckoLayout.scrollTo(0, 0);
                     mGeckoLayout.requestLayout();
                 }
 
-                if (!mTabsPanel.isShown())
+                if (!mTabsPanel.isShown()) {
                     mBrowserToolbar.updateTabs(false);
+                    mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+                }
             }
         });
     }
 
     /* Doorhanger notification methods */
     @Override
     void updatePopups(final Tab tab) {
         mDoorHangerPopup.updatePopup(mBrowserToolbar.mFavicon);
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -355,35 +355,38 @@ public class BrowserToolbar implements V
         } else if (mCount < count) {
             mTabsCount.setInAnimation(mSlideUpIn);
             mTabsCount.setOutAnimation(mSlideUpOut);
         } else {
             return;
         }
 
         mTabsCount.setText(String.valueOf(count));
+        mTabs.setContentDescription((count > 1) ?
+                                    mContext.getString(R.string.num_tabs, count) :
+                                    mContext.getString(R.string.one_tab));
         mCount = count;
-        mTabs.setContentDescription(mContext.getString(R.string.num_tabs, count));
-
         mHandler.postDelayed(new Runnable() {
             public void run() {
                 ((TextView) mTabsCount.getCurrentView()).setTextColor(mContext.getResources().getColor(R.color.url_bar_text_highlight));
             }
         }, mDuration);
 
         mHandler.postDelayed(new Runnable() {
             public void run() {
                 ((TextView) mTabsCount.getCurrentView()).setTextColor(mContext.getResources().getColor(R.color.tabs_counter_color));
             }
         }, 2 * mDuration);
     }
 
     public void updateTabCount(int count) {
         mTabsCount.setCurrentText(String.valueOf(count));
-        mTabs.setContentDescription(mContext.getString(R.string.num_tabs, count));
+        mTabs.setContentDescription((count > 1) ?
+                                    mContext.getString(R.string.num_tabs, count) :
+                                    mContext.getString(R.string.one_tab));
         mCount = count;
         updateTabs(GeckoApp.mAppContext.areTabsShown());
     }
 
     public void updateTabs(boolean areTabsShown) {
         if (areTabsShown) {
             mTabs.getBackground().setLevel(TABS_EXPANDED);
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/CustomEditText.java
@@ -0,0 +1,54 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.EditText;
+
+public class CustomEditText extends EditText {
+    public CustomEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mOnKeyPreImeListener = null;
+    }
+
+    OnKeyPreImeListener mOnKeyPreImeListener;
+
+    public interface OnKeyPreImeListener {
+        public boolean onKeyPreIme(View v, int keyCode, KeyEvent event);
+    }
+
+    public void setOnKeyPreImeListener(OnKeyPreImeListener listener) {
+        mOnKeyPreImeListener = listener;
+    }
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (mOnKeyPreImeListener != null)
+            return mOnKeyPreImeListener.onKeyPreIme(this, keyCode, event);
+
+        return false;
+    }
+
+    public void setOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
+        mOnWindowFocusChangeListener = listener;
+    }
+
+    OnWindowFocusChangeListener mOnWindowFocusChangeListener;
+
+    public interface OnWindowFocusChangeListener {
+        public void onWindowFocusChanged(boolean hasFocus);
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (mOnWindowFocusChangeListener != null)
+            mOnWindowFocusChangeListener.onWindowFocusChanged(hasFocus);
+    }
+}
--- a/mobile/android/base/FindInPageBar.java
+++ b/mobile/android/base/FindInPageBar.java
@@ -4,63 +4,94 @@
 
 package org.mozilla.gecko;
 
 import android.content.Context;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.EditText;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.RelativeLayout;
 import android.widget.RelativeLayout.LayoutParams;
 
 public class FindInPageBar extends RelativeLayout implements TextWatcher, View.OnClickListener {
     private static final String LOGTAG = "GeckoFindInPagePopup";
 
     private final Context mContext;
-    private EditText mFindText;
+    private CustomEditText mFindText;
     private boolean mInflated = false;
 
     public FindInPageBar(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
         setFocusable(true);
     }
 
     public void inflateContent() {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         View content = inflater.inflate(R.layout.find_in_page_content, this);
 
         content.findViewById(R.id.find_prev).setOnClickListener(this);
         content.findViewById(R.id.find_next).setOnClickListener(this);
         content.findViewById(R.id.find_close).setOnClickListener(this);
 
-        mFindText = (EditText) content.findViewById(R.id.find_text);
+        mFindText = (CustomEditText) content.findViewById(R.id.find_text);
         mFindText.addTextChangedListener(this);
+        mFindText.setOnKeyPreImeListener(new CustomEditText.OnKeyPreImeListener() {
+            public boolean onKeyPreIme(View v, int keyCode, KeyEvent event) {
+                if (keyCode == KeyEvent.KEYCODE_BACK) {
+                    hide();
+                    return true;
+                }
+                return false;
+            }
+        });
 
         mInflated = true;
     }
 
     public void show() {
         if (!mInflated)
             inflateContent();
 
-        setVisibility(VISIBLE);        
+        setVisibility(VISIBLE);
         mFindText.requestFocus();
+
+        // Show the virtual keyboard.
+        if (mFindText.hasWindowFocus()) {
+            getInputMethodManager(mFindText).showSoftInput(mFindText, 0);
+        } else {
+            // showSoftInput won't work until after the window is focused.
+            mFindText.setOnWindowFocusChangeListener(new CustomEditText.OnWindowFocusChangeListener() {
+               public void onWindowFocusChanged(boolean hasFocus) {
+                   if (!hasFocus)
+                       return;
+                   mFindText.setOnWindowFocusChangeListener(null);
+                   getInputMethodManager(mFindText).showSoftInput(mFindText, 0);
+               }
+            });
+        }
     }
 
     public void hide() {
-        setVisibility(GONE);        
+        setVisibility(GONE);
+        getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Closed", null));
     }
 
+    private InputMethodManager getInputMethodManager(View view) {
+        Context context = view.getContext();
+        return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+     }
+
     // TextWatcher implementation
 
     public void afterTextChanged(Editable s) {
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Find", s.toString()));
     }
 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
         // ignore
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
+import org.mozilla.gecko.gfx.GfxInfoThread;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.ScreenshotLayer;
 import org.mozilla.gecko.FloatUtils;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.ViewportMetrics;
 import org.mozilla.gecko.gfx.RectUtils;
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -15,16 +15,18 @@ import android.widget.AbsoluteLayout;
 import java.io.File;
 import java.io.FilenameFilter;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Date;
 import java.util.Locale;
 import java.util.concurrent.CountDownLatch;
 
+import org.mozilla.gecko.gfx.GfxInfoThread;
+
 public class GeckoThread extends Thread {
     private static final String LOGTAG = "GeckoThread";
 
     Intent mIntent;
     String mUri;
     int mRestoreMode;
     CountDownLatch mStartSignal;
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -71,16 +71,17 @@ FENNEC_JAVA_FILES = \
   INIParser.java \
   INISection.java \
   LinkPreference.java \
   LinkTextView.java \
   MenuItemActionBar.java \
   MenuItemDefault.java \
   MultiChoicePreference.java \
   NSSBridge.java \
+  CustomEditText.java \
   PrivateDataPreference.java \
   PropertyAnimator.java \
   ProfileMigrator.java \
   PromptService.java \
   sqlite/ByteBufferInputStream.java \
   sqlite/MatrixBlobCursor.java \
   sqlite/SQLiteBridge.java \
   sqlite/SQLiteBridgeException.java \
--- a/mobile/android/base/gfx/GfxInfoThread.java
+++ b/mobile/android/base/gfx/GfxInfoThread.java
@@ -1,14 +1,14 @@
 /* -*- 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;
+package org.mozilla.gecko.gfx;
 
 import android.util.Log;
 import java.util.concurrent.SynchronousQueue;
 
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
 import javax.microedition.khronos.egl.EGLDisplay;
@@ -85,16 +85,21 @@ public class GfxInfoThread extends Threa
                                  returnedNumberOfConfigs))
         {
             eglError(egl, "eglChooseConfig failed (querying number of configs)");
             return;
         }
 
         // get the first config
         int numConfigs = returnedNumberOfConfigs[0];
+        if (numConfigs == 0) {
+            error("eglChooseConfig returned zero configs");
+            return;
+        }
+
         EGLConfig[] returnedConfigs = new EGLConfig[numConfigs];
         if (!egl.eglChooseConfig(eglDisplay,
                                  configAttribs,
                                  returnedConfigs,
                                  numConfigs,
                                  returnedNumberOfConfigs))
         {
             eglError(egl, "eglChooseConfig failed (listing configs)");
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -49,16 +49,17 @@
 <!ENTITY forward "Forward">
 <!ENTITY menu "Menu">
 <!ENTITY back "Back">
 <!ENTITY stop "Stop">
 <!ENTITY site_security "Site Security">
 
 <!ENTITY close_tab "Close Tab">
 <!ENTITY new_tab "New Tab">
+<!ENTITY one_tab "1 tab">
 <!-- Localization note (num_tabs) : Number of tabs is always more than one. 
      We can't use android plural forms, sadly. See bug #753859. -->
 <!ENTITY num_tabs "&#037;d tabs">
 <!ENTITY new_tab_opened "New tab opened">
 
 <!ENTITY settings "Settings">
 <!ENTITY settings_title "Settings">
 <!ENTITY pref_category_general "General">
--- a/mobile/android/base/resources/layout-large/awesomebar_search.xml
+++ b/mobile/android/base/resources/layout-large/awesomebar_search.xml
@@ -9,17 +9,17 @@
 
     <ImageView android:id="@+id/dummy_tab"
                style="@style/AddressBar.ImageButton"
                android:layout_width="84dip"
                android:layout_alignParentLeft="true"
                android:background="@drawable/tabs_normal"
                android:gravity="center_vertical"/>
 
-    <view class="org.mozilla.gecko.AwesomeBar$AwesomeBarEditText"
+    <view class="org.mozilla.gecko.CustomEditText"
           android:id="@+id/awesomebar_text"
           style="@style/AddressBar.Button"
           android:background="@drawable/address_bar_url"
           android:layout_marginLeft="0dip"
           android:layout_marginRight="168dip"
           android:layout_marginTop="6dip"
           android:layout_marginBottom="6dip"
           android:layout_toRightOf="@id/dummy_tab"
--- a/mobile/android/base/resources/layout/awesomebar_search.xml
+++ b/mobile/android/base/resources/layout/awesomebar_search.xml
@@ -2,17 +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/. -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 style="@style/AwesomeBar"
                 android:background="@drawable/address_bar_bg">
 
-    <view class="org.mozilla.gecko.AwesomeBar$AwesomeBarEditText"
+    <view class="org.mozilla.gecko.CustomEditText"
           android:id="@+id/awesomebar_text"
           style="@style/AddressBar.Button"
           android:background="@drawable/address_bar_url"
           android:layout_margin="4dip"
           android:layout_alignParentBottom="true"
           android:layout_centerVertical="true"
           android:paddingLeft="15dip"
           android:paddingRight="40dip"
--- a/mobile/android/base/resources/layout/find_in_page_content.xml
+++ b/mobile/android/base/resources/layout/find_in_page_content.xml
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <EditText android:id="@+id/find_text"
+    <view class="org.mozilla.gecko.CustomEditText"
+              android:id="@+id/find_text"
               android:contentDescription="@string/find_text"
               android:background="@drawable/address_bar_url"
               android:singleLine="true"
               android:textColor="#000000"
               android:textCursorDrawable="@null"
               android:inputType="text"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
--- a/mobile/android/base/resources/layout/sync_send_tab.xml
+++ b/mobile/android/base/resources/layout/sync_send_tab.xml
@@ -1,35 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-  style="@style/SyncLayout" >
+  style="@style/SyncLayout.Vertical" >
   <LinearLayout
     android:id="@+id/sendtab_top"
-    style="@style/SyncTop">
+    style="@style/SyncTop" >
     <ImageView
       style="@style/SyncTopIcon" />
     <TextView
       style="@style/SyncTextTitle"
       android:text="@string/sync_title_send_tab" />
   </LinearLayout>
 
-  <LinearLayout
-    android:id="@+id/device_list_layout"
+  <ListView
+    android:id="@+id/device_list"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
     android:layout_below="@id/sendtab_top"
-    android:layout_above="@+id/send_button"
-    style="@style/SyncLayout.Vertical" >
-
-    <ListView
-      android:id="@+id/device_list"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content" >
-    </ListView>
-  </LinearLayout>
+    android:layout_above="@+id/sendtab_bottom" >
+  </ListView>
 
   <LinearLayout
+    android:id="@id/sendtab_bottom"
     style="@style/SyncBottom" >
     <Button
       style="@style/SyncButton"
       android:id="@+id/send_button"
       android:onClick="sendClickHandler"
       android:clickable="false"
       android:enabled="false"
       android:text="@string/sync_button_send" />
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -106,16 +106,17 @@
   <string name="forward">&forward;</string>
   <string name="menu">&menu;</string>
   <string name="back">&back;</string>
   <string name="stop">&stop;</string>
   <string name="site_security">&site_security;</string>
   <string name="close_tab">&close_tab;</string>
   <string name="new_tab">&new_tab;</string>
   <string name="new_tab_opened">&new_tab_opened;</string>
+  <string name="one_tab">&one_tab;</string>
   <string name="num_tabs">&num_tabs;</string>
   <string name="addons">&addons;</string>
   <string name="downloads">&downloads;</string>
   <string name="apps">&apps;</string>
   <string name="char_encoding">&char_encoding;</string>
   <!-- This string only appears in developer builds, which
        is why it is not localizable. -->
   <string name="toggle_profiling">Toggle Profiling</string>
--- a/mobile/android/base/sync/Utils.java
+++ b/mobile/android/base/sync/Utils.java
@@ -10,16 +10,17 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.URLDecoder;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.TreeMap;
 
@@ -486,9 +487,21 @@ public class Utils {
         try {
           fis.close();
         } catch (IOException e) {
           // Ignore.
         }
       }
     }
   }
+
+  /**
+   * Format a duration as a string, like "0.56 seconds".
+   *
+   * @param startMillis start time in milliseconds.
+   * @param endMillis end time in milliseconds.
+   * @return formatted string.
+   */
+  public static String formatDuration(long startMillis, long endMillis) {
+    final long duration = endMillis - startMillis;
+    return new DecimalFormat("#0.00 seconds").format(((double) duration) / 1000);
+  }
 }
--- a/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java
+++ b/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java
@@ -105,28 +105,16 @@ public class Crypto5MiddlewareRepository
       } catch (Exception e) {
         next.onFetchFailed(e, r);
         return;
       }
       next.onFetchedRecord(transformed);
     }
 
     @Override
-    public void onFetchSucceeded(Record[] records, long fetchEnd) {
-      for (Record record : records) {
-        try {
-          this.onFetchedRecord(record);
-        } catch (Exception e) {
-          this.onFetchFailed(e, record);
-        }
-      }
-      this.onFetchCompleted(fetchEnd);
-    }
-
-    @Override
     public void onFetchCompleted(final long fetchEnd) {
       next.onFetchCompleted(fetchEnd);
     }
 
     @Override
     public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor) {
       // Synchronously perform *our* work, passing through appropriately.
       RepositorySessionFetchRecordsDelegate deferredNext = next.deferredFetchDelegate(executor);
--- a/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java
+++ b/mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java
@@ -23,43 +23,33 @@ public class DeferredRepositorySessionFe
       @Override
       public void run() {
          inner.onFetchedRecord(record);
       }
     });
   }
 
   @Override
-  public void onFetchSucceeded(final Record[] records, final long fetchEnd) {
-    executor.execute(new Runnable() {
-      @Override
-      public void run() {
-        inner.onFetchSucceeded(records, fetchEnd);
-      }
-    });       
-  }
-
-  @Override
   public void onFetchFailed(final Exception ex, final Record record) {
     executor.execute(new Runnable() {
       @Override
       public void run() {
         inner.onFetchFailed(ex, record);
       }
-    });       
+    });
   }
 
   @Override
   public void onFetchCompleted(final long fetchEnd) {
     executor.execute(new Runnable() {
       @Override
       public void run() {
         inner.onFetchCompleted(fetchEnd);
       }
-    });        
+    });
   }
 
   @Override
   public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService newExecutor) {
     if (newExecutor == executor) {
       return this;
     }
     throw new IllegalArgumentException("Can't re-defer this delegate.");
--- a/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java
+++ b/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java
@@ -18,25 +18,10 @@ public interface RepositorySessionFetchR
    * @param fetchEnd
    *        A millisecond-resolution timestamp indicating the *remote* timestamp
    *        at the end of the range of records. Usually this is the timestamp at
    *        which the request was received.
    *        E.g., the (normalized) value of the X-Weave-Timestamp header.
    */
   public void onFetchCompleted(final long fetchEnd);
 
-  /**
-   * Shorthand for calling onFetchedRecord for each record in turn, then
-   * calling onFetchCompleted.
-   *
-   * @param records
-   *        An array of fetched records. Can be empty.
-   *
-   * @param fetchEnd
-   *        A millisecond-resolution timestamp indicating the *remote* timestamp
-   *        at the end of the range of records. Usually this is the timestamp at
-   *        which the request was received.
-   *        E.g., the (normalized) value of the X-Weave-Timestamp header.
-   */
-  public void onFetchSucceeded(Record[] records, final long fetchEnd);
-
   public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor);
 }
--- a/mobile/android/base/sync/setup/activities/ClientRecordArrayAdapter.java
+++ b/mobile/android/base/sync/setup/activities/ClientRecordArrayAdapter.java
@@ -1,13 +1,15 @@
 package org.mozilla.gecko.sync.setup.activities;
 
-import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 
 import android.content.Context;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.CheckBox;
 import android.widget.ImageView;
@@ -26,30 +28,37 @@ public class ClientRecordArrayAdapter ex
     this.sendTabActivity = (SendTabActivity) context;
     this.clientRecordList = clientRecordList;
     this.checkedItems = new boolean[clientRecordList.length];
   }
 
   @Override
   public View getView(final int position, View convertView, ViewGroup parent) {
     final Context context = this.getContext();
-    View view = View.inflate(context, R.layout.sync_list_item, null);
-    setSelectable(view, true);
-    view.setBackgroundResource(android.R.drawable.menuitem_background);
+
+    // Reuse View objects if they exist.
+    View row = convertView;
+    if (row == null) {
+      row = View.inflate(context, R.layout.sync_list_item, null);
+      setSelectable(row, true);
+      row.setBackgroundResource(android.R.drawable.menuitem_background);
+    }
 
     final ClientRecord clientRecord = clientRecordList[position];
-    ImageView clientType = (ImageView) view.findViewById(R.id.img);
-    TextView clientName = (TextView) view.findViewById(R.id.client_name);
-    CheckBox checkbox = (CheckBox) view.findViewById(R.id.check);
+    ImageView clientType = (ImageView) row.findViewById(R.id.img);
+    TextView clientName = (TextView) row.findViewById(R.id.client_name);
+    // Set up checkbox and restore stored state.
+    CheckBox checkbox = (CheckBox) row.findViewById(R.id.check);
+    checkbox.setChecked(checkedItems[position]);
     setSelectable(checkbox, false);
 
     clientName.setText(clientRecord.name);
     clientType.setImageResource(getImage(clientRecord));
 
-    view.setOnClickListener(new OnClickListener() {
+    row.setOnClickListener(new OnClickListener() {
       @Override
       public void onClick(View view) {
         CheckBox item = (CheckBox) view.findViewById(R.id.check);
 
         // Update the checked item, both in the UI and in the checkedItems array.
         boolean newCheckedValue = !item.isChecked();
         item.setChecked(newCheckedValue);
         checkedItems[position] = newCheckedValue;
@@ -59,24 +68,24 @@ public class ClientRecordArrayAdapter ex
           sendTabActivity.enableSend(false);
           return;
         }
         sendTabActivity.enableSend(true);
       }
 
     });
 
-    return view;
+    return row;
   }
 
-  public String[] getCheckedGUIDs() {
-    String[] guids = new String[numCheckedGUIDs];
-    for (int i = 0, j = 0; i < checkedItems.length; i++) {
+  public List<String> getCheckedGUIDs() {
+    final List<String> guids = new ArrayList<String>();
+    for (int i = 0; i < checkedItems.length; i++) {
       if (checkedItems[i]) {
-        guids[j++] = clientRecordList[i].guid;
+        guids.add(clientRecordList[i].guid);
       }
     }
     return guids;
   }
 
   public int getNumCheckedGUIDs() {
     return numCheckedGUIDs;
   }
--- a/mobile/android/base/sync/setup/activities/SendTabActivity.java
+++ b/mobile/android/base/sync/setup/activities/SendTabActivity.java
@@ -16,25 +16,26 @@ import org.mozilla.gecko.sync.repositori
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 import org.mozilla.gecko.sync.setup.Constants;
 import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
 import org.mozilla.gecko.sync.syncadapter.SyncAdapter;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.Toast;
 
 public class SendTabActivity extends Activity {
   public static final String LOG_TAG = "SendTabActivity";
-  private ListView listview;
   private ClientRecordArrayAdapter arrayAdapter;
   private AccountManager accountManager;
   private Account localAccount;
 
   @Override
   public void onCreate(Bundle savedInstanceState) {
     setTheme(R.style.SyncTheme);
     super.onCreate(savedInstanceState);
@@ -44,24 +45,39 @@ public class SendTabActivity extends Act
   public void onResume() {
     Logger.info(LOG_TAG, "Called SendTabActivity.onResume.");
     super.onResume();
 
     redirectIfNoSyncAccount();
     registerDisplayURICommand();
 
     setContentView(R.layout.sync_send_tab);
-    arrayAdapter = new ClientRecordArrayAdapter(this, R.layout.sync_list_item, getClientArray());
-
-    listview = (ListView) findViewById(R.id.device_list);
-    listview.setAdapter(arrayAdapter);
+    final ListView listview = (ListView) findViewById(R.id.device_list);
     listview.setItemsCanFocus(true);
     listview.setTextFilterEnabled(true);
     listview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
     enableSend(false);
+
+    // Fetching the client list hits the clients database, so we spin this onto
+    // a background task.
+    final Context context = this;
+    new AsyncTask<Void, Void, ClientRecord[]>() {
+
+      @Override
+      protected ClientRecord[] doInBackground(Void... params) {
+        return getClientArray();
+      }
+
+      @Override
+      protected void onPostExecute(final ClientRecord[] clientArray) {
+        // We're allowed to update the UI from here.
+        arrayAdapter = new ClientRecordArrayAdapter(context, R.layout.sync_list_item, clientArray);
+        listview.setAdapter(arrayAdapter);
+      }
+    }.execute();
   }
 
   private static void registerDisplayURICommand() {
     final CommandProcessor processor = CommandProcessor.getProcessor();
     processor.registerCommand("displayURI", new CommandRunner(3) {
       @Override
       public void executeCommand(final GlobalSession session, List<String> args) {
         CommandProcessor.displayURI(args, session.getContext());
@@ -97,24 +113,33 @@ public class SendTabActivity extends Act
 
   public void sendClickHandler(View view) {
     Logger.info(LOG_TAG, "Send was clicked.");
     Bundle extras = this.getIntent().getExtras();
     final String uri = extras.getString(Intent.EXTRA_TEXT);
     final String title = extras.getString(Intent.EXTRA_SUBJECT);
     final CommandProcessor processor = CommandProcessor.getProcessor();
 
-    final String[] guids = arrayAdapter.getCheckedGUIDs();
+    final String clientGUID = getAccountGUID();
+    final List<String> guids = arrayAdapter.getCheckedGUIDs();
+
+    if (clientGUID == null || guids == null) {
+      // Should never happen.
+      Logger.warn(LOG_TAG, "clientGUID? " + (clientGUID == null) + " or guids? " + (guids == null) +
+          " was null; aborting without sending tab.");
+      finish();
+      return;
+    }
 
     // Perform tab sending on another thread.
     new Thread() {
       @Override
       public void run() {
-        for (int i = 0; i < guids.length; i++) {
-          processor.sendURIToClientForDisplay(uri, guids[i], title, getAccountGUID(), getApplicationContext());
+        for (String guid : guids) {
+          processor.sendURIToClientForDisplay(uri, guid, title, clientGUID, getApplicationContext());
         }
 
         Logger.info(LOG_TAG, "Requesting immediate clients stage sync.");
         SyncAdapter.requestImmediateSync(localAccount, new String[] { SyncClientsEngineStage.COLLECTION_NAME });
       }
     }.start();
 
     notifyAndFinish();
--- a/mobile/android/base/sync/stage/ServerSyncStage.java
+++ b/mobile/android/base/sync/stage/ServerSyncStage.java
@@ -35,16 +35,17 @@ import org.mozilla.gecko.sync.repositori
 import org.mozilla.gecko.sync.repositories.Server11Repository;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
 import org.mozilla.gecko.sync.synchronizer.ServerLocalSynchronizer;
 import org.mozilla.gecko.sync.synchronizer.Synchronizer;
 import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
+import org.mozilla.gecko.sync.synchronizer.SynchronizerSession;
 
 import android.content.Context;
 
 /**
  * Fetch from a server collection into a local repository, encrypting
  * and decrypting along the way.
  *
  * @author rnewman
@@ -53,16 +54,19 @@ import android.content.Context;
 public abstract class ServerSyncStage implements
     GlobalSyncStage,
     SynchronizerDelegate {
 
   protected static final String LOG_TAG = "ServerSyncStage";
 
   protected final GlobalSession session;
 
+  protected long stageStartTimestamp = -1;
+  protected long stageCompleteTimestamp = -1;
+
   public ServerSyncStage(GlobalSession session) {
     if (session == null) {
       throw new IllegalArgumentException("session must not be null.");
     }
     this.session = session;
   }
 
   /**
@@ -445,16 +449,18 @@ public abstract class ServerSyncStage im
     Logger.info(LOG_TAG, "Wiping server complete.");
   }
 
   @Override
   public void execute() throws NoSuchStageException {
     final String name = getEngineName();
     Logger.debug(LOG_TAG, "Starting execute for " + name);
 
+    stageStartTimestamp = System.currentTimeMillis();
+
     try {
       if (!this.isEnabled()) {
         Logger.info(LOG_TAG, "Skipping stage " + name + ".");
         session.advance();
         return;
       }
     } catch (MetaGlobalException.MetaGlobalMalformedSyncIDException e) {
       // Bad engine syncID. This should never happen. Wipe the server.
@@ -507,55 +513,72 @@ public abstract class ServerSyncStage im
     }
 
     Logger.debug(LOG_TAG, "Invoking synchronizer.");
     synchronizer.synchronize(session.getContext(), this);
     Logger.debug(LOG_TAG, "Reached end of execute.");
   }
 
   /**
+   * Express the duration taken by this stage as a String, like "0.56 seconds".
+   *
+   * @return formatted string.
+   */
+  protected String getStageDurationString() {
+    return Utils.formatDuration(stageStartTimestamp, stageCompleteTimestamp);
+  }
+
+  /**
    * We synced this engine!  Persist timestamps and advance the session.
    *
    * @param synchronizer the <code>Synchronizer</code> that succeeded.
    */
   @Override
   public void onSynchronized(Synchronizer synchronizer) {
+    stageCompleteTimestamp = System.currentTimeMillis();
     Logger.debug(LOG_TAG, "onSynchronized.");
 
     SynchronizerConfiguration newConfig = synchronizer.save();
     if (newConfig != null) {
       persistConfig(newConfig);
     } else {
       Logger.warn(LOG_TAG, "Didn't get configuration from synchronizer after success.");
     }
 
+    final SynchronizerSession synchronizerSession = synchronizer.getSynchronizerSession();
+    int inboundCount = synchronizerSession.getInboundCount();
+    int outboundCount = synchronizerSession.getOutboundCount();
+    Logger.info(LOG_TAG, "Received " + inboundCount + " and sent " + outboundCount +
+        " records in " + getStageDurationString() + ".");
     Logger.info(LOG_TAG, "Advancing session.");
     session.advance();
   }
 
   /**
    * We failed to sync this engine! Do not persist timestamps (which means that
    * the next sync will include this sync's data), but do advance the session
    * (if we didn't get a Retry-After header).
    *
    * @param synchronizer the <code>Synchronizer</code> that failed.
    */
   @Override
   public void onSynchronizeFailed(Synchronizer synchronizer,
                                   Exception lastException, String reason) {
+    stageCompleteTimestamp = System.currentTimeMillis();
     Logger.warn(LOG_TAG, "Synchronize failed: " + reason, lastException);
 
     // This failure could be due to a 503 or a 401 and it could have headers.
     // Interrogate the headers but only abort the global session if Retry-After header is set.
     if (lastException instanceof HTTPFailureException) {
       SyncStorageResponse response = ((HTTPFailureException)lastException).response;
       if (response.retryAfterInSeconds() > 0) {
         session.handleHTTPError(response, reason); // Calls session.abort().
         return;
       } else {
         session.interpretHTTPFailure(response.httpResponse()); // Does not call session.abort().
       }
     }
 
-    Logger.info(LOG_TAG, "Advancing session even though stage failed. Timestamps not persisted.");
+    Logger.info(LOG_TAG, "Advancing session even though stage failed (took " + getStageDurationString() +
+        "). Timestamps not persisted.");
     session.advance();
   }
 }
--- a/mobile/android/base/sync/syncadapter/SyncAdapter.java
+++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java
@@ -61,16 +61,18 @@ public class SyncAdapter extends Abstrac
   private static final int     SHARED_PREFERENCES_MODE = 0;
   private static final int     BACKOFF_PAD_SECONDS = 5;
   public  static final int     MULTI_DEVICE_INTERVAL_MILLISECONDS = 5 * 60 * 1000;         // 5 minutes.
   public  static final int     SINGLE_DEVICE_INTERVAL_MILLISECONDS = 24 * 60 * 60 * 1000;  // 24 hours.
 
   private final AccountManager mAccountManager;
   private final Context        mContext;
 
+  protected long syncStartTimestamp;
+
   public SyncAdapter(Context context, boolean autoInitialize) {
     super(context, autoInitialize);
     mContext = context;
     mAccountManager = AccountManager.get(context);
   }
 
   public static SharedPreferences getGlobalPrefs(Context context) {
     return context.getSharedPreferences("sync.prefs.global", SHARED_PREFERENCES_MODE);
@@ -249,17 +251,16 @@ public class SyncAdapter extends Abstrac
   }
 
   @Override
   public void onPerformSync(final Account account,
                             final Bundle extras,
                             final String authority,
                             final ContentProviderClient provider,
                             final SyncResult syncResult) {
-
     Logger.resetLogging();
     Utils.reseedSharedRandom(); // Make sure we don't work with the same random seed for too long.
 
     // Set these so that we don't need to thread them through assorted calls and callbacks.
     this.syncResult   = syncResult;
     this.localAccount = account;
 
     Log.i(LOG_TAG,
@@ -375,16 +376,17 @@ public class SyncAdapter extends Abstrac
 
       Logger.trace(LOG_TAG, "Waiting on sync monitor.");
       try {
         syncMonitor.wait();
         long interval = getSyncInterval();
         long next = System.currentTimeMillis() + interval;
         Log.i(LOG_TAG, "Setting minimum next sync time to " + next + " (" + interval + "ms from now).");
         extendEarliestNextSync(next);
+        Log.i(LOG_TAG, "Sync took " + Utils.formatDuration(syncStartTimestamp, System.currentTimeMillis()) + ".");
       } catch (InterruptedException e) {
         Log.w(LOG_TAG, "Waiting on sync monitor interrupted.", e);
       } finally {
         // And we're done with HTTP stuff.
         stale.shutdown();
       }
     }
  }
@@ -411,49 +413,64 @@ public class SyncAdapter extends Abstrac
    * @throws IllegalArgumentException
    * @throws SyncConfigurationException
    * @throws AlreadySyncingException
    * @throws NonObjectJSONException
    * @throws ParseException
    * @throws IOException
    * @throws CryptoException
    */
-  protected void performSync(Account account, Bundle extras, String authority,
-                             ContentProviderClient provider,
-                             SyncResult syncResult,
-                             String username, String password,
-                             String prefsPath,
-                             String serverURL,
-                             String syncKey)
+  protected void performSync(final Account account,
+                             final Bundle extras,
+                             final String authority,
+                             final ContentProviderClient provider,
+                             final SyncResult syncResult,
+                             final String username,
+                             final String password,
+                             final String prefsPath,
+                             final String serverURL,
+                             final String syncKey)
                                  throws NoSuchAlgorithmException,
                                         SyncConfigurationException,
                                         IllegalArgumentException,
                                         AlreadySyncingException,
                                         IOException, ParseException,
                                         NonObjectJSONException, CryptoException {
     Logger.trace(LOG_TAG, "Performing sync.");
+    syncStartTimestamp = System.currentTimeMillis();
 
     /**
      * Bug 769745: pickle Sync account parameters to JSON file. Un-pickle in
      * <code>SyncAccounts.syncAccountsExist</code>.
      */
     try {
       // Constructor can throw on nulls, which should not happen -- but let's be safe.
       final SyncAccountParameters params = new SyncAccountParameters(mContext, mAccountManager,
         account.name, // Un-encoded, like "test@mozilla.com".
         syncKey,
         password,
         serverURL,
         null, // We'll re-fetch cluster URL; not great, but not harmful.
         getClientName(),
         getAccountGUID());
 
-        final boolean syncAutomatically = ContentResolver.getSyncAutomatically(account, authority);
-
-        AccountPickler.pickle(mContext, Constants.ACCOUNT_PICKLE_FILENAME, params, syncAutomatically);
+      // Bug 772971: pickle Sync account parameters on background thread to
+      // avoid strict mode warnings.
+      ThreadPool.run(new Runnable() {
+        @Override
+        public void run() {
+          final boolean syncAutomatically = ContentResolver.getSyncAutomatically(account, authority);
+          try {
+            AccountPickler.pickle(mContext, Constants.ACCOUNT_PICKLE_FILENAME, params, syncAutomatically);
+          } catch (Exception e) {
+            // Should never happen, but we really don't want to die in a background thread.
+            Logger.warn(LOG_TAG, "Got exception pickling current account details; ignoring.", e);
+          }
+        }
+      });
     } catch (IllegalArgumentException e) {
       // Do nothing.
     }
 
     // TODO: default serverURL.
     final KeyBundle keyBundle = new KeyBundle(username, syncKey);
     GlobalSession globalSession = new GlobalSession(SyncConfiguration.DEFAULT_USER_API,
                                                     serverURL, username, password, prefsPath,
--- a/mobile/android/base/sync/synchronizer/RecordsChannel.java
+++ b/mobile/android/base/sync/synchronizer/RecordsChannel.java
@@ -68,18 +68,20 @@ public class RecordsChannel implements
 
   private static final String LOG_TAG = "RecordsChannel";
   public RepositorySession source;
   public RepositorySession sink;
   private RecordsChannelDelegate delegate;
   private long timestamp;
   private long fetchEnd = -1;
 
-  private final AtomicInteger numFetchFailed = new AtomicInteger();
-  private final AtomicInteger numStoreFailed = new AtomicInteger();
+  protected final AtomicInteger numFetched = new AtomicInteger();
+  protected final AtomicInteger numFetchFailed = new AtomicInteger();
+  protected final AtomicInteger numStored = new AtomicInteger();
+  protected final AtomicInteger numStoreFailed = new AtomicInteger();
 
   public RecordsChannel(RepositorySession source, RepositorySession sink, RecordsChannelDelegate delegate) {
     this.source    = source;
     this.sink      = sink;
     this.delegate  = delegate;
     this.timestamp = source.lastSyncTimestamp;
   }
 
@@ -101,25 +103,45 @@ public class RecordsChannel implements
     return toProcess;
   }
 
   protected boolean isReady() {
     return source.isActive() && sink.isActive();
   }
 
   /**
+   * Get the number of records fetched so far.
+   *
+   * @return number of fetches.
+   */
+  public int getFetchCount() {
+    return numFetched.get();
+  }
+
+  /**
    * Get the number of fetch failures recorded so far.
+   *
    * @return number of fetch failures.
    */
   public int getFetchFailureCount() {
     return numFetchFailed.get();
   }
 
   /**
+   * Get the number of store attempts (successful or not) so far.
+   *
+   * @return number of stores attempted.
+   */
+  public int getStoreCount() {
+    return numStored.get();
+  }
+
+  /**
    * Get the number of store failures recorded so far.
+   *
    * @return number of store failures.
    */
   public int getStoreFailureCount() {
     return numStoreFailed.get();
   }
 
   /**
    * Start records flowing through the channel.
@@ -129,17 +151,19 @@ public class RecordsChannel implements
       RepositorySession failed = source;
       if (source.isActive()) {
         failed = sink;
       }
       this.delegate.onFlowBeginFailed(this, new SessionNotBegunException(failed));
       return;
     }
     sink.setStoreDelegate(this);
+    numFetched.set(0);
     numFetchFailed.set(0);
+    numStored.set(0);
     numStoreFailed.set(0);
     // Start a consumer thread.
     this.consumer = new ConcurrentRecordConsumer(this);
     ThreadPool.run(this.consumer);
     waitingForQueueDone = true;
     source.fetchSince(timestamp, this);
   }
 
@@ -149,16 +173,17 @@ public class RecordsChannel implements
    */
   public void beginAndFlow() throws InvalidSessionTransitionException {
     Logger.trace(LOG_TAG, "Beginning source.");
     source.begin(this);
   }
 
   @Override
   public void store(Record record) {
+    numStored.incrementAndGet();
     try {
       sink.store(record);
     } catch (NoStoreDelegateException e) {
       Logger.error(LOG_TAG, "Got NoStoreDelegateException in RecordsChannel.store(). This should not occur. Aborting.", e);
       delegate.onFlowStoreFailed(this, e, record.guid);
     }
   }
 
@@ -167,30 +192,22 @@ public class RecordsChannel implements
     Logger.warn(LOG_TAG, "onFetchFailed. Calling for immediate stop.", ex);
     numFetchFailed.incrementAndGet();
     this.consumer.halt();
     delegate.onFlowFetchFailed(this, ex);
   }
 
   @Override
   public void onFetchedRecord(Record record) {
+    numFetched.incrementAndGet();
     this.toProcess.add(record);
     this.consumer.doNotify();
   }
 
   @Override
-  public void onFetchSucceeded(Record[] records, final long fetchEnd) {
-    for (Record record : records) {
-      this.toProcess.add(record);
-    }
-    this.consumer.doNotify();
-    this.onFetchCompleted(fetchEnd);
-  }
-
-  @Override
   public void onFetchCompleted(final long fetchEnd) {
     Logger.trace(LOG_TAG, "onFetchCompleted. Stopping consumer once stores are done.");
     Logger.trace(LOG_TAG, "Fetch timestamp is " + fetchEnd);
     this.fetchEnd = fetchEnd;
     this.consumer.queueFilled();
   }
 
   @Override
--- a/mobile/android/base/sync/synchronizer/ServerLocalSynchronizer.java
+++ b/mobile/android/base/sync/synchronizer/ServerLocalSynchronizer.java
@@ -6,12 +6,13 @@ package org.mozilla.gecko.sync.synchroni
 
 /**
  * A <code>SynchronizerSession</code> designed to be used between a remote
  * server and a local repository.
  * <p>
  * See <code>ServerLocalSynchronizerSession</code> for error handling details.
  */
 public class ServerLocalSynchronizer extends Synchronizer {
-  public SynchronizerSession getSynchronizerSession() {
+  @Override
+  public SynchronizerSession newSynchronizerSession() {
     return new ServerLocalSynchronizerSession(this, this);
   }
 }
--- a/mobile/android/base/sync/synchronizer/Synchronizer.java
+++ b/mobile/android/base/sync/synchronizer/Synchronizer.java
@@ -29,16 +29,22 @@ import android.content.Context;
  */
 public class Synchronizer implements SynchronizerSessionDelegate {
   public static final String LOG_TAG = "SyncDelSDelegate";
 
   protected String configSyncID; // Used to pass syncID from load() back into save().
 
   protected SynchronizerDelegate synchronizerDelegate;
 
+  protected SynchronizerSession session = null;
+
+  public SynchronizerSession getSynchronizerSession() {
+    return session;
+  }
+
   @Override
   public void onInitialized(SynchronizerSession session) {
     session.synchronize();
   }
 
   @Override
   public void onSynchronized(SynchronizerSession synchronizerSession) {
     Logger.debug(LOG_TAG, "Got onSynchronized.");
@@ -62,27 +68,27 @@ public class Synchronizer implements Syn
   public Repository repositoryA;
   public Repository repositoryB;
   public RepositorySessionBundle bundleA;
   public RepositorySessionBundle bundleB;
 
   /**
    * Fetch a synchronizer session appropriate for this <code>Synchronizer</code>
    */
-  public SynchronizerSession getSynchronizerSession() {
+  protected SynchronizerSession newSynchronizerSession() {
     return new SynchronizerSession(this, this);
   }
 
   /**
    * Start synchronizing, calling delegate's callback methods.
    */
   public void synchronize(Context context, SynchronizerDelegate delegate) {
     this.synchronizerDelegate = delegate;
-    SynchronizerSession session = getSynchronizerSession();
-    session.init(context, bundleA, bundleB);
+    this.session = newSynchronizerSession();
+    this.session.init(context, bundleA, bundleB);
   }
 
   public SynchronizerConfiguration save() {
     return new SynchronizerConfiguration(configSyncID, bundleA, bundleB);
   }
 
   /**
    * Set my repository session bundles from a SynchronizerConfiguration.
--- a/mobile/android/base/sync/synchronizer/SynchronizerSession.java
+++ b/mobile/android/base/sync/synchronizer/SynchronizerSession.java
@@ -1,16 +1,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.sync.synchronizer;
 
 
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
 import org.mozilla.gecko.sync.repositories.RepositorySession;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 import org.mozilla.gecko.sync.repositories.delegates.DeferrableRepositorySessionCreationDelegate;
 import org.mozilla.gecko.sync.repositories.delegates.DeferredRepositorySessionFinishDelegate;
@@ -69,16 +70,19 @@ implements RecordsChannelDelegate,
   // that a concurrently syncing client has uploaded.
   private long pendingATimestamp = -1;
   private long pendingBTimestamp = -1;
   private long storeEndATimestamp = -1;
   private long storeEndBTimestamp = -1;
   private boolean flowAToBCompleted = false;
   private boolean flowBToACompleted = false;
 
+  protected final AtomicInteger numInboundRecords = new AtomicInteger(-1);
+  protected final AtomicInteger numOutboundRecords = new AtomicInteger(-1);
+
   /*
    * Public API: constructor, init, synchronize.
    */
   public SynchronizerSession(Synchronizer synchronizer, SynchronizerSessionDelegate delegate) {
     this.setSynchronizer(synchronizer);
     this.delegate = delegate;
   }
 
@@ -93,25 +97,52 @@ implements RecordsChannelDelegate,
   public void init(Context context, RepositorySessionBundle bundleA, RepositorySessionBundle bundleB) {
     this.context = context;
     this.bundleA = bundleA;
     this.bundleB = bundleB;
     // Begin sessionA and sessionB, call onInitialized in callbacks.
     this.getSynchronizer().repositoryA.createSession(this, context);
   }
 
+  /**
+   * Get the number of records fetched from the first repository (usually the
+   * server, hence inbound).
+   * <p>
+   * Valid only after first flow has completed.
+   *
+   * @return number of records, or -1 if not valid.
+   */
+  public int getInboundCount() {
+    return numInboundRecords.get();
+  }
+
+  /**
+   * Get the number of records fetched from the second repository (usually the
+   * local store, hence outbound).
+   * <p>
+   * Valid only after second flow has completed.
+   *
+   * @return number of records, or -1 if not valid.
+   */
+  public int getOutboundCount() {
+    return numOutboundRecords.get();
+  }
+
   // These are accessed by `abort` and `synchronize`, both of which are synchronized.
   // Guarded by `this`.
   protected RecordsChannel channelAToB;
   protected RecordsChannel channelBToA;
 
   /**
    * Please don't call this until you've been notified with onInitialized.
    */
   public synchronized void synchronize() {
+    numInboundRecords.set(-1);
+    numOutboundRecords.set(-1);
+
     // First thing: decide whether we should.
     if (!sessionA.dataAvailable() &&
         !sessionB.dataAvailable()) {
       Logger.info(LOG_TAG, "Neither session reports data available. Short-circuiting sync.");
       sessionA.abort();
       sessionB.abort();
       this.delegate.onSynchronizeSkipped(this);
       return;
@@ -173,16 +204,17 @@ implements RecordsChannelDelegate,
    * @param fetchEnd timestamp when fetches completed.
    * @param storeEnd timestamp when stores completed.
    */
   public void onFirstFlowCompleted(RecordsChannel recordsChannel, long fetchEnd, long storeEnd) {
     Logger.trace(LOG_TAG, "First RecordsChannel onFlowCompleted.");
     Logger.debug(LOG_TAG, "Fetch end is " + fetchEnd + ". Store end is " + storeEnd + ". Starting next.");
     pendingATimestamp = fetchEnd;
     storeEndBTimestamp = storeEnd;
+    numInboundRecords.set(recordsChannel.getFetchCount());
     flowAToBCompleted = true;
     channelBToA.flow();
   }
 
   /**
    * Called after the second flow completes.
    * <p>
    * By default, any fetch and store failures are ignored.
@@ -191,16 +223,17 @@ implements RecordsChannelDelegate,
    * @param storeEnd timestamp when stores completed.
    */
   public void onSecondFlowCompleted(RecordsChannel recordsChannel, long fetchEnd, long storeEnd) {
     Logger.trace(LOG_TAG, "Second RecordsChannel onFlowCompleted.");
     Logger.debug(LOG_TAG, "Fetch end is " + fetchEnd + ". Store end is " + storeEnd + ". Finishing.");
 
     pendingBTimestamp = fetchEnd;
     storeEndATimestamp = storeEnd;
+    numOutboundRecords.set(recordsChannel.getFetchCount());
     flowBToACompleted = true;
 
     // Finish the two sessions.
     try {
       this.sessionA.finish(this);
     } catch (InactiveSessionException e) {
       this.onFinishFailed(e);
       return;
--- a/mobile/android/branding/beta/content/Makefile.in
+++ b/mobile/android/branding/beta/content/Makefile.in
@@ -10,16 +10,16 @@ topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/rules.mk
 
 LINUX_BRANDING_FILES = \
-	fennec_40x40.png \
+	fennec_48x48.png \
 	fennec_72x72.png \
 	$(NULL)
 
 export::
 	$(NSINSTALL) -D $(DIST)/branding
 	cp $(addprefix $(srcdir)/, $(LINUX_BRANDING_FILES)) $(DIST)/branding/
 	$(NSINSTALL) -D $(DIST)/install
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3231,16 +3231,18 @@ Tab.prototype = {
     Ci.nsIWebProgressListener,
     Ci.nsISHistoryListener,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
     Ci.nsIBrowserTab
   ])
 };
 
+const kTapHighlightDelay = 50; // milliseconds
+
 var BrowserEventHandler = {
   init: function init() {
     Services.obs.addObserver(this, "Gesture:SingleTap", false);
     Services.obs.addObserver(this, "Gesture:CancelTouch", false);
     Services.obs.addObserver(this, "Gesture:DoubleTap", false);
     Services.obs.addObserver(this, "Gesture:Scroll", false);
     Services.obs.addObserver(this, "dom-touch-listener-added", false);
 
@@ -3462,22 +3464,32 @@ var BrowserEventHandler = {
   },
 
   _firstScrollEvent: false,
 
   _scrollableElement: null,
 
   _highlightElement: null,
 
+  _highlightTimeout: null,
+
   _doTapHighlight: function _doTapHighlight(aElement) {
-    DOMUtils.setContentState(aElement, kStateActive);
-    this._highlightElement = aElement;
+    this._cancelTapHighlight();
+    this._highlightTimeout = setTimeout(function(self) {
+      DOMUtils.setContentState(aElement, kStateActive);
+      self._highlightElement = aElement;
+    }, kTapHighlightDelay, this);
   },
 
   _cancelTapHighlight: function _cancelTapHighlight() {
+    if (this._highlightTimeout) {
+      clearTimeout(this._highlightTimeout);
+      this._highlightTimeout = null;
+    }
+
     if (!this._highlightElement)
       return;
 
     // If the active element is in a sub-frame, we need to make that frame's document
     // active to remove the element's active state.
     if (this._highlightElement.ownerDocument != BrowserApp.selectedBrowser.contentWindow.document)
       DOMUtils.setContentState(this._highlightElement.ownerDocument.documentElement, kStateActive);
 
@@ -5983,16 +5995,23 @@ var WebappsUI = {
     favicon.onload = function() {
       ctx.drawImage(favicon, 0, 0, kIconSize, kIconSize);
       let base64icon = canvas.toDataURL("image/png", "");
       canvas = null;
       aCallbackFunction.call(null, base64icon);
     };
     favicon.onerror = function() {
       Cu.reportError("CreateShortcut: favicon image load error");
+
+      // if the image failed to load, and it was not our default icon, attempt to
+      // use our default as a fallback
+      let uri = Services.io.newURI(favicon.src, null, null);
+      if (!/^chrome$/.test(uri.scheme)) {
+        favicon.src = WebappsUI.getBiggestIcon(null);
+      }
     };
   
     favicon.src = aIconURL;
   },
 
   createShortcut: function createShortcut(aTitle, aURL, aIconURL, aType) {
     this.makeBase64Icon(aIconURL, function _createShortcut(icon) {
       try {
--- a/mobile/android/components/DirectoryProvider.js
+++ b/mobile/android/components/DirectoryProvider.js
@@ -21,32 +21,17 @@ DirectoryProvider.prototype = {
   classID: Components.ID("{ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}"),
   
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
 
   getFile: function(prop, persistent) {
     if (prop == NS_APP_CACHE_PARENT_DIR) {
       let dirsvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
       let profile = dirsvc.get("ProfD", Ci.nsIFile);
-
-      let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
-      let device = sysInfo.get("device");
-      switch (device) {
-#ifdef MOZ_PLATFORM_MAEMO
-        case "Nokia N900":
-          return profile;
-        
-        case "Nokia N8xx":
-          let folder = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
-          folder.initWithPath("/media/mmc2/.mozilla/fennec");
-          return folder;
-#endif
-        default:
-          return profile;
-      }
+      return profile;
     } else if (prop == XRE_UPDATE_ROOT_DIR) {
       let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
       if (env.exists(ENVVAR_UPDATE_DIR)) {
         let path = env.get(ENVVAR_UPDATE_DIR);
         if (path) {
           let localFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
           localFile.initWithPath(path);
           return localFile;
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -35,19 +35,16 @@ category app-startup SessionStore servic
 category agent-style-sheets browser-content-stylesheet chrome://browser/skin/content.css
 
 # ContentPermissionPrompt.js
 component {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} ContentPermissionPrompt.js
 contract @mozilla.org/content-permission/prompt;1 {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}
 
 # AlertsService.js
 component {fe33c107-82a4-41d6-8c64-5353267e04c9} AlertsService.js
-#ifndef ANDROID
-contract @mozilla.org/system-alerts-service;1 {fe33c107-82a4-41d6-8c64-5353267e04c9}
-#endif
 
 # ToasterAlertsService : alias to AlertsService
 contract @mozilla.org/toaster-alerts-service;1 {fe33c107-82a4-41d6-8c64-5353267e04c9}
 
 # XPIDialogService.js
 component {c1242012-27d8-477e-a0f1-0b098ffc329b} XPIDialogService.js
 contract @mozilla.org/addons/web-install-prompt;1 {c1242012-27d8-477e-a0f1-0b098ffc329b}
 
--- a/mobile/android/components/build/Makefile.in
+++ b/mobile/android/components/build/Makefile.in
@@ -23,18 +23,9 @@ XPIDL_MODULE = browsercomps
 
 XPIDLSRCS = nsIShellService.idl
 
 CPPSRCS = \
 	nsBrowserModule.cpp \
 	nsShellService.cpp \
 	$(NULL)
 
-ifeq ($(MOZ_PLATFORM_MAEMO),5)
-LOCAL_INCLUDES   += $(MOZ_DBUS_GLIB_CFLAGS)
-endif
-
-ifneq (,$(filter $(MOZ_WIDGET_TOOLKIT),qt))
-LOCAL_INCLUDES += $(MOZ_QT_CFLAGS)
-endif
-
 include $(topsrcdir)/config/rules.mk
-
--- a/mobile/android/components/build/nsShellService.cpp
+++ b/mobile/android/components/build/nsShellService.cpp
@@ -1,65 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#if (MOZ_PLATFORM_MAEMO == 5)
-#include <dbus/dbus.h>
-#endif
-
-#ifdef MOZ_WIDGET_QT
-#include <QtGui/QApplication>
-#include <QtGui/QWidget>
-#endif
-
-#ifdef MOZ_WIDGET_ANDROID
-#include "AndroidBridge.h"
-#endif
-
 #include "nsShellService.h"
 #include "nsString.h"
 
+#include "AndroidBridge.h"
+
 NS_IMPL_ISUPPORTS1(nsShellService, nsIShellService)
 
 NS_IMETHODIMP
 nsShellService::SwitchTask()
 {
-#if (MOZ_PLATFORM_MAEMO == 5)
-  DBusError error;
-  dbus_error_init(&error);
-
-  DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
-
-  DBusMessage *msg = dbus_message_new_signal("/com/nokia/hildon_desktop",
-                                             "com.nokia.hildon_desktop",
-                                             "exit_app_view");
-
-  if (msg) {
-      dbus_connection_send(conn, msg, NULL);
-      dbus_message_unref(msg);
-      dbus_connection_flush(conn);
-  }
-  return NS_OK;
-#elif MOZ_WIDGET_QT
-  QWidget * window = QApplication::activeWindow();
-  if (window)
-      window->showMinimized();
-  return NS_OK;
-#else
   return NS_ERROR_NOT_IMPLEMENTED;
-#endif
 }
 
 NS_IMETHODIMP
 nsShellService::CreateShortcut(const nsAString& aTitle, const nsAString& aURI, const nsAString& aIconData, const nsAString& aIntent)
 {
   if (!aTitle.Length() || !aURI.Length() || !aIconData.Length())
     return NS_ERROR_FAILURE;
 
-#if MOZ_WIDGET_ANDROID
   mozilla::AndroidBridge::Bridge()->CreateShortcut(aTitle, aURI, aIconData, aIntent);
   return NS_OK;
-#else
-  return NS_ERROR_NOT_IMPLEMENTED;
-#endif
 }
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -9,131 +9,76 @@
 ; [] designates a toplevel component. Example: [xpcom]
 ; - in front of a file specifies it to be removed from the destination
 ; * wildcard support to recursively copy the entire directory
 ; ; file comment
 ;
 
 #filter substitution
 
-#ifdef XP_MACOSX
-; Mac bundle stuff
-@APPNAME@/Contents/Info.plist
-@APPNAME@/Contents/PkgInfo
-@APPNAME@/Contents/Plug-Ins/
-@APPNAME@/Contents/Resources/
-#endif
-
 [@AB_CD@]
 @BINPATH@/chrome/@AB_CD@@JAREXT@
 @BINPATH@/chrome/@AB_CD@.manifest
 @BINPATH@/@PREF_DIR@/mobile-l10n.js
 @BINPATH@/searchplugins/*
 @BINPATH@/defaults/profile/bookmarks.html
 @BINPATH@/defaults/profile/localstore.rdf
 @BINPATH@/defaults/profile/mimeTypes.rdf
 @BINPATH@/defaults/profile/chrome/*
 #ifdef MOZ_UPDATER
 @BINPATH@/update.locale
 @BINPATH@/updater.ini
 #endif
 @BINPATH@/dictionaries/*
 @BINPATH@/hyphenation/*
-#ifdef XP_WIN32
-@BINPATH@/uninstall/helper.exe
-#endif
 
 [xpcom]
 @BINPATH@/dependentlibs.list
 #ifndef MOZ_STATIC_JS
 @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
 #endif
 @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@xpcom@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
-#ifdef XP_MACOSX
-@BINPATH@/XUL
-#else
 @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
-#endif
-#ifdef XP_MACOSX
-@BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
-#else
 @BINPATH@/@MOZ_CHILD_PROCESS_NAME@
-#endif
-#ifdef XP_WIN32
-#if _MSC_VER == 1400
-@BINPATH@/Microsoft.VC80.CRT.manifest
-@BINPATH@/msvcm80.dll
-@BINPATH@/msvcp80.dll
-@BINPATH@/msvcr80.dll
-#elif _MSC_VER == 1500
-@BINPATH@/Microsoft.VC90.CRT.manifest
-@BINPATH@/msvcm90.dll
-@BINPATH@/msvcp90.dll
-@BINPATH@/msvcr90.dll
-#elif _MSC_VER == 1600
-@BINPATH@/msvcp100.dll
-@BINPATH@/msvcr100.dll
-#elif _MSC_VER == 1700
-@BINPATH@/msvcp110.dll
-@BINPATH@/msvcr110.dll
-#endif
 
-#endif
-
-#ifdef ANDROID
 @BINPATH@/AndroidManifest.xml
 @BINPATH@/resources.arsc
 @BINPATH@/package-name.txt
 @BINPATH@/classes.dex
 @BINPATH@/res/drawable
 @BINPATH@/res/drawable-hdpi
 @BINPATH@/res/layout
 @BINPATH@/recommended-addons.json
-#endif
-
-#ifdef MOZ_PLATFORM_MAEMO
-@BINPATH@/res/drawable
-#endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
 @BINPATH@/@MOZ_APP_NAME@
 #endif
 @BINPATH@/application.ini
 @BINPATH@/platform.ini
-#ifndef XP_OS2
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
-#else
-@BINPATH@/mozsqlt3@DLL_SUFFIX@
-#endif
 @BINPATH@/blocklist.xml
 #ifdef XP_UNIX
 @BINPATH@/run-mozilla.sh
-#ifndef XP_MACOSX
 @BINPATH@/mozilla-xremote-client
 #endif
-#endif
 
 ; [Components]
 @BINPATH@/components/components.manifest
 @BINPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
-#ifdef XP_WIN32
-@BINPATH@/AccessibleMarshal.dll
-@BINPATH@/components/accessibility-msaa.xpt
-#endif
 @BINPATH@/components/accessibility.xpt
 #endif
 @BINPATH@/components/appshell.xpt
 @BINPATH@/components/appstartup.xpt
 @BINPATH@/components/autocomplete.xpt
 @BINPATH@/components/autoconfig.xpt
 @BINPATH@/components/browsercompsbase.xpt
 @BINPATH@/components/browser-feeds.xpt
@@ -270,22 +215,17 @@
 @BINPATH@/components/update.xpt
 #endif
 @BINPATH@/components/uriloader.xpt
 @BINPATH@/components/urlformatter.xpt
 @BINPATH@/components/webBrowser_core.xpt
 @BINPATH@/components/webbrowserpersist.xpt
 @BINPATH@/components/webshell_idls.xpt
 @BINPATH@/components/widget.xpt
-#ifdef XP_MACOSX
-@BINPATH@/components/widget_cocoa.xpt
-#endif
-#ifdef ANDROID
 @BINPATH@/components/widget_android.xpt
-#endif
 @BINPATH@/components/windowds.xpt
 @BINPATH@/components/windowwatcher.xpt
 @BINPATH@/components/xpcom_base.xpt
 @BINPATH@/components/xpcom_system.xpt
 @BINPATH@/components/xpcom_components.xpt
 @BINPATH@/components/xpcom_ds.xpt
 @BINPATH@/components/xpcom_io.xpt
 @BINPATH@/components/xpcom_threads.xpt
@@ -359,21 +299,17 @@
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/pluginGlue.manifest
 @BINPATH@/components/nsSessionStore.manifest
 @BINPATH@/components/nsSessionStartup.js
 @BINPATH@/components/nsSessionStore.js
 @BINPATH@/components/nsURLFormatter.manifest
 @BINPATH@/components/nsURLFormatter.js
-#ifndef XP_OS2
 @BINPATH@/components/@DLL_PREFIX@browsercomps@DLL_SUFFIX@
-#else
-@BINPATH@/components/brwsrcmp@DLL_SUFFIX@
-#endif
 @BINPATH@/components/txEXSLTRegExFunctions.manifest
 @BINPATH@/components/txEXSLTRegExFunctions.js
 @BINPATH@/components/nsDefaultCLH.manifest
 @BINPATH@/components/nsDefaultCLH.js
 @BINPATH@/components/nsContentPrefService.manifest
 @BINPATH@/components/nsContentPrefService.js
 @BINPATH@/components/nsContentDispatchChooser.manifest
 @BINPATH@/components/nsContentDispatchChooser.js
@@ -390,19 +326,16 @@
 @BINPATH@/components/contentSecurityPolicy.manifest
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/nsFilePicker.js
 @BINPATH@/components/nsFilePicker.manifest
-#ifdef XP_MACOSX
-@BINPATH@/components/libalerts.dylib
-#endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 #ifdef MOZ_SERVICES_SYNC
@@ -428,51 +361,39 @@
 @BINPATH@/components/nsUrlClassifierLib.js
 @BINPATH@/components/url-classifier.xpt
 
 ; GNOME hooks
 #ifdef MOZ_ENABLE_GNOME_COMPONENT
 @BINPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
 #endif
 
-; ANGLE on Win32
-#ifdef XP_WIN32
-#ifndef HAVE_64BIT_OS
-@BINPATH@/libEGL.dll
-@BINPATH@/libGLESv2.dll
-#endif
-#endif
-
 ; [Browser Chrome Files]
 @BINPATH@/chrome/browser@JAREXT@
 @BINPATH@/chrome/browser.manifest
 @BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
 @BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
 @BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/preview.png
 #if MOZ_UPDATE_CHANNEL == beta
 @BINPATH@/extensions/testpilot@labs.mozilla.com/*
 #endif
 @BINPATH@/chrome/toolkit@JAREXT@
 @BINPATH@/chrome/toolkit.manifest
 #ifdef XP_UNIX
-#ifndef XP_MACOSX
 @BINPATH@/chrome/icons/default/default16.png
 @BINPATH@/chrome/icons/default/default32.png
 @BINPATH@/chrome/icons/default/default48.png
 #endif
-#endif
 
 
 ; shell icons
 #ifdef XP_UNIX
-#ifndef XP_MACOSX
 @BINPATH@/icons/*.xpm
 @BINPATH@/icons/*.png
 #endif
-#endif
 
 ; [Default Preferences]
 ; All the pref files must be part of base to prevent migration bugs
 @BINPATH@/@PREF_DIR@/mobile.js
 @BINPATH@/@PREF_DIR@/mobile-branding.js
 @BINPATH@/@PREF_DIR@/channel-prefs.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/@PREF_DIR@/services-sync.js
@@ -503,28 +424,22 @@
 @BINPATH@/res/table-add-row-before.gif
 @BINPATH@/res/table-remove-column-active.gif
 @BINPATH@/res/table-remove-column-hover.gif
 @BINPATH@/res/table-remove-column.gif
 @BINPATH@/res/table-remove-row-active.gif
 @BINPATH@/res/table-remove-row-hover.gif
 @BINPATH@/res/table-remove-row.gif
 @BINPATH@/res/grabber.gif
-#ifdef XP_MACOSX
-@BINPATH@/res/cursors/*
-#endif
 @BINPATH@/res/fonts/*
 @BINPATH@/res/dtd/*
 @BINPATH@/res/html/*
 @BINPATH@/res/langGroups.properties
 @BINPATH@/res/language.properties
 @BINPATH@/res/entityTables/*
-#ifdef XP_MACOSX
-@BINPATH@/res/MainMenu.nib/
-#endif
 
 ; svg
 @BINPATH@/res/svg.css
 @BINPATH@/components/dom_svg.xpt
 @BINPATH@/components/dom_smil.xpt
 
 ; [Personal Security Manager]
 ;
@@ -559,50 +474,37 @@ bin/libfreebl_32int_3.chk
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.chk
 bin/libfreebl_32int64_3.so
 #endif
 
 ; [Updater]
 ;
 #ifdef MOZ_UPDATER
-#ifdef XP_MACOSX
-@BINPATH@/updater.app/
-#else
 @BINPATH@/updater@BIN_SUFFIX@
 #endif
-#endif
 
 ; [Crash Reporter]
 ;
 #ifdef MOZ_CRASHREPORTER
-#ifdef XP_MACOSX
-@BINPATH@/crashreporter.app/
-#else
 @BINPATH@/crashreporter@BIN_SUFFIX@
 @BINPATH@/crashreporter.crt
 @BINPATH@/crashreporter.ini
 #ifdef XP_UNIX
 @BINPATH@/Throbber-small.gif
 #endif
-#endif
 @BINPATH@/crashreporter-override.ini
 #endif
 
 ; [Extensions]
 ;
 #ifdef MOZ_ENABLE_GNOMEVFS
 bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
 #endif
 
-; [OS/2]
-#ifdef XP_OS2
-@BINPATH@/MozSounds.cmd
-#endif
-
 [mobile]
 @BINPATH@/chrome/icons/
 @BINPATH@/chrome/chrome@JAREXT@
 @BINPATH@/chrome/chrome.manifest
 @BINPATH@/components/AboutRedirector.js
 @BINPATH@/components/AddonUpdateService.js
 @BINPATH@/components/AlertsService.js
 @BINPATH@/components/BlocklistPrompt.js
--- a/mobile/android/installer/removed-files.in
+++ b/mobile/android/installer/removed-files.in
@@ -1,33 +1,9 @@
 update.locale
 README.txt
 components/nsTryToClose.js
 components/CapturePicker.js
 #if MOZ_UPDATE_CHANNEL != beta
 extensions/feedback@mobile.mozilla.org.xpi
 #endif
-#ifdef XP_WIN
-  #if _MSC_VER != 1400
-    Microsoft.VC80.CRT.manifest
-    msvcm80.dll
-    msvcp80.dll
-    msvcr80.dll
-  #endif
-  #if _MSC_VER != 1500
-    Microsoft.VC90.CRT.manifest
-    msvcm90.dll
-    msvcp90.dll
-    msvcr90.dll
-  #endif
-  #if _MSC_VER != 1600
-    msvcp100.dll
-    msvcr100.dll
-  #endif
-  #if _MSC_VER != 1700
-    msvcp110.dll
-    msvcr110.dll
-  #endif
-  mozcrt19.dll
-  mozcpp19.dll
-#endif
 @DLL_PREFIX@mozutils@DLL_SUFFIX@
 jssubloader/
--- a/mobile/android/modules/contacts.jsm
+++ b/mobile/android/modules/contacts.jsm
@@ -45,82 +45,8 @@ let Contacts = {
       this._contacts.forEach(function(aContact) {
         if (field in aContact && aContact[field].indexOf(match) != -1)
           results.push(aContact);
       });
     }
     return results;
   }
 };
-
-#ifndef ANDROID
-#ifndef XP_MACOSX
-#ifdef XP_UNIX
-Cu.import("resource://gre/modules/ctypes.jsm");
-Cu.import("resource:///modules/linuxTypes.jsm");
-
-function EBookProvider() {
-  EBook.init();
-}
-
-EBookProvider.prototype = {
-  getContacts: function() {
-    if (!EBook.lib) {
-      Cu.reportError("EBook not loaded")
-      return [];
-    }
-
-    let gError = new GLib.GError.ptr;
-    let book = EBook.openSystem(gError.address());
-    if (!book) {
-      Cu.reportError("EBook.openSystem: " + gError.contents.message.readString())
-      return [];
-    }
-
-    if (!EBook.openBook(book, false, gError.address())) {
-      Cu.reportError("EBook.openBook: " + gError.contents.message.readString())
-      return [];
-    }
-
-    let query = EBook.queryAnyFieldContains("");
-    if (query) {
-      let gList = new GLib.GList.ptr();
-      if (!EBook.getContacts(book, query, gList.address(),  gError.address())) {
-        Cu.reportError("EBook.getContacts: " + gError.contents.message.readString())
-        return [];
-      }
-
-      let contacts = [];
-      while (gList && !gList.isNull()) {
-        let fullName = EBook.getContactField(gList.contents.data, EBook.E_CONTACT_FULL_NAME);
-        if (!fullName.isNull()) {
-          let contact = {};
-          contact.fullName = fullName.readString();
-          contact.emails = [];
-          contact.phoneNumbers = [];
-
-          for (let emailIndex=EBook.E_CONTACT_EMAIL_FIRST; emailIndex<=EBook.E_CONTACT_EMAIL_LAST; emailIndex++) {
-            let email = EBook.getContactField(gList.contents.data, emailIndex);
-            if (!email.isNull())
-              contact.emails.push(email.readString());
-          }
-
-          for (let phoneIndex=EBook.E_CONTACT_PHONE_FIRST; phoneIndex<=EBook.E_CONTACT_PHONE_LAST; phoneIndex++) {
-            let phone = EBook.getContactField(gList.contents.data, phoneIndex);
-            if (!phone.isNull())
-              contact.phoneNumbers.push(phone.readString());
-          }
-
-          contacts.push(contact);
-        }
-        gList = ctypes.cast(gList.contents.next, GLib.GList.ptr);
-      }
-      return contacts;
-    }
-    return [];
-  }
-};
-
-Contacts.addProvider(new EBookProvider);
-# XP_UNIX
-#endif
-#endif
-#endif
--- a/mobile/android/themes/core/defines.inc
+++ b/mobile/android/themes/core/defines.inc
@@ -114,15 +114,9 @@
 %define dialog_width 76.2mozmm
 
 %define appmenu_portrait_height 21.17mozmm
 %define appmenu_button_height  10.48mozmm
 
 %define tablet_panel_controls  40mozmm
 %define tablet_panel_minwidth  124mozmm
 
-%ifdef MOZ_PLATFORM_MAEMO
 %define orientation -moz-device-orientation
-%elifdef ANDROID
-%define orientation -moz-device-orientation
-%else
-%define orientation orientation
-%endif
--- a/modules/libjar/Makefile.in
+++ b/modules/libjar/Makefile.in
@@ -33,12 +33,8 @@ LIBXUL_LIBRARY = 1
 
 CPPSRCS		= $(MODULES_LIBJAR_LCPPSRCS)
 
 XPIDLSRCS	= $(MODULES_LIBJAR_LXPIDLSRCS)
 
 EXPORTS		= $(MODULES_LIBJAR_LEXPORTS)
 
 include $(topsrcdir)/config/rules.mk
-
-ifeq ($(OS_ARCH),WINNT)
-DEFINES		+= -DZLIB_DLL=1
-endif
--- a/modules/libjar/nsIJARChannel.idl
+++ b/modules/libjar/nsIJARChannel.idl
@@ -10,9 +10,14 @@ interface nsIJARChannel : nsIChannel
 {
     /**
      * Returns TRUE if the JAR file is not safe (if the content type reported
      * by the server for a remote JAR is not of an expected type).  Scripting,
      * redirects, and plugins should be disabled when loading from this
      * channel.
      */
     readonly attribute boolean isUnsafe;
+
+    /**
+     * Forces the uri to be a app:// uri.
+     */
+    void setAppURI(in nsIURI uri);
 };
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -181,17 +181,18 @@ nsJARInputThunk::IsNonBlocking(bool *non
 }
 
 //-----------------------------------------------------------------------------
 // nsJARChannel
 //-----------------------------------------------------------------------------
 
 
 nsJARChannel::nsJARChannel()
-    : mContentLength(-1)
+    : mAppURI(nsnull)
+    , mContentLength(-1)
     , mLoadFlags(LOAD_NORMAL)
     , mStatus(NS_OK)
     , mIsPending(false)
     , mIsUnsafe(true)
     , mJarInput(nsnull)
 {
 #if defined(PR_LOGGING)
     if (!gJarProtocolLog)
@@ -471,17 +472,22 @@ nsJARChannel::SetOriginalURI(nsIURI *aUR
     NS_ENSURE_ARG_POINTER(aURI);
     mOriginalURI = aURI;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetURI(nsIURI **aURI)
 {
-    NS_IF_ADDREF(*aURI = mJarURI);
+    if (mAppURI) {
+        NS_IF_ADDREF(*aURI = mAppURI);
+    } else {
+        NS_IF_ADDREF(*aURI = mJarURI);
+    }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetOwner(nsISupports **result)
 {
     nsresult rv;
 
@@ -753,16 +759,30 @@ nsJARChannel::AsyncOpen(nsIStreamListene
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 nsJARChannel::GetIsUnsafe(bool *isUnsafe)
 {
     *isUnsafe = mIsUnsafe;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsJARChannel::SetAppURI(nsIURI *aURI) {
+    NS_ENSURE_ARG_POINTER(aURI);
+
+    nsCAutoString scheme;
+    aURI->GetScheme(scheme);
+    if (!scheme.EqualsLiteral("app")) {
+        return NS_ERROR_INVALID_ARG;
+    }
+
+    mAppURI = aURI;
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsIDownloadObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
                                  nsIRequest    *request,
                                  nsISupports   *context,
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -50,16 +50,17 @@ private:
     nsresult EnsureJarInput(bool blocking);
 
 #if defined(PR_LOGGING)
     nsCString                       mSpec;
 #endif
 
     nsCOMPtr<nsIJARURI>             mJarURI;
     nsCOMPtr<nsIURI>                mOriginalURI;
+    nsCOMPtr<nsIURI>                mAppURI;
     nsCOMPtr<nsISupports>           mOwner;
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsISupports>           mSecurityInfo;
     nsCOMPtr<nsIProgressEventSink>  mProgressSink;
     nsCOMPtr<nsILoadGroup>          mLoadGroup;
     nsCOMPtr<nsIStreamListener>     mListener;
     nsCOMPtr<nsISupports>           mListenerContext;
     nsCString                       mContentType;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -690,16 +690,25 @@ pref("javascript.options.mem.high_water_
 pref("javascript.options.mem.max", -1);
 pref("javascript.options.mem.gc_per_compartment", true);
 pref("javascript.options.mem.disable_explicit_compartment_gc", true);
 pref("javascript.options.mem.gc_incremental", true);
 pref("javascript.options.mem.gc_incremental_slice_ms", 10);
 pref("javascript.options.mem.log", false);
 pref("javascript.options.gc_on_memory_pressure", true);
 
+pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1000);
+pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100);
+pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 500);
+pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
+pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 150);
+pref("javascript.options.mem.gc_low_frequency_heap_growth", 150);
+pref("javascript.options.mem.gc_dynamic_heap_growth", true);
+pref("javascript.options.mem.gc_dynamic_mark_slice", true);
+
 // advanced prefs
 pref("advanced.mailftp",                    false);
 pref("image.animation_mode",                "normal");
 
 // Same-origin policy for file URIs, "false" is traditional
 pref("security.fileuri.strict_origin_policy", true);
 
 // If there is ever a security firedrill that requires
@@ -3616,8 +3625,10 @@ pref("memory.low_physical_memory_thresho
 // low_memory_notification_interval_ms.
 pref("memory.low_memory_notification_interval_ms", 10000);
 #endif
 
 // How long must we wait before declaring that a window is a "ghost" (i.e., a
 // likely leak)?  This should be longer than it usually takes for an eligible
 // window to be collected via the GC/CC.
 pref("memory.ghost_window_timeout_seconds", 60);
+
+pref("social.enabled", false);
--- a/modules/zlib/src/Makefile.in
+++ b/modules/zlib/src/Makefile.in
@@ -8,22 +8,15 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(srcdir)/objs.mk
 
 MODULE		= zlib
 LIBRARY_NAME	= mozz
-GRE_MODULE	= 1
-LIBXUL_LIBRARY = 1
-DIST_INSTALL = 1
-
-ifeq (,$(filter-out WINNT OS2,$(OS_ARCH)))
-DEFINES	+= -DZLIB_DLL=1
-endif
 
 CSRCS		= $(MODULES_ZLIB_SRC_LCSRCS)
 
 EXPORTS		= zlib.h zconf.h mozzconf.h
 
 
 include $(topsrcdir)/config/rules.mk
--- a/modules/zlib/src/mozzconf.h
+++ b/modules/zlib/src/mozzconf.h
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZZCONF_H
 #define MOZZCONF_H
 
-#if defined(XP_WIN) && defined(ZLIB_DLL)
-#undef ZLIB_DLL
+#if defined(XP_WIN)
+#define ZLIB_DLL 1
 #endif
 
 /* Exported Symbols */
 #define zlibVersion MOZ_Z_zlibVersion
 #define deflate MOZ_Z_deflate
 #define deflateEnd MOZ_Z_deflateEnd
 #define inflate MOZ_Z_inflate
 #define inflateEnd MOZ_Z_inflateEnd
--- a/mozglue/build/Makefile.in
+++ b/mozglue/build/Makefile.in
@@ -51,16 +51,18 @@ mozglue.def: mozglue.def.in
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(ACDEFINES) $< > $@
 
 GARBAGE += mozglue.def
 
 ifneq (,$(filter -DEFAULTLIB:mozcrt,$(MOZ_GLUE_LDFLAGS)))
 # Don't install the import library if we use mozcrt
 NO_INSTALL_IMPORT_LIBRARY = 1
 endif
+
+EXTRA_DSO_LDOPTS += $(MOZ_ZLIB_LIBS)
 endif
 
 ifeq (Android,$(OS_TARGET))
 # To properly wrap jemalloc's pthread_atfork call.
 EXTRA_DSO_LDOPTS += -Wl,--wrap=pthread_atfork
 CPPSRCS += BionicGlue.cpp
 SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,$(DEPTH)/other-licenses/android)
 endif
--- a/netwerk/base/public/nsIApplicationCache.idl
+++ b/netwerk/base/public/nsIApplicationCache.idl
@@ -74,17 +74,17 @@ interface nsIApplicationCacheNamespace :
  * types, as discussed in the WHAT-WG offline applications
  * specification.
  *
  * All application caches with the same group ID belong to a cache
  * group.  Each group has one "active" cache that will service future
  * loads.  Inactive caches will be removed from the cache when they are
  * no longer referenced.
  */
-[scriptable, uuid(231e1e53-05c1-41b6-b9de-dbbcce9385c9)]
+[scriptable, uuid(C3A17414-763B-4235-8BB7-B48324F95DF8)]
 interface nsIApplicationCache : nsISupports
 {
     /**
      * Init this application cache instance to just hold the group ID and
      * the client ID to work just as a handle to the real cache. Used on
      * content process to simplify the application cache code.
      */
     void initAsHandle(in ACString groupId, in ACString clientId);
@@ -190,10 +190,10 @@ interface nsIApplicationCache : nsISuppo
      * Get the most specific namespace matching a given key.
      */
     nsIApplicationCacheNamespace getMatchingNamespace(in ACString key);
 
     /**
      * If set, this offline cache is placed in a different directory
      * than the current application profile.
      */
-    readonly attribute nsIFile cacheDirectory;
+    readonly attribute nsIFile profileDirectory;
 };
--- a/netwerk/base/public/nsIApplicationCacheChannel.idl
+++ b/netwerk/base/public/nsIApplicationCacheChannel.idl
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIApplicationCacheContainer.idl"
 
 /**
  * Interface implemented by channels that support application caches.
  */
-[scriptable, uuid(8d9024e6-ab01-442d-8119-2f55d55d91b0)]
+[scriptable, uuid(6FA816B1-6D5F-4380-9704-054D0908CFA3)]
 interface nsIApplicationCacheChannel : nsIApplicationCacheContainer
 {
     /**
      * TRUE when the resource came from the application cache. This
      * might be false even there is assigned an application cache
      * e.g. in case of fallback of load of an entry matching bypass
      * namespace.
      */
@@ -44,9 +44,15 @@ interface nsIApplicationCacheChannel : n
     attribute boolean chooseApplicationCache;
 
     /**
      * A shortcut method to mark the cache item of this channel as 'foreign'.
      * See the 'cache selection algorithm' and CACHE_SELECTION_RELOAD
      * action handling in nsContentSink.
      */
     void markOfflineCacheEntryAsForeign();
+
+    /**
+     * Set offline application cache object to instruct the channel
+     * to cache for offline use using this application cache.
+     */
+    attribute nsIApplicationCache applicationCacheForWrite;
 };
--- a/netwerk/base/public/nsICachingChannel.idl
+++ b/netwerk/base/public/nsICachingChannel.idl
@@ -12,17 +12,17 @@ interface nsIFile;
  * to affect its behavior with respect to how it uses the cache service.
  *
  * This interface provides:
  *   1) Support for "stream as file" semantics (for JAR and plugins).
  *   2) Support for "pinning" cached data in the cache (for printing and save-as).
  *   3) Support for uniquely identifying cached data in cases when the URL
  *      is insufficient (e.g., HTTP form submission).
  */
-[scriptable, uuid(E2143B61-62FE-4da5-BE2E-E31981095889)]
+[scriptable, uuid(8456EBEE-7127-485b-9518-C2E328C09F5D)]
 interface nsICachingChannel : nsICacheInfoChannel
 {
     /**
      * Set/get the cache token... uniquely identifies the data in the cache.
      * Holding a reference to this token prevents the cached data from being
      * removed.
      * 
      * A cache token retrieved from a particular instance of nsICachingChannel
@@ -69,36 +69,16 @@ interface nsICachingChannel : nsICacheIn
      * may fail if the disk cache is not present.  The value of this attribute
      * is usually only settable during the processing of a channel's
      * OnStartRequest.  The default value of this attribute depends on the
      * particular implementation of nsICachingChannel.
      */
     attribute boolean cacheAsFile;
 
     /**
-     * Specifies whether or not the data should be placed in the offline cache,
-     * in addition to normal memory/disk caching.  This may fail if the offline
-     * cache is not present.  The value of this attribute should be set before
-     * opening the channel.
-     */
-    attribute boolean cacheForOfflineUse;
-
-    /**
-     * The session into which to cache offline data.  If not specified,
-     * data will be placed in "HTTP-offline"
-     */
-    attribute ACString offlineCacheClientID;
-
-    /**
-     * Override base (profile) directory to work with when accessing the cache.
-     * When not specified, the current process' profile directory will be used.
-     */
-    attribute nsIFile profileDirectory;
-
-    /**
      * Get the "file" where the cached data can be found.  This is valid for
      * as long as a reference to the cache token is held.  This may return
      * an error if cacheAsFile is false.
      */
     readonly attribute nsIFile cacheFile;
 
     /**************************************************************************
      * Caching channel specific load flags:
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -1583,16 +1583,17 @@ nsCacheService::GetCustomOfflineDevice(n
     nsAutoString profilePath;
     rv = aProfileDir->GetPath(profilePath);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
         rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
         NS_ENSURE_SUCCESS(rv, rv);
 
+        (*aDevice)->SetAutoShutdown();
         mCustomOfflineDevices.Put(profilePath, *aDevice);
     }
 
     return NS_OK;
 }
 
 nsresult
 nsCacheService::CreateOfflineDevice()
@@ -1671,16 +1672,30 @@ nsCacheService::CreateMemoryDevice()
 
     MemoryCacheReporter =
         new NS_MEMORY_REPORTER_NAME(NetworkMemoryCache);
     NS_RegisterMemoryReporter(MemoryCacheReporter);
 
     return rv;
 }
 
+nsresult
+nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
+{
+    nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
+    if (!profileDir)
+        return NS_ERROR_UNEXPECTED;
+
+    nsAutoString profilePath;
+    nsresult rv = profileDir->GetPath(profilePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mCustomOfflineDevices.Remove(profilePath);
+    return NS_OK;
+}
 
 nsresult
 nsCacheService::CreateRequest(nsCacheSession *   session,
                               const nsACString & clientKey,
                               nsCacheAccessMode  accessRequested,
                               bool               blockingMode,
                               nsICacheListener * listener,
                               nsCacheRequest **  request)
--- a/netwerk/cache/nsCacheService.h
+++ b/netwerk/cache/nsCacheService.h
@@ -194,16 +194,18 @@ private:
 
     nsresult         CreateDiskDevice();
     nsresult         CreateOfflineDevice();
     nsresult         CreateCustomOfflineDevice(nsIFile *aProfileDir,
                                                PRInt32 aQuota,
                                                nsOfflineCacheDevice **aDevice);
     nsresult         CreateMemoryDevice();
 
+    nsresult         RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice);
+
     nsresult         CreateRequest(nsCacheSession *   session,
                                    const nsACString & clientKey,
                                    nsCacheAccessMode  accessRequested,
                                    bool               blockingMode,
                                    nsICacheListener * listener,
                                    nsCacheRequest **  request);
 
     nsresult         DoomEntry_Internal(nsCacheEntry * entry,
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -654,17 +654,17 @@ nsApplicationCache::GetGroupID(nsACStrin
 NS_IMETHODIMP
 nsApplicationCache::GetClientID(nsACString &out)
 {
   out = mClientID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsApplicationCache::GetCacheDirectory(nsIFile **out)
+nsApplicationCache::GetProfileDirectory(nsIFile **out)
 {
   if (mDevice->BaseDirectory())
       NS_ADDREF(*out = mDevice->BaseDirectory());
   else
       *out = nsnull;
 
   return NS_OK;
 }
@@ -680,16 +680,20 @@ nsApplicationCache::GetActive(bool *out)
 
 NS_IMETHODIMP
 nsApplicationCache::Activate()
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   mDevice->ActivateCache(mGroup, mClientID);
+
+  if (mDevice->AutoShutdown(this))
+    mDevice = nsnull;
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::Discard()
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
@@ -825,16 +829,17 @@ private:
  */
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(nsOfflineCacheDevice)
 
 nsOfflineCacheDevice::nsOfflineCacheDevice()
   : mDB(nsnull)
   , mCacheCapacity(0)
   , mDeltaCounter(0)
+  , mAutoShutdown(false)
 {
 }
 
 /* static */
 bool
 nsOfflineCacheDevice::GetStrictFileOriginPolicy()
 {
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@@ -2413,8 +2418,28 @@ nsOfflineCacheDevice::SetCacheParentDire
   mCacheDirectory = do_QueryInterface(dir);
 }
 
 void
 nsOfflineCacheDevice::SetCapacity(PRUint32 capacity)
 {
   mCacheCapacity = capacity * 1024;
 }
+
+bool
+nsOfflineCacheDevice::AutoShutdown(nsIApplicationCache * aAppCache)
+{
+  if (!mAutoShutdown)
+    return false;
+
+  mAutoShutdown = false;
+
+  Shutdown();
+
+  nsRefPtr<nsCacheService> cacheService = nsCacheService::GlobalInstance();
+  cacheService->RemoveCustomOfflineDevice(this);
+
+  nsCAutoString clientID;
+  aAppCache->GetClientID(clientID);
+  mCaches.Remove(clientID);
+
+  return true;
+}
--- a/netwerk/cache/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.h
@@ -162,16 +162,18 @@ public:
                                                char ***keys);
 
   /**
    * Preference accessors
    */
 
   void                    SetCacheParentDirectory(nsIFile * parentDir);
   void                    SetCapacity(PRUint32  capacity);
+  void                    SetAutoShutdown() { mAutoShutdown = true; }
+  bool                    AutoShutdown(nsIApplicationCache * aAppCache);
 
   nsIFile *               BaseDirectory() { return mBaseDirectory; }
   nsIFile *               CacheDirectory() { return mCacheDirectory; }
   PRUint32                CacheCapacity() { return mCacheCapacity; }
   PRUint32                CacheSize();
   PRUint32                EntryCount();
   
 private:
@@ -252,16 +254,17 @@ private:
   nsCOMPtr<mozIStorageStatement>  mStatement_FindClientByNamespace;
   nsCOMPtr<mozIStorageStatement>  mStatement_EnumerateGroups;
   nsCOMPtr<mozIStorageStatement>  mStatement_EnumerateGroupsTimeOrder;
 
   nsCOMPtr<nsIFile>               mBaseDirectory;
   nsCOMPtr<nsIFile>               mCacheDirectory;
   PRUint32                        mCacheCapacity; // in bytes
   PRInt32                         mDeltaCounter;
+  bool                            mAutoShutdown;
 
   nsInterfaceHashtable<nsCStringHashKey, nsIWeakReference> mCaches;
   nsClassHashtable<nsCStringHashKey, nsCString> mActiveCachesByGroup;
   nsTHashtable<nsCStringHashKey> mActiveCaches;
 
   nsCOMPtr<nsIThread> mInitThread;
 };
 
--- a/netwerk/cache/nsMemoryCacheDevice.cpp
+++ b/netwerk/cache/nsMemoryCacheDevice.cpp
@@ -23,17 +23,16 @@
 // values for entries.
 // Entries larger than 2^(kQueueCount-1) go in the last queue.
 // Entries with no expiration go in the first queue.
 
 const char *gMemoryDeviceID      = "memory";
 
 nsMemoryCacheDevice::nsMemoryCacheDevice()
     : mInitialized(false),
-      mEvictionThreshold(PR_INT32_MAX),
       mHardLimit(4 * 1024 * 1024),       // default, if no pref
       mSoftLimit((mHardLimit * 9) / 10), // default, if no pref
       mTotalSize(0),
       mInactiveSize(0),
       mEntryCount(0),
       mMaxEntryCount(0),
       mMaxEntrySize(-1) // -1 means "no limit"
 {
--- a/netwerk/cache/nsMemoryCacheDevice.h
+++ b/netwerk/cache/nsMemoryCacheDevice.h
@@ -82,17 +82,16 @@ private:
     enum {
         kQueueCount = 24   // entries > 2^23 (8Mb) start in last queue
     };
 
     nsCacheEntryHashTable  mMemCacheEntries;
     bool                   mInitialized;
 
     PRCList                mEvictionList[kQueueCount];
-    PRInt32                mEvictionThreshold;
 
     PRInt32                mHardLimit;
     PRInt32                mSoftLimit;
 
     PRInt32                mTotalSize;
     PRInt32                mInactiveSize;
 
     PRInt32                mEntryCount;
--- a/netwerk/protocol/Makefile.in
+++ b/netwerk/protocol/Makefile.in
@@ -7,16 +7,17 @@ DEPTH     = ../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 PARALLEL_DIRS = \
   about \
+  app \
   data \
   device \
   file \
   ftp \
   http \
   res \
   viewsource \
   websocket \
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/app/AppProtocolHandler.js
@@ -0,0 +1,88 @@
+/* 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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"]
+         .getService(Ci.nsIFrameMessageManager)
+         .QueryInterface(Ci.nsISyncMessageSender);
+});
+
+function AppProtocolHandler() {
+  this._basePath = null;
+}
+
+AppProtocolHandler.prototype = {
+  classID: Components.ID("{b7ad6144-d344-4687-b2d0-b6b9dce1f07f}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
+
+  scheme: "app",
+  defaultPort: -1,
+  // Using the same flags as the JAR protocol handler.
+  protocolFlags2: Ci.nsIProtocolHandler.URI_NORELATIVE |
+                  Ci.nsIProtocolHandler.URI_NOAUTH |
+                  Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,
+
+  get basePath() {
+    if (!this._basePath) {
+      this._basePath = cpmm.sendSyncMessage("Webapps:GetBasePath", { })[0] + "/";
+    }
+
+    return this._basePath;
+  },
+
+  newURI: function app_phNewURI(aSpec, aOriginCharset, aBaseURI) {
+    let uri = Cc["@mozilla.org/network/standard-url;1"]
+              .createInstance(Ci.nsIStandardURL);
+    uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD, -1, aSpec, aOriginCharset,
+             aBaseURI);
+    return uri.QueryInterface(Ci.nsIURI);
+  },
+
+  newChannel: function app_phNewChannel(aURI) {
+    // We map app://ABCDEF/path/to/file.ext to
+    // jar:file:///path/to/profile/webapps/ABCDEF/application.zip!/path/to/file.ext
+    let noScheme = aURI.spec.substring(6);
+    let firstSlash = noScheme.indexOf("/");
+
+    let appId = noScheme;
+    let fileSpec = aURI.path;
+
+    if (firstSlash) {
+      appId = noScheme.substring(0, firstSlash);
+    }
+
+    // Simulates default behavior of http servers:
+    // Adds index.html if the file spec ends in / in /#anchor
+    let lastSlash = fileSpec.lastIndexOf("/");
+    if (lastSlash == fileSpec.length - 1) {
+      fileSpec += "index.html";
+    } else if (fileSpec[lastSlash + 1] == '#') {
+      let anchor = fileSpec.substring(lastSlash + 1);
+      fileSpec = fileSpec.substring(0, lastSlash) + "/index.html" + anchor;
+    }
+
+    // Build a jar channel and masquerade as an app:// URI.
+    let uri = "jar:file://" + this.basePath + appId + "/application.zip!" + fileSpec;
+    let channel = Services.io.newChannel(uri, null, null);
+    channel.QueryInterface(Ci.nsIJARChannel).setAppURI(aURI);
+    channel.QueryInterface(Ci.nsIChannel).originalURI = aURI;
+
+    return channel;
+  },
+
+  allowPort: function app_phAllowPort(aPort, aScheme) {
+    return false;
+  }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([AppProtocolHandler]);
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/app/AppProtocolHandler.manifest
@@ -0,0 +1,3 @@
+# AppProtocolHander.js
+component {b7ad6144-d344-4687-b2d0-b6b9dce1f07f} AppProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=app {b7ad6144-d344-4687-b2d0-b6b9dce1f07f}
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/app/Makefile.in
@@ -0,0 +1,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/.
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_COMPONENTS = \
+  AppProtocolHandler.js \
+  AppProtocolHandler.manifest \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1217,16 +1217,31 @@ HttpChannelChild::SetApplicationCache(ns
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIApplicationCacheChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
+HttpChannelChild::GetApplicationCacheForWrite(nsIApplicationCache **aApplicationCache)
+{
+  *aApplicationCache = nsnull;
+  return NS_OK;
+}
+NS_IMETHODIMP
+HttpChannelChild::SetApplicationCacheForWrite(nsIApplicationCache *aApplicationCache)
+{
+  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+  // Child channels are not intended to be used for cache writes
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 HttpChannelChild::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache)
 {
   *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::GetInheritApplicationCache(bool *aInherit)
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -196,17 +196,17 @@ public:
                    bool noWait,
                    bool usingSSL,
                    bool loadedFromApplicationCache)
         // in
         : mChannel(channel)
         , mHasQueryString(HasQueryString(channel->mRequestHead.Method(),
                                          channel->mURI))
         , mLoadFlags(channel->mLoadFlags)
-        , mCacheForOfflineUse(channel->mCacheForOfflineUse)
+        , mCacheForOfflineUse(!!channel->mApplicationCacheForWrite)
         , mFallbackChannel(channel->mFallbackChannel)