Merge from mozilla-inbound.
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 26 Jan 2013 14:11:02 +0100
changeset 138146 8a3901221fd9bc9bb39e759f4a49bedffbaec287
parent 138145 1cc0768e68c499567bcda62144a04c18f41f2d53 (current diff)
parent 129811 358dd2a3299058e813a16760d41294fb6886bc46 (diff)
child 138147 13f62a92819a26d1591f4482ed84b545978e7e39
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.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 from mozilla-inbound.
dom/devicestorage/test/test_stat.html
dom/interfaces/devicestorage/nsIDOMDeviceStorageStat.idl
js/src/ion/Bailouts.cpp
js/src/ion/BaselineCompiler.cpp
js/src/ion/BaselineIC.cpp
js/src/ion/Ion.cpp
js/src/ion/Ion.h
js/src/ion/IonCaches.cpp
js/src/ion/IonFrames-inl.h
js/src/ion/IonFrames.cpp
js/src/ion/IonMacroAssembler.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/arm/Trampoline-arm.cpp
js/src/ion/x64/MacroAssembler-x64.h
js/src/ion/x86/MacroAssembler-x86.h
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jsinterp.cpp
js/src/jsscript.cpp
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/ForkJoin-inl.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
mobile/android/base/resources/drawable/tabs_tray_default_selector.xml
--- a/b2g/app/BootAnimation.cpp
+++ b/b2g/app/BootAnimation.cpp
@@ -8,34 +8,32 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include <GLES2/gl2.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
 #include <algorithm>
 #include <endian.h>
 #include <fcntl.h>
 #include <string>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <vector>
+#include "mozilla/FileUtils.h"
+#include "mozilla/NullPtr.h"
 #include "mozilla/Util.h"
-#include "mozilla/NullPtr.h"
 #include "png.h"
 
 #include "android/log.h"
 #include "ui/FramebufferNativeWindow.h"
 #include "hardware_legacy/power.h"
+#include "hardware/gralloc.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args)
 #define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args)
 
 using namespace android;
 using namespace mozilla;
 using namespace std;
@@ -226,19 +224,20 @@ public:
             return nullptr;
         return data;
     }
 };
 
 struct AnimationFrame {
     char path[256];
     char *buf;
-    uint16_t width;
-    uint16_t height;
     const local_file_header *file;
+    uint32_t width;
+    uint32_t height;
+    uint16_t bytepp;
 
     AnimationFrame() : buf(nullptr) {}
     AnimationFrame(const AnimationFrame &frame) : buf(nullptr) {
         strncpy(path, frame.path, sizeof(path));
         file = frame.file;
     }
     ~AnimationFrame()
     {
@@ -246,17 +245,17 @@ struct AnimationFrame {
             free(buf);
     }
 
     bool operator<(const AnimationFrame &other) const
     {
         return strcmp(path, other.path) < 0;
     }
 
-    void ReadPngFrame();
+    void ReadPngFrame(int outputFormat);
 };
 
 struct AnimationPart {
     int32_t count;
     int32_t pause;
     char path[256];
     vector<AnimationFrame> frames;
 };
@@ -275,18 +274,30 @@ RawReader(png_structp png_ptr, png_bytep
     RawReadState *state = (RawReadState *)png_get_io_ptr(png_ptr);
     if (length > (state->length - state->offset))
         png_err(png_ptr);
 
     memcpy(data, state->start + state->offset, length);
     state->offset += length;
 }
 
+static void
+TransformTo565(png_structp png_ptr, png_row_infop row_info, png_bytep data)
+{
+    uint16_t *outbuf = (uint16_t *)data;
+    uint8_t *inbuf = (uint8_t *)data;
+    for (int i = 0; i < row_info->rowbytes; i += 3) {
+        *outbuf++ = ((inbuf[i]     & 0xF8) << 8) |
+                    ((inbuf[i + 1] & 0xFC) << 3) |
+                    ((inbuf[i + 2]       ) >> 3);
+    }
+}
+
 void
-AnimationFrame::ReadPngFrame()
+AnimationFrame::ReadPngFrame(int outputFormat)
 {
     png_structp pngread = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                                  nullptr, nullptr, nullptr);
 
     png_infop pnginfo = png_create_info_struct(pngread);
 
     RawReadState state;
     state.start = file->GetData();
@@ -296,101 +307,91 @@ AnimationFrame::ReadPngFrame()
     png_set_read_fn(pngread, &state, RawReader);
 
     setjmp(png_jmpbuf(pngread));
 
     png_read_info(pngread, pnginfo);
 
     width = png_get_image_width(pngread, pnginfo);
     height = png_get_image_height(pngread, pnginfo);
-    buf = (char *)malloc(width * height * 3);
+    switch (outputFormat) {
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+        png_set_bgr(pngread);
+        // FALL THROUGH
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+        bytepp = 4;
+        png_set_filler(pngread, 0xFF, PNG_FILLER_AFTER);
+        break;
+    case HAL_PIXEL_FORMAT_RGB_888:
+        bytepp = 3;
+        png_set_strip_alpha(pngread);
+        break;
+    default:
+        LOGW("Unknown pixel format %d. Assuming RGB 565.", outputFormat);
+        // FALL THROUGH
+    case HAL_PIXEL_FORMAT_RGB_565:
+        bytepp = 2;
+        png_set_strip_alpha(pngread);
+        png_set_read_user_transform_fn(pngread, TransformTo565);
+        break;
+    }
+
+    // An extra row is added to give libpng enough space when
+    // decoding 3/4 bytepp inputs for 2 bytepp output surfaces
+    buf = (char *)malloc(width * (height + 1) * bytepp);
 
     vector<char *> rows(height + 1);
-    uint32_t stride = width * 3;
+    uint32_t stride = width * bytepp;
     for (int i = 0; i < height; i++) {
         rows[i] = buf + (stride * i);
     }
     rows[height] = nullptr;
+    png_set_strip_16(pngread);
     png_set_palette_to_rgb(pngread);
     png_read_image(pngread, (png_bytepp)&rows.front());
     png_destroy_read_struct(&pngread, &pnginfo, nullptr);
 }
 
-static const EGLint kEGLConfigAttribs[] = {
-    EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
-    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-    EGL_NONE
-};
-
-static bool
-CreateConfig(EGLConfig* aConfig, EGLDisplay display, int format)
-{
-    EGLConfig configs[64];
-    EGLint ncfg = ArrayLength(configs);
-
-    if (!eglChooseConfig(display, kEGLConfigAttribs,
-                         configs, ncfg, &ncfg) ||
-        ncfg < 1) {
-        return false;
-    }
-
-    for (int j = 0; j < ncfg; ++j) {
-        EGLConfig config = configs[j];
-        EGLint id;
-
-        if (eglGetConfigAttrib(display, config,
-                               EGL_NATIVE_VISUAL_ID, &id) &&
-            id > 0 && id == format)
-        {
-            *aConfig = config;
-            return true;
-        }
-    }
-    return false;
-}
-
 static void *
 AnimationThread(void *)
 {
     ZipReader reader;
     if (!reader.OpenArchive("/system/media/bootanimation.zip")) {
         LOGW("Could not open boot animation");
         return nullptr;
     }
 
-    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    eglInitialize(display, nullptr, nullptr);
-
-    int format;
-    ANativeWindow const * const window = gNativeWindow.get();
-    window->query(window, NATIVE_WINDOW_FORMAT, &format);
-
-    EGLConfig config;
-    if (!CreateConfig(&config, display, format)) {
-        LOGW("Could not find config for pixel format");
-        return nullptr;
-    }
-
-    EGLSurface surface = eglCreateWindowSurface(display, config, gNativeWindow.get(), nullptr);
-
     const cdir_entry *entry = nullptr;
     const local_file_header *file = nullptr;
     while ((entry = reader.GetNextEntry(entry))) {
         string name = reader.GetEntryName(entry);
         if (!name.compare("desc.txt")) {
             file = reader.GetLocalEntry(entry);
             break;
         }
     }
 
     if (!file) {
         LOGW("Could not find desc.txt in boot animation");
         return nullptr;
     }
 
+    int format;
+    ANativeWindow *window = gNativeWindow.get();
+    window->query(window, NATIVE_WINDOW_FORMAT, &format);
+
+    hw_module_t const *module;
+    if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) {
+        LOGW("Could not get gralloc module");
+        return nullptr;
+    }
+    gralloc_module_t const *grmodule =
+        reinterpret_cast<gralloc_module_t const*>(module);
+
     string descCopy;
     descCopy.append(file->GetData(), entry->GetDataSize());
     int32_t width, height, fps;
     const char *line = descCopy.c_str();
     const char *end;
     bool headerRead = true;
     vector<AnimationPart> parts;
 
@@ -447,167 +448,77 @@ AnimationThread(void *)
             AnimationFrame &frame = part.frames.back();
             strcpy(frame.path, name.c_str());
             frame.file = reader.GetLocalEntry(entry);
         }
 
         sort(part.frames.begin(), part.frames.end());
     }
 
-    static EGLint gContextAttribs[] = {
-        EGL_CONTEXT_CLIENT_VERSION, 2,
-        EGL_NONE, 0
-    };
-    EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, gContextAttribs);
-
-    eglMakeCurrent(display, surface, surface, context);
-    glEnable(GL_TEXTURE_2D);
-
-    const char *vsString =
-        "attribute vec2 aPosition; "
-        "attribute vec2 aTexCoord; "
-        "varying vec2 vTexCoord; "
-        "void main() { "
-        "  gl_Position = vec4(aPosition, 0.0, 1.0); "
-        "  vTexCoord = aTexCoord; "
-        "}";
-
-    const char *fsString =
-        "precision mediump float; "
-        "varying vec2 vTexCoord; "
-        "uniform sampler2D sTexture; "
-        "void main() { "
-        "  gl_FragColor = vec4(texture2D(sTexture, vTexCoord).rgb, 1.0); "
-        "}";
-
-    GLint status;
-    GLuint vsh = glCreateShader(GL_VERTEX_SHADER);
-    glShaderSource(vsh, 1, &vsString, nullptr);
-    glCompileShader(vsh);
-    glGetShaderiv(vsh, GL_COMPILE_STATUS, &status);
-    if (!status) {
-        LOGE("Failed to compile vertex shader");
-        return nullptr;
-    }
-
-    GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER);
-    glShaderSource(fsh, 1, &fsString, nullptr);
-    glCompileShader(fsh);
-    glGetShaderiv(fsh, GL_COMPILE_STATUS, &status);
-    if (!status) {
-        LOGE("Failed to compile fragment shader");
-        return nullptr;
-    }
-
-    GLuint programId = glCreateProgram();
-    glAttachShader(programId, vsh);
-    glAttachShader(programId, fsh);
-
-    glLinkProgram(programId);
-    glGetProgramiv(programId, GL_LINK_STATUS, &status);
-    if (!status) {
-        LOG("Failed to link program");
-        return nullptr;
-    }
-
-    GLint positionLoc = glGetAttribLocation(programId, "aPosition");
-    GLint texCoordLoc = glGetAttribLocation(programId, "aTexCoord");
-    GLint textureLoc = glGetUniformLocation(programId, "sTexture");
-
-    glUseProgram(programId);
-
-    GLfloat texCoords[] = { 0.0f, 1.0f,
-                            0.0f, 0.0f,
-                            1.0f, 1.0f,
-                            1.0f, 0.0f };
-
-    GLfloat vCoords[] = { -1.0f, -1.0f,
-                          -1.0f,  1.0f,
-                           1.0f, -1.0f,
-                           1.0f,  1.0f };
-
-    GLuint rectBuf, texBuf;
-    glGenBuffers(1, &rectBuf);
-    glGenBuffers(1, &texBuf);
-
-    GLuint tex;
-    glGenTextures(1, &tex);
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, tex);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-    glEnableVertexAttribArray(positionLoc);
-    glBindBuffer(GL_ARRAY_BUFFER, rectBuf);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(vCoords), vCoords, GL_STATIC_DRAW);
-    glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
-
-    glEnableVertexAttribArray(texCoordLoc);
-    glBindBuffer(GL_ARRAY_BUFFER, texBuf);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
-    glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-    glUniform1i(textureLoc, 0);
-
     uint32_t frameDelayUs = 1000000 / fps;
 
     for (uint32_t i = 0; i < parts.size(); i++) {
         AnimationPart &part = parts[i];
 
         uint32_t j = 0;
         while (sRunAnimation && (!part.count || j++ < part.count)) {
             for (uint32_t k = 0; k < part.frames.size(); k++) {
                 struct timeval tv1, tv2;
                 gettimeofday(&tv1, nullptr);
                 AnimationFrame &frame = part.frames[k];
                 if (!frame.buf) {
-                    frame.ReadPngFrame();
+                    frame.ReadPngFrame(format);
+                }
+
+                ANativeWindowBuffer *buf;
+                if (window->dequeueBuffer(window, &buf)) {
+                    LOGW("Failed to get an ANativeWindowBuffer");
+                    break;
+                }
+                if (window->lockBuffer(window, buf)) {
+                    LOGW("Failed to lock ANativeWindowBuffer");
+                    window->queueBuffer(window, buf);
+                    break;
                 }
 
-                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
-                             frame.width, frame.height, 0,
-                             GL_RGB, GL_UNSIGNED_BYTE, frame.buf);
-                glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+                void *vaddr;
+                if (grmodule->lock(grmodule, buf->handle,
+                                   GRALLOC_USAGE_SW_READ_NEVER |
+                                   GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                   GRALLOC_USAGE_HW_FB,
+                                   0, 0, width, height, &vaddr)) {
+                    LOGW("Failed to lock buffer_handle_t");
+                    window->queueBuffer(window, buf);
+                    break;
+                }
+                memcpy(vaddr, frame.buf,
+                       frame.width * frame.height * frame.bytepp);
+                grmodule->unlock(grmodule, buf->handle);
 
                 gettimeofday(&tv2, nullptr);
 
                 timersub(&tv2, &tv1, &tv2);
 
                 if (tv2.tv_usec < frameDelayUs) {
                     usleep(frameDelayUs - tv2.tv_usec);
                 } else {
                     LOGW("Frame delay is %d us but decoding took %d us", frameDelayUs, tv2.tv_usec);
                 }
 
-                eglSwapBuffers(display, surface);
+                window->queueBuffer(window, buf);
 
                 if (part.count && j >= part.count) {
                     free(frame.buf);
                     frame.buf = nullptr;
                 }
             }
             usleep(frameDelayUs * part.pause);
         }
     }
-    glBindTexture(GL_TEXTURE_2D, 0);
-    glUseProgram(0);
-    glDeleteTextures(1, &tex);
-    glDeleteBuffers(1, &texBuf);
-    glDeleteBuffers(1, &rectBuf);
-    glDeleteProgram(programId);
-    glDeleteShader(fsh);
-    glDeleteShader(vsh);
 
-    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-    eglDestroyContext(display, context);
-    eglDestroySurface(display, surface);
     return nullptr;
 }
 
 static int
 CancelBufferNoop(ANativeWindow* aWindow, android_native_buffer_t* aBuffer)
 {
     return 0;
 }
@@ -623,16 +534,31 @@ NativeWindow()
     // Some gralloc HALs need this in order to open the
     // framebuffer device after we restart with the screen off.
     //
     // NB: this *must* run BEFORE allocating the
     // FramebufferNativeWindow.  Do not separate these two C++
     // statements.
     set_screen_state(1);
 
+    // For some devices, it takes a while for the framebuffer to become
+    // usable. So we wait until the framebuffer has woken up before we
+    // try to open it.
+    {
+        char buf;
+        int len = 0;
+        ScopedClose fd(open("/sys/power/wait_for_fb_wake", O_RDONLY, 0));
+        do {
+            len = read(fd.get(), &buf, 1);
+        } while (len < 0 && errno == EINTR);
+        if (len < 0) {
+            LOGE("BootAnimation: wait_for_fb_sleep failed errno: %d", errno);
+        }
+    }
+
     // We (apparently) don't have a way to tell if allocating the
     // fbs succeeded or failed.
     gNativeWindow = new FramebufferNativeWindow();
 
     // Bug 776742: FrambufferNativeWindow doesn't set the cancelBuffer
     // function pointer, causing EGL to segfault when the window surface
     // is destroyed (i.e. on process exit). This workaround stops us
     // from hard crashing in that situation.
--- a/b2g/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -22,18 +22,16 @@ else
 PROGRAM=$(MOZ_APP_NAME)$(BIN_SUFFIX)
 endif
 
 CPPSRCS = nsBrowserApp.cpp
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += BootAnimation.cpp
 LIBS += \
-  -lGLESv2 \
-  -lEGL \
   -lui \
   -lhardware_legacy \
   -lhardware \
   -lcutils \
   $(DEPTH)/media/libpng/$(LIB_PREFIX)mozpng.$(LIB_SUFFIX) \
   $(MOZ_ZLIB_LIBS) \
   $(NULL)
 OS_LDFLAGS += -Wl,--export-dynamic
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -51,16 +51,20 @@ function UpdateCheckListener(updatePromp
 
 UpdateCheckListener.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateCheckListener]),
 
   _updatePrompt: null,
 
   onCheckComplete: function UCL_onCheckComplete(request, updates, updateCount) {
     if (Services.um.activeUpdate) {
+      // We're actively downloading an update, that's the update the user should
+      // see, even if a newer update is available.
+      this._updatePrompt.setUpdateStatus("active-update");
+      this._updatePrompt.showUpdateAvailable(Services.um.activeUpdate);
       return;
     }
 
     if (updateCount == 0) {
       this._updatePrompt.setUpdateStatus("no-updates");
       return;
     }
 
@@ -172,16 +176,24 @@ UpdatePrompt.prototype = {
 
     this._update = aUpdate;
     this.waitForIdle();
   },
 
   showUpdateError: function UP_showUpdateError(aUpdate) {
     log("Update error, state: " + aUpdate.state + ", errorCode: " +
         aUpdate.errorCode);
+    if (aUpdate.state == "applied" && aUpdate.errorCode == 0) {
+      // The user chose to apply the update later and then tried to download
+      // it again. If there isn't a new update to download, then the updater
+      // code will detect that there is an update waiting to be installed and
+      // fail. So reprompt the user to apply the update.
+      this.showApplyPrompt(aUpdate);
+      return;
+    }
 
     this.sendUpdateEvent("update-error", aUpdate);
     this.setUpdateStatus(aUpdate.statusText);
   },
 
   showUpdateHistory: function UP_showUpdateHistory(aParent) { },
   showUpdateInstalled: function UP_showUpdateInstalled() {
     let lock = Services.settings.createLock();
--- a/b2g/config/otoro/releng-otoro.tt
+++ b/b2g/config/otoro/releng-otoro.tt
@@ -2,13 +2,13 @@
 {
 "size": 895408640,
 "digest": "fc5be04b9b8365cd65fa8e66f4686bf0da8e34abb16ee618dd069469c9d9c9c3495562ebfcd21a2beadb27d59d6c011781188b9038ffebfd3e85cdd264f0aac3",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 },
 {
 "size": 4139008,
-"digest": "b1eac90cebe52708d512bbc293c507da9d93bc8c6a78cbbbd78cc9b7beb2cd335bef9bf8e4bf5a9dc38521f7080d29a743626f9e4af6c42ec211db22dc9d0fda",
+"digest": "6f65553e882316582b944e46c659915a1b907c4a326104cb31d81356330dddacba757e3eafbd282063da0e670c3c5d6b9a0905ab88da84b47848d810c37571cb",
 "algorithm": "sha512",
 "filename": "boot.img"
 }
 ]
--- a/b2g/config/unagi/releng-unagi.tt
+++ b/b2g/config/unagi/releng-unagi.tt
@@ -2,13 +2,13 @@
 {
 "size": 832272360,
 "digest": "bb7369106d32a184c61fc8c6658c4d1c64dd778e432a3dd39592b99a92ed0a8f4a9fede60399ec2c85ddaf077f27d77613848b253f0ac155383b23955446396f",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 },
 {
 "size": 8622080,
-"digest": "7a2bbf0c76f7b7d5e4b89f758f69b5d8bcf08ec579374877de8939ad69883ab8cd842f04fdaa03a4ef9cdf8170f242e0381dd437e969d5212ead6cdd6f79ab50",
+"digest": "36681be904b20a52dbebf38b86466026430d59adb0e72428ae7557a442d037eb378d278aab181b04a753821ff0a99b6228380d59f86ddd5fbf291284fe54932b",
 "algorithm": "sha512",
 "filename": "boot.img"
 }
 ]
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -269,16 +269,18 @@
            noautofocus="true"/>
     <panel id="social-flyout-panel"
            class="social-panel"
            onpopupshown="SocialFlyout.onShown()"
            onpopuphidden="SocialFlyout.onHidden()"
            side="right"
            type="arrow"
            hidden="true"
+           rolluponmousewheel="true"
+           consumeoutsideclicks="false"
            noautofocus="true"
            position="topcenter topright"/>
 
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event);">
       <menuseparator/>
       <menuitem command="cmd_ToggleTabsOnTop"
                 type="checkbox"
--- a/browser/base/content/test/browser_bug832435.js
+++ b/browser/base/content/test/browser_bug832435.js
@@ -6,14 +6,18 @@ function test() {
   waitForExplicitFinish();
   ok(true, "Starting up");
 
   gBrowser.selectedBrowser.focus();
   gURLBar.addEventListener("focus", function onFocus() {
     gURLBar.removeEventListener("focus", onFocus);
     ok(true, "Invoked onfocus handler");
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true });
-    ok(true, "Evaluated without crashing");
-    finish();
+
+    // javscript: URIs are evaluated async.
+    SimpleTest.executeSoon(function() {
+      ok(true, "Evaluated without crashing");
+      finish();
+    });
   });
-  gURLBar.inputField.value = "javascript: document.body.innerHTML = '11111111'); ";
+  gURLBar.inputField.value = "javascript: var foo = '11111111'; ";
   gURLBar.focus();
 }
--- a/browser/themes/gnomestripe/downloads/downloads.css
+++ b/browser/themes/gnomestripe/downloads/downloads.css
@@ -233,16 +233,19 @@ toolbar[iconsize="large"] > #downloads-i
 toolbar[iconsize="small"] > #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
   background-image: url("chrome://browser/skin/downloads/download-glow-small.png");
 }
 
 toolbar[iconsize="large"] > #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
   background-image: url("chrome://browser/skin/downloads/download-glow.png");
 }
 
+/* In the next few rules, we use :not([counter]) as a shortcut that is
+   equivalent to -moz-any([progress], [paused]). */
+
 #downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
                               0, 16, 16, 0) center no-repeat;
   background-size: 12px;
 }
 
 #downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
   background-image: url("chrome://browser/skin/downloads/download-glow.png");
--- a/browser/themes/pinstripe/downloads/downloads.css
+++ b/browser/themes/pinstripe/downloads/downloads.css
@@ -371,16 +371,19 @@ richlistitem[type="download"]:hover > st
                               0, 140, 20, 120) center no-repeat;
 }
 
 #downloads-indicator[attention]
 #downloads-indicator-icon {
   background-image: url("chrome://browser/skin/downloads/download-glow.png");
 }
 
+/* In the next few rules, we use :not([counter]) as a shortcut that is
+   equivalent to -moz-any([progress], [paused]). */
+
 #downloads-indicator:not([counter])
 #downloads-indicator-counter {
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
                               0, 140, 20, 120) center no-repeat;
   background-size: 12px;
 }
 
 #downloads-indicator:not([counter])[attention]
--- a/browser/themes/winstripe/downloads/downloads-aero.css
+++ b/browser/themes/winstripe/downloads/downloads-aero.css
@@ -17,21 +17,37 @@
     box-shadow: 0 0 0 1px hsla(0,0%,100%,.5) inset,
                 0 1px 0 hsla(0,0%,100%,.3) inset;
     background-image: linear-gradient(hsl(212,86%,92%), hsl(212,91%,86%));
     color: black;
   }
 }
 
 @media (-moz-windows-compositor) {
+  /* The following rules are for the downloads indicator when in its normal,
+     non-downloading, non-paused state (ie, it's just showing the downloads
+     button icon). */
   #toolbar-menubar #downloads-indicator-icon:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true] #downloads-indicator-icon:not(:-moz-lwtheme),
   #navigator-toolbox[tabsontop=false] > #nav-bar #downloads-indicator-icon:not(:-moz-lwtheme),
-  #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child #downloads-indicator-icon:not(:-moz-lwtheme) {
-    list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
+  #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child #downloads-indicator-icon:not(:-moz-lwtheme),
+
+  /* The following rules are for the downloads indicator when in its paused
+     or undetermined progress state. We use :not([counter]) as a shortcut for
+     :-moz-any([progress], [paused]). */
+
+  /* This is the case where the downloads indicator has been moved next to the menubar */
+  #toolbar-menubar #downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter,
+  /* This is the case where the downloads indicator is in the tabstrip toolbar with tabs on top. */
+  #TabsToolbar[tabsontop=true] #downloads-indicator:not(:-moz-lwtheme):not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter,
+  /* This is the case where the downloads indicator is anywhere in the nav-bar with tabs on bottom. */
+  #navigator-toolbox[tabsontop=false] > #nav-bar #downloads-indicator:not(:-moz-lwtheme):not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter,
+  /* This is the case where the downloads indicator is in the tabstrip when the tabstrip is the last item in the toolbox (and is therefore over glass) */
+  #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child #downloads-indicator:not(:-moz-lwtheme):not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
+    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 108, 18, 90);
   }
 
   #toolbar-menubar #downloads-indicator-counter:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true] #downloads-indicator-counter:not(:-moz-lwtheme),
   #navigator-toolbox[tabsontop=false] > #nav-bar #downloads-indicator-counter:not(:-moz-lwtheme),
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child #downloads-indicator-counter:not(:-moz-lwtheme) {
     color: white;
     text-shadow: 0 0 1px rgba(0,0,0,.7),
--- a/browser/themes/winstripe/downloads/downloads.css
+++ b/browser/themes/winstripe/downloads/downloads.css
@@ -252,16 +252,19 @@ richlistitem[type="download"]:hover > st
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"),
                               0, 108, 18, 90) center no-repeat;
 }
 
 #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
   background-image: url("chrome://browser/skin/downloads/download-glow.png");
 }
 
+/* In the next few rules, we use :not([counter]) as a shortcut that is
+   equivalent to -moz-any([progress], [paused]). */
+
 #downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
                               0, 108, 18, 90) center no-repeat;
   background-size: 12px;
 }
 
 #downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
   background-image: url("chrome://browser/skin/downloads/download-glow.png");
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -11,16 +11,17 @@ import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.IntBuffer;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
@@ -232,26 +233,31 @@ public class FennecNativeDriver implemen
         } catch (InvocationTargetException e) {
             log(LogLevel.ERROR, e);
         }
 
         return 0.0f;
     }
 
     private View getSurfaceView() {
+        ArrayList<View> views = mSolo.getCurrentViews();
         try {
             Class c = Class.forName("org.mozilla.gecko.gfx.LayerView");
-            for (View v : mSolo.getCurrentViews()) {
+            for (View v : views) {
                 if (c.isInstance(v)) {
                     return v;
                 }
             }
         } catch (ClassNotFoundException e) {
             log(LogLevel.ERROR, e);
         }
+        log(LogLevel.WARN, "getSurfaceView could not find LayerView");
+        for (View v : views) {
+            log(LogLevel.WARN, v.toString());
+        }
         return null;
     }
 
     public PaintedSurface getPaintedSurface() {
         View view = getSurfaceView();
         if (view == null) {
             return null;
         }
--- a/build/mobile/sutagent/android/Makefile.in
+++ b/build/mobile/sutagent/android/Makefile.in
@@ -37,46 +37,44 @@ RES_FILES = \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
   sutAgentAndroid.apk  \
+  sutAgentAndroid.ap_ \
+  sutAgentAndroid-unsigned-unaligned.apk \
+  sutAgentAndroid-unaligned.apk \
   $(NULL)
 
-GARBAGE_DIRS += res classes network-libs
+GARBAGE_DIRS += network-libs
 
 EXTRA_JARS = $(srcdir)/network-libs/commons-net-2.0.jar:$(srcdir)/network-libs/jmdns.jar
+
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(EXTRA_JARS)
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
 tools:: sutAgentAndroid.apk
 
 classes.dex: $(JAVAFILES)
-	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes $(subst :, ,$(EXTRA_JARS))
 
 sutAgentAndroid.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
 sutAgentAndroid-unsigned-unaligned.apk: sutAgentAndroid.ap_ classes.dex
 	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z sutAgentAndroid.ap_ -f classes.dex
 
 sutAgentAndroid-unaligned.apk: sutAgentAndroid-unsigned-unaligned.apk
-	cp  sutAgentAndroid-unsigned-unaligned.apk $@
+	cp $< $@
 ifdef JARSIGNER
 	$(JARSIGNER) $@
 endif
 
 sutAgentAndroid.apk: sutAgentAndroid-unaligned.apk
 	$(ZIPALIGN) -f -v 4 sutAgentAndroid-unaligned.apk $@
-
-export::
-	$(NSINSTALL) -D res
-	@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/sutagent/android/res && tar -xf -)
-
--- a/build/mobile/sutagent/android/fencp/Makefile.in
+++ b/build/mobile/sutagent/android/fencp/Makefile.in
@@ -28,29 +28,28 @@ RES_FILES = \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
   FenCP.apk  \
   $(NULL)
 
-GARBAGE_DIRS += res classes network-libs
+GARBAGE_DIRS += network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
 tools:: FenCP.apk
 
 classes.dex: $(JAVAFILES)
-	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
 FenCP.ap_: $(srcdir)/AndroidManifest.xml
 	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
 
 FenCP-unsigned-unaligned.apk: FenCP.ap_ classes.dex
 	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FenCP.ap_ -f classes.dex
@@ -58,13 +57,8 @@ FenCP-unsigned-unaligned.apk: FenCP.ap_ 
 FenCP-unaligned.apk: FenCP-unsigned-unaligned.apk
 	cp  FenCP-unsigned-unaligned.apk $@
 ifdef JARSIGNER
   $(JARSIGNER) $@
 endif
 
 FenCP.apk: FenCP-unaligned.apk
 	$(ZIPALIGN) -f -v 4 FenCP-unaligned.apk $@
-
-export::
-	$(NSINSTALL) -D res
-	@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/sutagent/android/fencp/res && tar -xf -)
-
--- a/build/mobile/sutagent/android/ffxcp/Makefile.in
+++ b/build/mobile/sutagent/android/ffxcp/Makefile.in
@@ -28,29 +28,28 @@ RES_FILES = \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
   FfxCP.apk  \
   $(NULL)
 
-GARBAGE_DIRS += res classes network-libs
+GARBAGE_DIRS += network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
 tools:: FfxCP.apk
 
 classes.dex: $(JAVAFILES)
-	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
 FfxCP.ap_: $(srcdir)/AndroidManifest.xml
 	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
 
 FfxCP-unsigned-unaligned.apk: FfxCP.ap_ classes.dex
 	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FfxCP.ap_ -f classes.dex
@@ -58,13 +57,8 @@ FfxCP-unsigned-unaligned.apk: FfxCP.ap_ 
 FfxCP-unaligned.apk: FfxCP-unsigned-unaligned.apk
 	cp  FfxCP-unsigned-unaligned.apk $@
 ifdef JARSIGNER
   $(JARSIGNER) $@
 endif
 
 FfxCP.apk: FfxCP-unaligned.apk
 	$(ZIPALIGN) -f -v 4 FfxCP-unaligned.apk $@
-
-export::
-	$(NSINSTALL) -D res
-	@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/sutagent/android/ffxcp/res && tar -xf -)
-
--- a/build/virtualenv/packages.txt
+++ b/build/virtualenv/packages.txt
@@ -1,25 +1,15 @@
 simplejson.pth:python/simplejson-2.1.1
-manifestdestiny.pth:testing/mozbase/manifestdestiny
-mozcrash.pth:testing/mozbase/mozcrash
-mozdevice.pth:testing/mozbase/mozdevice
-mozfile.pth:testing/mozbase/mozfile
-mozhttpd.pth:testing/mozbase/mozhttpd
-mozinfo.pth:testing/mozbase/mozinfo
-mozinstall.pth:testing/mozbase/mozinstall
-mozlog.pth:testing/mozbase/mozlog
-mozprocess.pth:testing/mozbase/mozprocess
-mozprofile.pth:testing/mozbase/mozprofile
-mozrunner.pth:testing/mozbase/mozrunner
 marionette.pth:testing/marionette/client
 blessings.pth:python/blessings
 mach.pth:python/mach
 mozbuild.pth:python/mozbuild
 pymake.pth:build/pymake
 optional:setup.py:python/psutil:build_ext:--inplace
 optional:psutil.pth:python/psutil
 which.pth:python/which
 mock.pth:python/mock-1.0.0
 mozilla.pth:build
 mozilla.pth:config
 mozilla.pth:xpcom/typelib/xpt/tools
 copy:build/buildconfig.py
+packages.txt:testing/mozbase/packages.txt
--- a/build/virtualenv/populate_virtualenv.py
+++ b/build/virtualenv/populate_virtualenv.py
@@ -17,100 +17,120 @@ import sys
 # Minimum version of Python required to build.
 MINIMUM_PYTHON_MAJOR = 2
 MINIMUM_PYTHON_MINOR = 7
 
 
 class VirtualenvManager(object):
     """Contains logic for managing virtualenvs for building the tree."""
 
-    def __init__(self, topsrcdir, virtualenv_path, log_handle):
+    def __init__(self, topsrcdir, virtualenv_path, log_handle, manifest_path):
         """Create a new manager.
 
         Each manager is associated with a source directory, a path where you
         want the virtualenv to be created, and a handle to write output to.
         """
+        assert os.path.isabs(manifest_path), "manifest_path must be an absolute path: %s" % (manifest_path)
         self.topsrcdir = topsrcdir
         self.virtualenv_root = virtualenv_path
         self.log_handle = log_handle
+        self.manifest_path = manifest_path
 
     @property
     def virtualenv_script_path(self):
         """Path to virtualenv's own populator script."""
         return os.path.join(self.topsrcdir, 'python', 'virtualenv',
             'virtualenv.py')
 
     @property
-    def manifest_path(self):
-        return os.path.join(self.topsrcdir, 'build', 'virtualenv',
-            'packages.txt')
-
-    @property
     def python_path(self):
         if sys.platform in ('win32', 'cygwin'):
             return os.path.join(self.virtualenv_root, 'Scripts', 'python.exe')
 
         return os.path.join(self.virtualenv_root, 'bin', 'python')
 
     @property
     def activate_path(self):
         if sys.platform in ('win32', 'cygwin'):
             return os.path.join(self.virtualenv_root, 'Scripts',
                 'activate_this.py')
 
         return os.path.join(self.virtualenv_root, 'bin', 'activate_this.py')
 
+    def up_to_date(self):
+        """Returns whether the virtualenv is present and up to date."""
+
+        deps = [self.manifest_path, __file__]
+
+        # check if virtualenv exists
+        if not os.path.exists(self.virtualenv_root) or \
+            not os.path.exists(self.activate_path):
+
+            return False
+
+        # check modification times
+        activate_mtime = os.path.getmtime(self.activate_path)
+        dep_mtime = max(os.path.getmtime(p) for p in deps)
+        if dep_mtime > activate_mtime:
+            return False
+
+        # recursively check sub packages.txt files
+        submanifests = [i[1] for i in self.packages()
+                        if i[0] == 'packages.txt']
+        for submanifest in submanifests:
+            submanifest = os.path.join(self.topsrcdir, submanifest)
+            submanager = VirtualenvManager(self.topsrcdir,
+                                           self.virtualenv_root,
+                                           self.log_handle,
+                                           submanifest)
+            if not submanager.up_to_date():
+                return False
+
+        return True
+
     def ensure(self):
         """Ensure the virtualenv is present and up to date.
 
         If the virtualenv is up to date, this does nothing. Otherwise, it
         creates and populates the virtualenv as necessary.
 
         This should be the main API used from this class as it is the
         highest-level.
         """
-        deps = [self.manifest_path, __file__]
-
-        if not os.path.exists(self.virtualenv_root) or \
-            not os.path.exists(self.activate_path):
-
-            return self.build()
-
-        activate_mtime = os.path.getmtime(self.activate_path)
-        dep_mtime = max(os.path.getmtime(p) for p in deps)
-
-        if dep_mtime > activate_mtime:
-            return self.build()
-
-        return self.virtualenv_root
+        if self.up_to_date():
+            return self.virtualenv_root
+        return self.build()
 
     def create(self):
         """Create a new, empty virtualenv.
 
         Receives the path to virtualenv's virtualenv.py script (which will be
         called out to), the path to create the virtualenv in, and a handle to
         write output to.
         """
         env = dict(os.environ)
-        try:
-            del env['PYTHONDONTWRITEBYTECODE']
-        except KeyError:
-            pass
+        env.pop('PYTHONDONTWRITEBYTECODE', None)
 
         args = [sys.executable, self.virtualenv_script_path,
             '--system-site-packages', self.virtualenv_root]
 
         result = subprocess.call(args, stdout=self.log_handle,
             stderr=subprocess.STDOUT, env=env)
 
-        if result != 0:
+        if result:
             raise Exception('Error creating virtualenv.')
 
         return self.virtualenv_root
 
+    def packages(self):
+        with file(self.manifest_path, 'rU') as fh:
+            packages = [line.rstrip().split(':')
+                        for line in fh]
+        return packages
+
     def populate(self):
         """Populate the virtualenv.
 
         The manifest file consists of colon-delimited fields. The first field
         specifies the action. The remaining fields are arguments to that
         action. The following actions are supported:
 
         setup.py -- Invoke setup.py for a package. Expects the arguments:
@@ -131,21 +151,18 @@ class VirtualenvManager(object):
         copy -- Copies the given file in the virtualenv site packages
             directory.
 
         Note that the Python interpreter running this function should be the
         one from the virtualenv. If it is the system Python or if the
         environment is not configured properly, packages could be installed
         into the wrong place. This is how virtualenv's work.
         """
-        packages = []
-        fh = open(self.manifest_path, 'rU')
-        for line in fh:
-            packages.append(line.rstrip().split(':'))
-        fh.close()
+
+        packages = self.packages()
 
         def handle_package(package):
             python_lib = distutils.sysconfig.get_python_lib()
             if package[0] == 'setup.py':
                 assert len(package) >= 2
 
                 self.call_setup(os.path.join(self.topsrcdir, package[1]),
                     package[2:])
@@ -157,16 +174,29 @@ class VirtualenvManager(object):
 
                 src = os.path.join(self.topsrcdir, package[1])
                 dst = os.path.join(python_lib, os.path.basename(package[1]))
 
                 shutil.copy(src, dst)
 
                 return True
 
+            if package[0] == 'packages.txt':
+                assert len(package) == 2
+
+                src = os.path.join(self.topsrcdir, package[1])
+                assert os.path.isfile(src), "'%s' does not exist" % src
+                submanager = VirtualenvManager(self.topsrcdir,
+                                               self.virtualenv_root,
+                                               self.log_handle,
+                                               src)
+                submanager.populate()
+
+                return True
+
             if package[0].endswith('.pth'):
                 assert len(package) == 2
 
                 path = os.path.join(self.topsrcdir, package[1])
 
                 with open(os.path.join(python_lib, package[0]), 'a') as f:
                     f.write("%s\n" % path)
 
@@ -216,27 +246,22 @@ class VirtualenvManager(object):
                     continue
 
                 old_env_variables[k] = os.environ[k]
                 del os.environ[k]
 
             for package in packages:
                 handle_package(package)
         finally:
-            try:
-                del os.environ['MACOSX_DEPLOYMENT_TARGET']
-            except KeyError:
-                pass
+            os.environ.pop('MACOSX_DEPLOYMENT_TARGET', None)
 
             if old_target is not None:
                 os.environ['MACOSX_DEPLOYMENT_TARGET'] = old_target
 
-            for k in old_env_variables:
-                os.environ[k] = old_env_variables[k]
-
+            os.environ.update(old_env_variables)
 
     def call_setup(self, directory, arguments):
         """Calls setup.py in a directory."""
         setup = os.path.join(directory, 'setup.py')
 
         program = [sys.executable, setup]
         program.extend(arguments)
 
@@ -315,15 +340,18 @@ if __name__ == '__main__':
     populate = False
 
     # This should only be called internally.
     if sys.argv[1] == 'populate':
         populate = True
         topsrcdir = sys.argv[2]
         virtualenv_path = sys.argv[3]
 
-    manager = VirtualenvManager(topsrcdir, virtualenv_path, sys.stdout)
+    # path to default packages.txt
+    manifest_path = os.path.join(topsrcdir, 'build', 'virtualenv', 'packages.txt')
+
+    manager = VirtualenvManager(topsrcdir, virtualenv_path, sys.stdout, manifest_path)
 
     if populate:
         manager.populate()
     else:
         manager.ensure()
 
--- a/client.mk
+++ b/client.mk
@@ -281,16 +281,17 @@ CONFIG_STATUS_DEPS := \
   $(TOPSRCDIR)/allmakefiles.sh \
   $(TOPSRCDIR)/CLOBBER \
   $(TOPSRCDIR)/nsprpub/configure \
   $(TOPSRCDIR)/config/milestone.txt \
   $(TOPSRCDIR)/js/src/config/milestone.txt \
   $(TOPSRCDIR)/browser/config/version.txt \
   $(TOPSRCDIR)/build/virtualenv/packages.txt \
   $(TOPSRCDIR)/build/virtualenv/populate_virtualenv.py \
+  $(TOPSRCDIR)/testing/mozbase/packages.txt \
   $(NULL)
 
 CONFIGURE_ENV_ARGS += \
   MAKE="$(MAKE)" \
   $(NULL)
 
 # configure uses the program name to determine @srcdir@. Calling it without
 #   $(TOPSRCDIR) will set @srcdir@ to "."; otherwise, it is set to the full
new file mode 100644
--- /dev/null
+++ b/config/makefiles/java-build.mk
@@ -0,0 +1,37 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# 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 INCLUDED_JAVA_BUILD_MK #{
+
+ifdef RES_FILES #{
+res-dep := .deps-copy-java-res
+
+GENERATED_DIRS += res
+GARBAGE        += $(res-dep)
+
+export:: $(res-dep)
+
+res-dep-preqs := \
+  $(addprefix $(srcdir)/,$(RES_FILES)) \
+  $(call mkdir_deps,res) \
+  $(if $(IS_LANGUAGE_REPACK),FORCE) \
+  $(NULL)
+
+# nop-build: only copy res/ files when needed
+$(res-dep): $(res-dep-preqs)
+	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
+	@$(TOUCH) $@
+endif #}
+
+
+ifdef JAVAFILES #{
+GENERATED_DIRS += classes
+endif #}
+
+INCLUDED_JAVA_BUILD_MK := 1
+
+endif #} INCLUDED_JAVA_BUILD_MK
--- a/config/makefiles/makeutils.mk
+++ b/config/makefiles/makeutils.mk
@@ -107,8 +107,11 @@ endif #}
 ###########################################################################
 ## Common makefile library loader
 ###########################################################################
 topORerr =$(if $(topsrcdir),$(topsrcdir),$(error topsrcdir is not defined))
 
 ifdef USE_AUTOTARGETS_MK # mkdir_deps
   include $(topORerr)/config/makefiles/autotargets.mk
 endif
+
+## copy(src, dst): recursive copy
+copy_dir = (cd $(1)/. && $(TAR) $(TAR_CREATE_FLAGS_QUIET) - .) | (cd $(2)/. && tar -xf -)
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1095,16 +1095,20 @@ ifeq ($(HOST_OS_ARCH),WINNT)
 #  on it, then merge with the rest of the path.
 root-path = $(shell echo $(1) | sed -e "s|\(/[^/]*\)/\?\(.*\)|\1|")
 non-root-path = $(shell echo $(1) | sed -e "s|\(/[^/]*\)/\?\(.*\)|\2|")
 normalizepath = $(foreach p,$(1),$(if $(filter /%,$(1)),$(patsubst %/,%,$(shell cd $(call root-path,$(1)) && pwd -W))/$(call non-root-path,$(1)),$(1)))
 else
 normalizepath = $(1)
 endif
 
+ifneq (,$(value JAVAFILES)$(value RESFILES))
+  include $(topsrcdir)/config/makefiles/java-build.mk
+endif
+
 _srcdir = $(call normalizepath,$(srcdir))
 ifdef JAVA_SOURCEPATH
 SP = $(subst $(SPACE),$(SEP),$(call normalizepath,$(strip $(JAVA_SOURCEPATH))))
 _JAVA_SOURCEPATH = ".$(SEP)$(_srcdir)$(SEP)$(SP)"
 else
 _JAVA_SOURCEPATH = ".$(SEP)$(_srcdir)"
 endif
 
--- a/configure.in
+++ b/configure.in
@@ -5208,22 +5208,18 @@ if test "$NS_PRINTING"; then
 fi
 
 dnl Turn off webrtc for OS's we don't handle yet, but allow 
 dnl --enable-webrtc to override.  Can disable for everything in
 dnl the master list above.
 if test -n "$MOZ_WEBRTC"; then
     case "$target" in
     *-android*|*-linuxandroid*)
-        if test -n "$MOZ_B2G"; then
-            MOZ_WEBRTC=1
-        else
-            dnl Make sure doesn't get matched by *-linux*
-            MOZ_WEBRTC=
-        fi
+        dnl Make sure doesn't get matched by *-linux*
+        MOZ_WEBRTC=
         ;;
     *-linux*|*-mingw*|*-darwin*)
         dnl Leave enabled
         ;;
     *)
         dnl default to disabled for all others
         MOZ_WEBRTC=
         ;;
@@ -5241,30 +5237,28 @@ MOZ_ARG_DISABLE_BOOL(webrtc,
 if test -n "$MOZ_WEBRTC"; then
     AC_DEFINE(MOZ_WEBRTC)
     MOZ_MEDIA=1
     MOZ_RAW=1
     MOZ_VP8=1
     MOZ_VP8_ENCODER=1
     MOZ_VP8_ERROR_CONCEALMENT=1
 
-    if test "$MOZ_WIDGET_TOOLKIT" != "gonk"; then
-       dnl OpenSLES is only available in Android 2.3 and later; we'll change this
-       dnl hard dependency to a dynamic load with graceful runtime failure before
-       dnl we make --enable-webrtc on by default in Android (bug 815905)
-       dnl
-       if test "$OS_TARGET" = "Android"; then
-          LDFLAGS="$LDFLAGS -lOpenSLES"
-       fi
-       case "$target" in
-          *-android*|*-linuxandroid*)
-          LDFLAGS="$LDFLAGS -lOpenSLES"
-          ;;
-       esac
-    fi
+    dnl OpenSLES is only available in Android 2.3 and later; we'll change this
+    dnl hard dependency to a dynamic load with graceful runtime failure before
+    dnl we make --enable-webrtc on by default in Android (bug 815905)
+    dnl
+    if test "$OS_TARGET" = "Android"; then
+       LDFLAGS="$LDFLAGS -lOpenSLES"
+    fi
+    case "$target" in
+    *-android*|*-linuxandroid*)
+       LDFLAGS="$LDFLAGS -lOpenSLES"
+       ;;
+    esac
 
 dnl enable once Signaling lands
     MOZ_WEBRTC_SIGNALING=1
     AC_DEFINE(MOZ_WEBRTC_SIGNALING)
     if test "${OS_TARGET}" = "WINNT"; then
         MOZ_WEBRTC_IN_LIBXUL=1
     fi
 dnl enable once PeerConnection lands
@@ -8988,45 +8982,31 @@ if test "${OS_TARGET}" = "WINNT"; then
    if test "$HAVE_64BIT_OS"; then
       OS_BITS=64
    else
       OS_BITS=32
    fi
    EXTRA_GYP_DEFINES="-D MSVS_VERSION=${_MSVS_VERSION} -D MSVS_OS_BITS=${OS_BITS}"
 
 elif test "${OS_TARGET}" = "Android"; then
-   if test "${MOZ_WIDGET_TOOLKIT}" != "gonk"; then
-      EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
-   fi
+   EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
    if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then
-      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
+      EXTRA_GYP_DEFINES+=" -D armv7=0 "
    else
-      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 "
+      EXTRA_GYP_DEFINES+=" -D armv7=1 "
    fi
 fi
 
 if test -n "$MOZ_WEBRTC"; then
    AC_MSG_RESULT("generating WebRTC Makefiles...")
 
-   if test "${MOZ_WIDGET_TOOLKIT}" = "gonk"; then
-      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D moz_widget_toolkit_gonk=1"
-   else
-      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D moz_widget_toolkit_gonk=0"
-   fi
-
 dnl Any --include files must also appear in -D FORCED_INCLUDE_FILE= entries
 dnl so that regeneration via dependencies works correctly
    WEBRTC_CONFIG="-D build_with_mozilla=1 --include ${srcdir}/media/webrtc/webrtc_config.gypi -D FORCED_INCLUDE_FILE=${srcdir}/media/webrtc/webrtc_config.gypi"
 
-   if test -n HAVE_CLOCK_MONOTONIC; then
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D moz_have_clock_monotonic=1"
-   else
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D moz_have_clock_monotonic=0"
-   fi
-
    GYP_WEBRTC_OPTIONS="--format=mozmake ${WEBRTC_CONFIG} -D target_arch=${WEBRTC_TARGET_ARCH} ${EXTRA_GYP_DEFINES} --depth=${srcdir}/media/webrtc/trunk --toplevel-dir=${srcdir} -G OBJDIR=${_objdir}"
 
    $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
      $GYP_WEBRTC_OPTIONS \
      --generator-output=${_objdir}/media/webrtc/trunk \
      ${srcdir}/media/webrtc/trunk/peerconnection.gyp
    if test "$?" != 0; then
       AC_MSG_ERROR([failed to generate WebRTC Makefiles])
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -127,27 +127,37 @@ template<>
 struct DataBlobs<Parent>
 {
   typedef BlobTraits<Parent>::ProtocolType ProtocolType;
 
   static InfallibleTArray<ProtocolType*>& Blobs(ClonedMessageData& aData)
   {
     return aData.blobsParent();
   }
+
+  static const InfallibleTArray<ProtocolType*>& Blobs(const ClonedMessageData& aData)
+  {
+    return aData.blobsParent();
+  }
 };
 
 template<>
 struct DataBlobs<Child>
 {
   typedef BlobTraits<Child>::ProtocolType ProtocolType;
 
   static InfallibleTArray<ProtocolType*>& Blobs(ClonedMessageData& aData)
   {
     return aData.blobsChild();
   }
+
+  static const InfallibleTArray<ProtocolType*>& Blobs(const ClonedMessageData& aData)
+  {
+    return aData.blobsChild();
+  }
 };
 
 template<ActorFlavorEnum Flavor>
 static bool
 BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager,
                        const StructuredCloneData& aData,
                        ClonedMessageData& aClonedData)
 {
@@ -182,16 +192,52 @@ MessageManagerCallback::BuildClonedMessa
 bool
 MessageManagerCallback::BuildClonedMessageDataForChild(ContentChild* aChild,
                                                        const StructuredCloneData& aData,
                                                        ClonedMessageData& aClonedData)
 {
   return BuildClonedMessageData<Child>(aChild, aData, aClonedData);
 }
 
+template<ActorFlavorEnum Flavor>
+static StructuredCloneData
+UnpackClonedMessageData(const ClonedMessageData& aData)
+{
+  const SerializedStructuredCloneBuffer& buffer = aData.data();
+  typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
+  const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData);
+  StructuredCloneData cloneData;
+  cloneData.mData = buffer.data;
+  cloneData.mDataLength = buffer.dataLength;
+  if (!blobs.IsEmpty()) {
+    uint32_t length = blobs.Length();
+    cloneData.mClosure.mBlobs.SetCapacity(length);
+    for (uint32_t i = 0; i < length; ++i) {
+      Blob<Flavor>* blob = static_cast<Blob<Flavor>*>(blobs[i]);
+      MOZ_ASSERT(blob);
+      nsCOMPtr<nsIDOMBlob> domBlob = blob->GetBlob();
+      MOZ_ASSERT(domBlob);
+      cloneData.mClosure.mBlobs.AppendElement(domBlob);
+    }
+  }
+  return cloneData;
+}
+
+StructuredCloneData
+mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aData)
+{
+  return UnpackClonedMessageData<Parent>(aData);
+}
+
+StructuredCloneData
+mozilla::dom::ipc::UnpackClonedMessageDataForChild(const ClonedMessageData& aData)
+{
+  return UnpackClonedMessageData<Child>(aData);
+}
+
 // nsIMessageListenerManager
 
 NS_IMETHODIMP
 nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
                                           nsIMessageListener* aListener)
 {
   nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
   uint32_t len = mListeners.Length();
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -83,16 +83,19 @@ protected:
   bool BuildClonedMessageDataForParent(ContentParent* aParent,
 				       const StructuredCloneData& aData,
 				       ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForChild(ContentChild* aChild,
 				      const StructuredCloneData& aData,
 				      ClonedMessageData& aClonedData);
 };
 
+StructuredCloneData UnpackClonedMessageDataForParent(const ClonedMessageData& aData);
+StructuredCloneData UnpackClonedMessageDataForChild(const ClonedMessageData& aData);
+
 } // namespace ipc
 } // namespace dom
 } // namespace mozilla
 
 class nsAXPCNativeCallContext;
 struct JSContext;
 class JSObject;
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -642,16 +642,17 @@ GK_ATOM(onbeforeunload, "onbeforeunload"
 GK_ATOM(onblocked, "onblocked")
 GK_ATOM(onblur, "onblur")
 GK_ATOM(onbroadcast, "onbroadcast")
 GK_ATOM(onbusy, "onbusy")
 GK_ATOM(oncached, "oncached")
 GK_ATOM(oncallschanged, "oncallschanged")
 GK_ATOM(oncancel, "oncancel")
 GK_ATOM(oncardstatechange, "oncardstatechange")
+GK_ATOM(oncfstatechange, "oncfstatechange")
 GK_ATOM(onchange, "onchange")
 GK_ATOM(onchargingchange, "onchargingchange")
 GK_ATOM(onchargingtimechange, "onchargingtimechange")
 GK_ATOM(onchecking, "onchecking")
 GK_ATOM(onclick, "onclick")
 GK_ATOM(onclose, "onclose")
 GK_ATOM(oncommand, "oncommand")
 GK_ATOM(oncommandupdate, "oncommandupdate")
@@ -906,16 +907,17 @@ GK_ATOM(rev, "rev")
 GK_ATOM(reverse, "reverse")
 GK_ATOM(reversed, "reversed")
 GK_ATOM(richlistbox, "richlistbox")
 GK_ATOM(richlistitem, "richlistitem")
 GK_ATOM(right, "right")
 GK_ATOM(rightmargin, "rightmargin")
 GK_ATOM(rightpadding, "rightpadding")
 GK_ATOM(role, "role")
+GK_ATOM(rolluponmousewheel, "rolluponmousewheel")
 GK_ATOM(round, "round")
 GK_ATOM(row, "row")
 GK_ATOM(rows, "rows")
 GK_ATOM(rowspan, "rowspan")
 GK_ATOM(rp, "rp")
 GK_ATOM(rt, "rt")
 GK_ATOM(rtl, "rtl")
 GK_ATOM(ruby, "ruby")
--- a/content/base/src/nsNodeInfo.cpp
+++ b/content/base/src/nsNodeInfo.cpp
@@ -23,16 +23,17 @@
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsAutoPtr.h"
 #include NEW_H
 #include "nsFixedSizeAllocator.h"
 #include "prprf.h"
 #include "nsIDocument.h"
 #include "nsGkAtoms.h"
+#include "nsCCUncollectableMarker.h"
 
 using namespace mozilla;
 
 static const size_t kNodeInfoPoolSizes[] = {
   sizeof(nsNodeInfo)
 };
 
 static const int32_t kNodeInfoPoolInitialSize = sizeof(nsNodeInfo) * 64;
@@ -180,16 +181,29 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   }
   else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsNodeInfo, tmp->mRefCnt.get())
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mOwnerManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfo)
+  return nsCCUncollectableMarker::sGeneration && tmp->CanSkip();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfo)
+  return nsCCUncollectableMarker::sGeneration && tmp->CanSkip();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfo)
+  return nsCCUncollectableMarker::sGeneration && tmp->CanSkip();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsNodeInfo, LastRelease())
 NS_INTERFACE_TABLE_HEAD(nsNodeInfo)
   NS_INTERFACE_TABLE1(nsNodeInfo, nsINodeInfo)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsNodeInfo)
 NS_INTERFACE_MAP_END
 
 // nsINodeInfo
@@ -237,8 +251,15 @@ nsNodeInfo::LastRelease()
   // The refcount balancing and destructor re-entrancy protection
   // code in Release() sets mRefCnt to 1 so we have to set it to 0
   // here to prevent leaks
   mRefCnt = 0;
 
   NS_ASSERTION(sNodeInfoPool, "No NodeInfoPool when deleting NodeInfo!!!");
   sNodeInfoPool->Free(this, sizeof(nsNodeInfo));
 }
+
+bool
+nsNodeInfo::CanSkip()
+{
+  return mDocument &&
+    nsCCUncollectableMarker::InGeneration(mDocument->GetMarkedCCGeneration());
+}
--- a/content/base/src/nsNodeInfo.h
+++ b/content/base/src/nsNodeInfo.h
@@ -21,17 +21,17 @@
 #include "nsGkAtoms.h"
 
 class nsFixedSizeAllocator;
 
 class nsNodeInfo : public nsINodeInfo
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(nsNodeInfo)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(nsNodeInfo)
 
   // nsINodeInfo
   virtual nsresult GetNamespaceURI(nsAString& aNameSpaceURI) const;
   virtual bool NamespaceEquals(const nsAString& aNamespaceURI) const;
 
   // nsNodeInfo
   // Create objects with Create
 public:
@@ -52,16 +52,18 @@ protected:
   virtual ~nsNodeInfo();
 
 public:
   /**
    * Call before shutdown to clear the cache and free memory for this class.
    */
   static void ClearCache();
 
+  bool CanSkip();
+
 private:
   static nsFixedSizeAllocator* sNodeInfoPool;
 
   /**
    * This method gets called by Release() when it's time to delete 
    * this object, instead of always deleting the object we'll put the
    * object in the cache unless the cache is already full.
    */
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -702,40 +702,37 @@ WebGLContext::ValidateAttribArraySetter(
 }
 
 bool
 WebGLContext::ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object,
                                          GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength)
 {
     if (!IsContextStable())
         return false;
-    nsCString nameString(name);
-    nsCString suffix = NS_LITERAL_CSTRING(": location");
-    nsCString concatenated = nameString + suffix;
-    if (!ValidateUniformLocation(concatenated.get(), location_object))
+    if (!ValidateUniformLocation(name, location_object))
         return false;
     location = location_object->Location();
     uint32_t uniformElemSize = location_object->ElementSize();
     if (expectedElemSize != uniformElemSize) {
         ErrorInvalidOperation("%s: this function expected a uniform of element size %d,"
                               " got a uniform of element size %d", name,
                               expectedElemSize,
                               uniformElemSize);
         return false;
     }
-    const WebGLUniformInfo& info = location_object->Info();
     if (arrayLength == 0 ||
         arrayLength % expectedElemSize)
     {
         ErrorInvalidValue("%s: expected an array of length a multiple"
                           " of %d, got an array of length %d", name,
                           expectedElemSize,
                           arrayLength);
         return false;
     }
+    const WebGLUniformInfo& info = location_object->Info();
     if (!info.isArray &&
         arrayLength != expectedElemSize) {
         ErrorInvalidOperation("%s: expected an array of length exactly"
                               " %d (since this uniform is not an array"
                               " uniform), got an array of length %d", name,
                               expectedElemSize,
                               arrayLength);
         return false;
@@ -748,40 +745,37 @@ WebGLContext::ValidateUniformArraySetter
 bool
 WebGLContext::ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object,
                                               GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength,
                                               WebGLboolean aTranspose)
 {
     uint32_t expectedElemSize = (dim)*(dim);
     if (!IsContextStable())
         return false;
-    nsCString nameString(name);
-    nsCString suffix = NS_LITERAL_CSTRING(": location");
-    nsCString concatenated = nameString + suffix;
-    if (!ValidateUniformLocation(concatenated.get(), location_object))
+    if (!ValidateUniformLocation(name, location_object))
         return false;
     location = location_object->Location();
     uint32_t uniformElemSize = location_object->ElementSize();
     if (expectedElemSize != uniformElemSize) {
         ErrorInvalidOperation("%s: this function expected a uniform of element size %d,"
                               " got a uniform of element size %d", name,
                               expectedElemSize,
                               uniformElemSize);
         return false;
     }
-    const WebGLUniformInfo& info = location_object->Info();
     if (arrayLength == 0 ||
         arrayLength % expectedElemSize)
     {
         ErrorInvalidValue("%s: expected an array of length a multiple"
                           " of %d, got an array of length %d", name,
                           expectedElemSize,
                           arrayLength);
         return false;
     }
+    const WebGLUniformInfo& info = location_object->Info();
     if (!info.isArray &&
         arrayLength != expectedElemSize) {
         ErrorInvalidOperation("%s: expected an array of length exactly"
                               " %d (since this uniform is not an array"
                               " uniform), got an array of length %d", name,
                               expectedElemSize,
                               arrayLength);
         return false;
@@ -796,20 +790,17 @@ WebGLContext::ValidateUniformMatrixArray
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location)
 {
     if (!IsContextStable())
         return false;
-    nsCString nameString(name);
-    nsCString suffix = NS_LITERAL_CSTRING(": location");
-    nsCString concatenated = nameString + suffix;
-    if (!ValidateUniformLocation(concatenated.get(), location_object))
+    if (!ValidateUniformLocation(name, location_object))
         return false;
     location = location_object->Location();
     return true;
 }
 
 bool WebGLContext::ValidateAttribIndex(WebGLuint index, const char *info)
 {
     if (index >= mAttribBuffers.Length()) {
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -3607,16 +3607,23 @@ void nsHTMLMediaElement::UpdateAudioChan
 
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
       mAudioChannelAgent->Init(mAudioChannelType, this);
+
+      nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
+      if (domDoc) {
+        bool hidden = false;
+        domDoc->GetHidden(&hidden);
+        mAudioChannelAgent->SetVisibilityState(!hidden);
+      }
     }
 
     if (mPlayingThroughTheAudioChannel) {
       bool canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
     } else {
       mAudioChannelAgent->StopPlaying();
       mAudioChannelAgent = nullptr;
--- a/content/media/ogg/OggCodecState.cpp
+++ b/content/media/ogg/OggCodecState.cpp
@@ -918,22 +918,16 @@ bool OpusState::DecodeHeader(ogg_packet*
         return false;
       }
 
       mChannels = aPacket->packet[9];
       if (mChannels<1) {
         LOG(PR_LOG_DEBUG, ("Invalid Opus file: Number of channels %d", mChannels));
         return false;
       }
-#ifndef MOZ_SAMPLE_TYPE_FLOAT32
-      // Downmixing more than 2 channels it is not supported for integer
-      // output samples. It is only supported for float output.
-      if (mChannels>2)
-        return false;
-#endif
       mPreSkip = LEUint16(aPacket->packet + 10);
       mNominalRate = LEUint32(aPacket->packet + 12);
       double gain_dB = LEInt16(aPacket->packet + 16) / 256.0;
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
       mGain = static_cast<float>(pow(10,0.05*gain_dB));
 #else
       mGain_Q16 = static_cast<int32_t>(std::min(65536*pow(10,0.05*gain_dB)+0.5,
                                               static_cast<double>(INT32_MAX)));
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -512,23 +512,22 @@ nsresult OggReader::DecodeOpus(ogg_packe
 
   // More than 2 decoded channels must be downmixed to stereo.
   if (channels > 2) {
     // Opus doesn't provide a channel mapping for more than 8 channels,
     // so we can't downmix more than that.
     if (channels > 8)
       return NS_ERROR_FAILURE;
 
-#ifdef MOZ_SAMPLE_TYPE_FLOAT32
     uint32_t out_channels;
     out_channels = 2;
-
     // dBuffer stores the downmixed sample data.
     nsAutoArrayPtr<AudioDataValue> dBuffer(new AudioDataValue[frames * out_channels]);
-    // Downmix matrix for channels up to 8, normalized to 2.0.
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+    // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
     static const float dmatrix[6][8][2]= {
         /*3*/{ {0.5858f,0}, {0.4142f,0.4142f}, {0,0.5858f}},
         /*4*/{ {0.4226f,0}, {0,0.4226f}, {0.366f,0.2114f}, {0.2114f,0.366f}},
         /*5*/{ {0.651f,0}, {0.46f,0.46f}, {0,0.651f}, {0.5636f,0.3254f}, {0.3254f,0.5636f}},
         /*6*/{ {0.529f,0}, {0.3741f,0.3741f}, {0,0.529f}, {0.4582f,0.2645f}, {0.2645f,0.4582f}, {0.3741f,0.3741f}},
         /*7*/{ {0.4553f,0}, {0.322f,0.322f}, {0,0.4553f}, {0.3943f,0.2277f}, {0.2277f,0.3943f}, {0.2788f,0.2788f}, {0.322f,0.322f}},
         /*8*/{ {0.3886f,0}, {0.2748f,0.2748f}, {0,0.3886f}, {0.3366f,0.1943f}, {0.1943f,0.3366f}, {0.3366f,0.1943f}, {0.1943f,0.3366f}, {0.2748f,0.2748f}},
     };
@@ -537,21 +536,42 @@ nsresult OggReader::DecodeOpus(ogg_packe
       float sampR = 0.0;
       for (uint32_t j = 0; j < channels; j++) {
         sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0];
         sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1];
       }
       dBuffer[i*out_channels]=sampL;
       dBuffer[i*out_channels+1]=sampR;
     }
+#else
+    // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
+    // Coefficients in Q14.
+    static const int16_t dmatrix[6][8][2]= {
+        /*3*/{{9598, 0},{6786,6786},{0,   9598}},
+        /*4*/{{6925, 0},{0,   6925},{5997,3462},{3462,5997}},
+        /*5*/{{10663,0},{7540,7540},{0,  10663},{9234,5331},{5331,9234}},
+        /*6*/{{8668, 0},{6129,6129},{0,   8668},{7507,4335},{4335,7507},{6129,6129}},
+        /*7*/{{7459, 0},{5275,5275},{0,   7459},{6460,3731},{3731,6460},{4568,4568},{5275,5275}},
+        /*8*/{{6368, 0},{4502,4502},{0,   6368},{5514,3184},{3184,5514},{5514,3184},{3184,5514},{4502,4502}}
+    };
+    for (int32_t i = 0; i < frames; i++) {
+      int32_t sampL = 0;
+      int32_t sampR = 0;
+      for (uint32_t j = 0; j < channels; j++) {
+        sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0];
+        sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1];
+      }
+      sampL = (sampL + 8192)>>14;
+      dBuffer[i*out_channels]   = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(sampL));
+      sampR = (sampR + 8192)>>14;
+      dBuffer[i*out_channels+1] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(sampR));
+    }
+#endif
     channels = out_channels;
     buffer = dBuffer;
-#else
-  return NS_ERROR_FAILURE;
-#endif
   }
 
   LOG(PR_LOG_DEBUG, ("Opus decoder pushing %d frames", frames));
   int64_t startTime = mOpusState->Time(startFrame);
   int64_t endTime = mOpusState->Time(endFrame);
   mAudioQueue.Push(new AudioData(mPageOffset,
                                  startTime,
                                  endTime - startTime,
--- a/dbm/src/mktemp.c
+++ b/dbm/src/mktemp.c
@@ -79,19 +79,16 @@ mkstempflags(char *path, int extraFlags)
 }
 
 /* NB: This routine modifies its input string, and does not always restore it.
 ** returns 1 on success, 0 on failure.
 */
 static int 
 _gettemp(char *path, register int *doopen, int extraFlags)
 {    
-#if !defined(_WINDOWS) || defined(_WIN32)
-	extern int errno;                    
-#endif
 	register char *start, *trv;
 	struct stat sbuf;
 	unsigned int pid;
 
 	pid = getpid();
 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
 	while (*--trv == 'X') {
 		*trv = (pid % 10) + '0';
--- a/docshell/test/chrome/Makefile.in
+++ b/docshell/test/chrome/Makefile.in
@@ -89,21 +89,26 @@ MOCHITEST_CHROME_FILES =	\
 		test_bug690056.xul \
 		bug690056_window.xul \
 		test_bug311007.xul \
 		bug311007_window.xul \
 		test_principalInherit.xul \
 		test_mozFrameType.xul \
 		mozFrameType_window.xul \
 		test_bug789773.xul \
-		test_private_hidden_window.html \
     docshell_helpers.js \
     generic.html \
     $(NULL)
 
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+MOCHITEST_CHROME_FILES += \
+		test_private_hidden_window.html \
+		$(NULL)
+endif
+
 ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 MOCHITEST_CHROME_FILES += \
 		test_bug454235.xul \
 		$(NULL)
 else
 $(filter disabled-temporarily--bug-684176, test_bug454235.xul)
 endif
 
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -55,16 +55,17 @@ this.AppsUtils = {
       readyToApplyDownload: aApp.readyToApplyDownload,
       downloadSize: aApp.downloadSize || 0,
       lastUpdateCheck: aApp.lastUpdateCheck,
       updateTime: aApp.updateTime,
       etag: aApp.etag,
       packageEtag: aApp.packageEtag,
       manifestHash: aApp.manifestHash,
       packageHash: aApp.packageHash,
+      staged: aApp.staged,
       installerAppId: aApp.installerAppId || Ci.nsIScriptSecurityManager.NO_APP_ID,
       installerIsBrowser: !!aApp.installerIsBrowser
     };
   },
 
   cloneAsMozIApplication: function cloneAsMozIApplication(aApp) {
     let res = this.cloneAppObject(aApp);
     res.hasPermission = function(aPermission) {
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -572,20 +572,25 @@ WebappsApplication.prototype = {
             this._fireEvent("downloadprogress", this._onprogress);
             break;
           case "installed":
             this._manifest = msg.manifest;
             this._fireEvent("downloadsuccess", this._ondownloadsuccess);
             this._fireEvent("downloadapplied", this._ondownloadapplied);
             break;
           case "downloaded":
-            this._manifest = msg.manifest;
+            // We don't update the packaged apps manifests until they
+            // are installed or until the update is unstaged.
+            if (msg.manifest) {
+              this._manifest = msg.manifest;
+            }
             this._fireEvent("downloadsuccess", this._ondownloadsuccess);
             break;
           case "applied":
+            this._manifest = msg.manifest;
             this._fireEvent("downloadapplied", this._ondownloadapplied);
             break;
         }
         break;
     }
   },
 
   classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
@@ -644,16 +649,17 @@ WebappsApplicationMgmt.prototype = {
       return;
     }
 
     cpmm.sendAsyncMessage("Webapps:ApplyDownload",
                           { manifestURL: aApp.manifestURL });
   },
 
   uninstall: function(aApp) {
+    dump("-- webapps.js uninstall " + aApp.manifestURL + "\n");
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
                                                  oid: this._id,
                                                  requestID: this.getRequestId(request) });
     return request;
   },
 
   getAll: function() {
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -846,16 +846,17 @@ this.DOMApplicationRegistry = {
     });
   },
 
   _getAppDir: function(aId) {
     return FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true);
   },
 
   _writeFile: function ss_writeFile(aFile, aData, aCallbak) {
+    debug("Saving " + aFile.path);
     // 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);
     converter.charset = "UTF-8";
 
@@ -892,19 +893,23 @@ this.DOMApplicationRegistry = {
       debug("Could not find a download for " + aManifestURL);
       return;
     }
 
     let app = this.webapps[download.appId];
 
     if (download.cacheUpdate) {
       // Cancel hosted app download.
+      app.isCanceling = true;
       try {
         download.cacheUpdate.cancel();
-      } catch (e) { debug (e); }
+      } catch (e) {
+        delete app.isCanceling;
+        debug (e);
+      }
     } else if (download.channel) {
       // Cancel packaged app download.
       app.isCanceling = true;
       try {
         download.channel.cancel(Cr.NS_BINDING_ABORTED);
       } catch(e) {
         delete app.isCanceling;
       }
@@ -940,18 +945,22 @@ this.DOMApplicationRegistry = {
     let isUpdate = (app.installState == "installed");
 
     // An app download would only be triggered for two reasons: an app
     // update or while retrying to download a previously failed or canceled
     // instalation.
     app.retryingDownload = !isUpdate;
 
     // We need to get the update manifest here, not the webapp manifest.
+    // If this is an update, the update manifest is staged.
     let file = FileUtils.getFile(DIRECTORY_NAME,
-                                 ["webapps", id, "update.webapp"], true);
+                                 ["webapps", id,
+                                  isUpdate ? "staged-update.webapp"
+                                           : "update.webapp"],
+                                 true);
 
     if (!file.exists()) {
       // This is a hosted app, let's check if it has an appcache
       // and download it.
       this._readManifests([{ id: id }], (function readManifest(aResults) {
         let jsonManifest = aResults[0].manifest;
         let manifest = new ManifestHelper(jsonManifest, app.origin);
 
@@ -1003,18 +1012,17 @@ this.DOMApplicationRegistry = {
           app.downloadAvailable = false;
           app.readyToApplyDownload = true;
           app.updateTime = Date.now();
           DOMApplicationRegistry._saveApps(function() {
             debug("About to fire Webapps:PackageEvent");
             DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
                                                     { type: "downloaded",
                                                       manifestURL: aManifestURL,
-                                                      app: app,
-                                                      manifest: aManifest });
+                                                      app: app });
             if (app.installState == "pending") {
               // We restarted a failed download, apply it automatically.
               DOMApplicationRegistry.applyDownload(aManifestURL);
             }
           });
         });
     }).bind(this));
   },
@@ -1038,16 +1046,26 @@ this.DOMApplicationRegistry = {
     manFile.append("manifest.webapp");
     let appFile = tmpDir.clone();
     appFile.append("application.zip");
 
     let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
     appFile.moveTo(dir, "application.zip");
     manFile.moveTo(dir, "manifest.webapp");
 
+    // Move the staged update manifest to a non staged one.
+    let staged = dir.clone();
+    staged.append("staged-update.webapp");
+
+    // If we are applying after a restarted download, we have no
+    // staged update manifest.
+    if (staged.exists()) {
+      staged.moveTo(dir, "update.webapp");
+    }
+
     try {
       tmpDir.remove(true);
     } catch(e) { }
 
     // Flush the zip reader cache to make sure we use the new application.zip
     // when re-launching the application.
     let zipFile = dir.clone();
     zipFile.append("application.zip");
@@ -1055,16 +1073,25 @@ this.DOMApplicationRegistry = {
 
     // Get the manifest, and set properties.
     this.getManifestFor(app.origin, (function(aData) {
       app.downloading = false;
       app.downloadAvailable = false;
       app.downloadSize = 0;
       app.installState = "installed";
       app.readyToApplyDownload = false;
+
+      // Update the staged properties.
+      if (app.staged) {
+        for (let prop in app.staged) {
+          app[prop] = app.staged[prop];
+        }
+        delete app.staged;
+      }
+
       delete app.retryingDownload;
 
       DOMApplicationRegistry._saveApps(function() {
         DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
                                                 { type: "applied",
                                                   manifestURL: app.manifestURL,
                                                   app: app,
                                                   manifest: aData });
@@ -1215,17 +1242,17 @@ this.DOMApplicationRegistry = {
       if (app.manifestURL.startsWith("app://")) {
         aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
         return;
       }
 
       // Store the new update manifest.
       let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       let manFile = dir.clone();
-      manFile.append("update.webapp");
+      manFile.append("staged-update.webapp");
       this._writeFile(manFile, JSON.stringify(aManifest), function() { });
 
       let manifest = new ManifestHelper(aManifest, app.manifestURL);
       // A package is available: set downloadAvailable to fire the matching
       // event.
       app.downloadAvailable = true;
       app.downloadSize = manifest.size;
       aData.event = "downloadavailable";
@@ -1399,16 +1426,17 @@ this.DOMApplicationRegistry = {
       xhr.setRequestHeader("If-None-Match", app.etag);
     }
     xhr.channel.notificationCallbacks =
       this.createLoadContext(app.installerAppId, app.installerIsBrowser);
 
     xhr.addEventListener("load", (function() {
       debug("Got http status=" + xhr.status + " for " + aData.manifestURL);
       let oldHash = app.manifestHash;
+      let isPackage = app.origin.startsWith("app://");
 
       if (xhr.status == 200) {
         let manifest = xhr.response;
         if (manifest == null) {
           sendError("MANIFEST_PARSE_ERROR");
           return;
         }
 
@@ -1416,22 +1444,29 @@ this.DOMApplicationRegistry = {
           sendError("INVALID_MANIFEST");
           return;
         } else if (!AppsUtils.checkInstallAllowed(manifest, app.installOrigin)) {
           sendError("INSTALL_FROM_DENIED");
           return;
         } else {
           let hash = this.computeManifestHash(manifest);
           debug("Manifest hash = " + hash);
-          app.manifestHash = hash;
-
-          app.etag = xhr.getResponseHeader("Etag");
-          debug("at update got app etag=" + app.etag);
+          if (isPackage) {
+            if (!app.staged) {
+              app.staged = { };
+            }
+            app.staged.manifestHash = hash;
+            app.staged.etag = xhr.getResponseHeader("Etag");
+          } else {
+            app.manifestHash = hash;
+            app.etag = xhr.getResponseHeader("Etag");
+          }
+
           app.lastCheckedUpdate = Date.now();
-          if (app.origin.startsWith("app://")) {
+          if (isPackage) {
             if (oldHash != hash) {
               updatePackagedApp.call(this, manifest);
             } else {
               // Like if we got a 304, just send a 'downloadapplied'
               // or downloadavailable event.
               aData.event = app.downloadAvailable ? "downloadavailable"
                                                   : "downloadapplied";
               aData.app = {
@@ -1446,17 +1481,17 @@ this.DOMApplicationRegistry = {
               // based on the hash value.
               updateHostedApp.call(this, aResult[0].manifest,
                                    oldHash == hash ? null : manifest);
             }).bind(this));
           }
         }
       } else if (xhr.status == 304) {
         // The manifest has not changed.
-        if (app.origin.startsWith("app://")) {
+        if (isPackage) {
           // If the app is a packaged app, we just send a 'downloadapplied'
           // or downloadavailable event.
           app.lastCheckedUpdate = Date.now();
           aData.event = app.downloadAvailable ? "downloadavailable"
                                               : "downloadapplied";
           aData.app = {
             lastCheckedUpdate: app.lastCheckedUpdate
           }
@@ -1595,17 +1630,18 @@ this.DOMApplicationRegistry = {
       aMm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
       Cu.reportError("Error installing packaged app from: " +
                      app.installOrigin + ": " + aError);
     }.bind(this);
 
     // Disallow reinstalls from the same manifest URL for now.
     // See https://bugzilla.mozilla.org/show_bug.cgi?id=821288
     if (this.getAppLocalIdByManifestURL(app.manifestURL) !==
-        Ci.nsIScriptSecurityManager.NO_APP_ID) {
+        Ci.nsIScriptSecurityManager.NO_APP_ID ||
+        this._appId(app.origin) !== null) {
       sendError("REINSTALL_FORBIDDEN");
       return;
     }
 
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                 .createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", app.manifestURL, true);
     xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
@@ -1935,20 +1971,33 @@ this.DOMApplicationRegistry = {
       // notification.
       if (app.isCanceling) {
         delete app.isCanceling;
         return;
       }
 
       let download = AppDownloadManager.get(aApp.manifestURL);
       app.downloading = false;
-      // If there were not enough storage to download the packaged app we
+
+      // To prevent repeated prompts, wait for the next checkForUpdates to
+      // try a new download.
+      app.downloadAvailable = false;
+
+      // If there were not enough storage to download the package we
       // won't have a record of the download details, so we just set the
-      // installState to 'pending'.
-      app.installState = download ? download.previousState : "pending";
+      // installState to 'pending' at first download and to 'installed' when
+      // updating.
+      app.installState = download ? download.previousState
+                                  : aIsUpdate ? "installed"
+                                  : "pending";
+
+      if (app.staged) {
+        delete app.staged;
+      }
+
       self.broadcastMessage("Webapps:PackageEvent",
                             { type: "error",
                               manifestURL:  aApp.manifestURL,
                               error: aError,
                               app: app });
       self._saveApps();
       AppDownloadManager.remove(aApp.manifestURL);
     }
@@ -2033,27 +2082,19 @@ this.DOMApplicationRegistry = {
                                    .createInstance(Ci.nsIBufferedOutputStream);
       bufferedOutputStream.init(outputStream, 1024);
 
       // Create a listener that will give data to the file output stream.
       let listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
                        .createInstance(Ci.nsISimpleStreamListener);
       listener.init(bufferedOutputStream, {
         onStartRequest: function(aRequest, aContext) {
-          // early check for ETag header
-          try {
-            requestChannel.getResponseHeader("Etag");
-          } catch (e) {
-            // in https://bugzilla.mozilla.org/show_bug.cgi?id=825218
-            // we might do something cleaner to have a proper user error
-            debug("We found no ETag Header, canceling the request");
-            requestChannel.cancel(Cr.NS_BINDING_ABORTED);
-            AppDownloadManager.remove(aApp.manifestURL);
-          }
+          // Nothing to do there anymore.
         },
+
         onStopRequest: function(aRequest, aContext, aStatusCode) {
           debug("onStopRequest " + aStatusCode);
           bufferedOutputStream.close();
           outputStream.close();
 
           if (!Components.isSuccessCode(aStatusCode)) {
             cleanup("NETWORK_ERROR");
             return;
@@ -2176,27 +2217,31 @@ this.DOMApplicationRegistry = {
 
                 let maxStatus = isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
                                          : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
 
                 if (AppsUtils.getAppManifestStatus(manifest) > maxStatus) {
                   throw "INVALID_SECURITY_LEVEL";
                 }
                 app.appStatus = AppsUtils.getAppManifestStatus(manifest);
-                app.packageHash = aHash;
+
                 // Save the new Etag for the package.
-                try {
+                if (aIsUpdate) {
+                  if (!app.staged) {
+                    app.staged = { };
+                  }
+                  app.staged.packageEtag =
+                    requestChannel.getResponseHeader("Etag");
+                  app.staged.packageHash = aHash;
+                  app.staged.appStatus =
+                    AppsUtils.getAppManifestStatus(manifest);
+                } else {
                   app.packageEtag = requestChannel.getResponseHeader("Etag");
-                  debug("Package etag=" + app.packageEtag);
-                } catch (e) {
-                  // in https://bugzilla.mozilla.org/show_bug.cgi?id=825218
-                  // we'll fail gracefully in this case
-                  // for now, just going on
-                  app.packageEtag = null;
-                  debug("Can't find an etag, this should not happen");
+                  app.packageHash = aHash;
+                  app.appStatus = AppsUtils.getAppManifestStatus(manifest);
                 }
 
                 if (aOnSuccess) {
                   aOnSuccess(id, manifest);
                 }
               } catch (e) {
                 // Something bad happened when reading the package.
                 if (typeof e == 'object') {
@@ -2239,25 +2284,32 @@ this.DOMApplicationRegistry = {
           return;
         }
       }
       download();
     }
   },
 
   uninstall: function(aData, aMm) {
+    debug("uninstall " + aData.origin);
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.origin != aData.origin) {
         continue;
       }
 
+      dump("-- webapps.js uninstall " + app.manifestURL + "\n");
+
       if (!app.removable)
         return;
 
+      // Check if we are downloading something for this app, and cancel the
+      // download if needed.
+      this.cancelDownload(app.manifestURL);
+
       // Clean up the deprecated manifest cache if needed.
       if (id in this._manifestCache) {
         delete this._manifestCache[id];
       }
 
       // Clear private data first.
       this._clearPrivateData(app.localId, false);
 
@@ -2709,32 +2761,41 @@ AppcacheObserver.prototype = {
       DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
                                               { manifest: app.manifestURL,
                                                 installState: app.installState,
                                                 progress: app.progress });
     }
 
     let setError = function appObs_setError(aError) {
       debug("Offlinecache setError to " + aError);
-      DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
-                                              { manifest: app.manifestURL,
-                                                error: aError });
+      // If we are canceling the download, we already send a DOWNLOAD_CANCELED
+      // error.
+      if (!app.isCanceling) {
+        DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
+                                                { manifest: app.manifestURL,
+                                                  error: aError });
+      } else {
+        delete app.isCanceling;
+      }
+
       app.downloading = false;
       app.downloadAvailable = false;
       mustSave = true;
     }
 
     switch (aState) {
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ERROR:
         aUpdate.removeObserver(this);
+        AppDownloadManager.remove(app.manifestURL);
         setError("APP_CACHE_DOWNLOAD_ERROR");
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_NOUPDATE:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED:
         aUpdate.removeObserver(this);
+        AppDownloadManager.remove(app.manifestURL);
         setStatus("installed", aUpdate.byteProgress);
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED:
         setStatus(this.startStatus, aUpdate.byteProgress);
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS:
         let now = Date.now();
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -62,16 +62,17 @@ AudioChannelService::Shutdown()
     gAudioChannelService = nullptr;
   }
 }
 
 NS_IMPL_ISUPPORTS0(AudioChannelService)
 
 AudioChannelService::AudioChannelService()
 : mCurrentHigherChannel(AUDIO_CHANNEL_LAST)
+, mActiveContentChildIDsFrozen(false)
 {
   // Creation of the hash table.
   mAgents.Init();
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
@@ -164,28 +165,48 @@ AudioChannelService::GetMutedInternal(Au
   AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
   AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
 
+  // If the audio content channel is visible, let's remember this ChildID.
+  if (newType == AUDIO_CHANNEL_INT_CONTENT &&
+      oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+      !mActiveContentChildIDs.Contains(aChildID)) {
+
+    if (mActiveContentChildIDsFrozen) {
+      mActiveContentChildIDsFrozen = false;
+      mActiveContentChildIDs.Clear();
+    }
+
+    mActiveContentChildIDs.AppendElement(aChildID);
+  }
+
+  // If nothing is visible, the list has to been frozen.
+  else if (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+           oldType == AUDIO_CHANNEL_INT_CONTENT &&
+           !mActiveContentChildIDsFrozen &&
+           mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
+    mActiveContentChildIDsFrozen = true;
+  }
+
   // Let play any visible audio channel.
   if (!aElementHidden) {
     return false;
   }
 
   bool muted = false;
 
   // We are not visible, maybe we have to mute.
   if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN ||
       (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-       (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() ||
-        HasMoreThanOneContentChannelHidden()))) {
+       !mActiveContentChildIDs.Contains(aChildID))) {
     muted = true;
   }
 
   if (!muted) {
     MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
     muted = ChannelsActiveWithHigherPriorityThan(newType);
   }
 
@@ -194,35 +215,16 @@ AudioChannelService::GetMutedInternal(Au
 
 bool
 AudioChannelService::ContentChannelIsActive()
 {
   return !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() ||
          !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty();
 }
 
-bool
-AudioChannelService::HasMoreThanOneContentChannelHidden()
-{
-  uint32_t childId = CONTENT_PARENT_UNKNOWN_CHILD_ID;
-  bool empty = true;
-  for (uint32_t i = 0;
-       i < mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Length();
-       ++i) {
-    if (empty) {
-      childId = mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN][i];
-      empty = false;
-    } else if (childId != mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN][i]) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 void
 AudioChannelService::SendAudioChannelChangedNotification()
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
   // Calculating the most important active channel.
@@ -274,18 +276,17 @@ AudioChannelService::SendAudioChannelCha
     higher = AUDIO_CHANNEL_ALARM;
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
     higher = AUDIO_CHANNEL_NOTIFICATION;
   }
 
   // Content channels play in background if just one is active.
-  else if ((!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty() &&
-            !HasMoreThanOneContentChannelHidden())) {
+  else if (!mActiveContentChildIDs.IsEmpty()) {
     higher = AUDIO_CHANNEL_CONTENT;
   }
 
   if (higher != mCurrentHigherChannel) {
     mCurrentHigherChannel = higher;
 
     nsString channelName;
     if (mCurrentHigherChannel != AUDIO_CHANNEL_LAST) {
@@ -386,16 +387,20 @@ AudioChannelService::Observe(nsISupports
   if (NS_SUCCEEDED(rv)) {
     for (int32_t type = AUDIO_CHANNEL_INT_NORMAL;
          type < AUDIO_CHANNEL_INT_LAST;
          ++type) {
       int32_t index;
       while ((index = mChannelCounters[type].IndexOf(childID)) != -1) {
         mChannelCounters[type].RemoveElementAt(index);
       }
+
+      if ((index = mActiveContentChildIDs.IndexOf(childID)) != -1) {
+        mActiveContentChildIDs.RemoveElementAt(index);
+      }
     }
 
     // We don't have to remove the agents from the mAgents hashtable because if
     // that table contains only agents running on the same process.
 
     SendAudioChannelChangedNotification();
     Notify();
   } else {
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -93,18 +93,16 @@ protected:
     AUDIO_CHANNEL_INT_RINGER_HIDDEN,
     AUDIO_CHANNEL_INT_PUBLICNOTIFICATION,
     AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN,
     AUDIO_CHANNEL_INT_LAST
   };
 
   bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType);
 
-  bool HasMoreThanOneContentChannelHidden();
-
   const char* ChannelName(AudioChannelType aType);
 
   AudioChannelInternalType GetInternalType(AudioChannelType aType,
                                            bool aElementHidden);
 
   class AudioChannelAgentData {
   public:
     AudioChannelAgentData(AudioChannelType aType,
@@ -125,16 +123,19 @@ protected:
                    AudioChannelAgentData* aData, void *aUnused);
 
   nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 
   nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
 
   AudioChannelType mCurrentHigherChannel;
 
+  nsTArray<uint64_t> mActiveContentChildIDs;
+  bool mActiveContentChildIDsFrozen;
+
   // This is needed for IPC comunication between
   // AudioChannelServiceChild and this class.
   friend class ContentParent;
   friend class ContentChild;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -140,16 +140,22 @@ TestContentChannels()
   Agent agent1(AUDIO_CHANNEL_CONTENT);
   nsresult rv = agent1.Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   Agent agent2(AUDIO_CHANNEL_CONTENT);
   rv = agent2.Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = agent1.mAgent->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = agent2.mAgent->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   bool playing;
   rv = agent1.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent1 should be playing");
 
   rv = agent2.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing");
@@ -161,17 +167,17 @@ TestContentChannels()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent1.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent1 should be playing");
 
   rv = agent2.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(!playing, "Test3: A content channel unvisible agent2 should not be playing");
+  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing");
 
   rv = agent1.mAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent2.mAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent1.StartPlaying(&playing);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -372,17 +372,16 @@
 #include "nsIImageDocument.h"
 
 // Storage includes
 #include "nsDOMStorage.h"
 
 // Device Storage
 #include "nsIDOMDeviceStorage.h"
 #include "nsIDOMDeviceStorageCursor.h"
-#include "nsIDOMDeviceStorageStat.h"
 
 // Drag and drop
 #include "nsIDOMDataTransfer.h"
 
 // Geolocation
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPosition.h"
 #include "nsIDOMGeoPositionCoords.h"
@@ -467,16 +466,17 @@ using mozilla::dom::workers::ResolveWork
 #include "TelephonyCall.h"
 #include "CallEvent.h"
 #include "nsIDOMVoicemail.h"
 #include "nsIDOMVoicemailEvent.h"
 #include "nsIDOMIccManager.h"
 #include "StkCommandEvent.h"
 #include "nsIDOMMozCellBroadcast.h"
 #include "nsIDOMMozCellBroadcastEvent.h"
+#include "CFStateChangeEvent.h"
 #endif // MOZ_B2G_RIL
 
 #ifdef MOZ_B2G_FM
 #include "FMRadio.h"
 #endif
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
@@ -1319,19 +1319,16 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DeviceStorage, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DeviceStorageCursor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  NS_DEFINE_CLASSINFO_DATA(DeviceStorageStat, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   NS_DEFINE_CLASSINFO_DATA(GeoGeolocation, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   
   NS_DEFINE_CLASSINFO_DATA(GeoPosition, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS) 
   
   NS_DEFINE_CLASSINFO_DATA(GeoPositionCoords, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -1373,16 +1370,19 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozMobileConnection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozCellBroadcast, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(CFStateChangeEvent, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(USSDReceivedEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DataErrorEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
@@ -3579,20 +3579,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DeviceStorageCursor, nsIDOMDeviceStorageCursor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageCursor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(DeviceStorageStat, nsIDOMDeviceStorageStat)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageStat)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(GeoGeolocation, nsIDOMGeoGeolocation)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoGeolocation)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(GeoPosition, nsIDOMGeoPosition)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoPosition)
   DOM_CLASSINFO_MAP_END
 
@@ -3662,16 +3658,21 @@ nsDOMClassInfo::Init()
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCellBroadcast)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozCellBroadcastEvent, nsIDOMMozCellBroadcastEvent)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCellBroadcastEvent)
      DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(CFStateChangeEvent, nsIDOMCFStateChangeEvent)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCFStateChangeEvent)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
+  DOM_CLASSINFO_MAP_END
 #endif // MOZ_B2G_RIL
 
   DOM_CLASSINFO_MAP_BEGIN(USSDReceivedEvent, nsIDOMUSSDReceivedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMUSSDReceivedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
   DOM_CLASSINFO_MAP_END
  
   DOM_CLASSINFO_MAP_BEGIN(DataErrorEvent, nsIDOMDataErrorEvent)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -322,17 +322,16 @@ DOMCI_CLASS(ModalContentWindow)
 DOMCI_CLASS(DataContainerEvent)
 
 // event used for cross-domain message-passing and for server-sent events in
 // HTML5
 DOMCI_CLASS(MessageEvent)
 
 DOMCI_CLASS(DeviceStorage)
 DOMCI_CLASS(DeviceStorageCursor)
-DOMCI_CLASS(DeviceStorageStat)
 
 // Geolocation
 DOMCI_CLASS(GeoGeolocation)
 DOMCI_CLASS(GeoPosition)
 DOMCI_CLASS(GeoPositionCoords)
 DOMCI_CLASS(GeoPositionError)
 
 DOMCI_CLASS(BatteryManager)
@@ -347,16 +346,17 @@ DOMCI_CLASS(MozSmsRequest)
 DOMCI_CLASS(MozSmsFilter)
 DOMCI_CLASS(MozSmsCursor)
 DOMCI_CLASS(MozSmsSegmentInfo)
 
 DOMCI_CLASS(MozConnection)
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozMobileConnection)
 DOMCI_CLASS(MozCellBroadcast)
+DOMCI_CLASS(CFStateChangeEvent)
 #endif
 
 DOMCI_CLASS(USSDReceivedEvent)
 
 DOMCI_CLASS(DataErrorEvent)
 
 // @font-face in CSS
 DOMCI_CLASS(CSSFontFaceRule)
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1291,20 +1291,20 @@ nsJSContext::EvaluateString(const nsAStr
       JSString* str = JS_ValueToString(mContext, *aRetValue);
       ok = !!str;
       *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue();
     }
     --mExecuteDepth;
   }
 
   if (!ok) {
+    *aRetValue = JS::UndefinedValue();
     // Tell XPConnect about any pending exceptions. This is needed
     // to avoid dropping JS exceptions in case we got here through
     // nested calls through XPConnect.
-
     ReportPendingException();
   }
 
   // ScriptEvaluated needs to come after we pop the stack
   pusher.Pop();
   ScriptEvaluated(true);
 
   // Wrap the return value into whatever compartment mContext was in.
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -91,17 +91,16 @@ nsScreen::~nsScreen()
 
 DOMCI_DATA(Screen, nsScreen)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_0(nsScreen, nsDOMEventTargetHelper)
 
 // QueryInterface implementation for nsScreen
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsScreen)
   NS_INTERFACE_MAP_ENTRY(nsIDOMScreen)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMScreen)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Screen)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(nsScreen, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(nsScreen, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(nsScreen, mozorientationchange)
 
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -2,19 +2,22 @@
  * 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 "base/basictypes.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
 #include "jsapi.h"
 #include "nsThread.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/Services.h"
+#include "mozilla/unused.h"
 #include "nsIObserverService.h"
 #include "nsIDOMDeviceStorage.h"
+#include "nsXULAppAPI.h"
 #include "DOMCameraManager.h"
 #include "DOMCameraCapabilities.h"
 #include "DOMCameraControl.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace dom;
 
@@ -254,16 +257,21 @@ nsDOMCameraControl::StartRecording(const
   if (!obs) {
     NS_WARNING("Could not get the Observer service for CameraControl::StartRecording.");
     return NS_ERROR_FAILURE;
   }
 
   obs->NotifyObservers(nullptr,
                        "recording-device-events",
                        NS_LITERAL_STRING("starting").get());
+  // Forward recording events to parent process.
+  // The events are gathered in chrome process and used for recording indicator
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    unused << ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("starting"));
+  }
 
   #ifdef MOZ_B2G
   if (!mAudioChannelAgent) {
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     if (mAudioChannelAgent) {
       // Camera app will stop recording when it falls to the background, so no callback is necessary.
       mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, nullptr);
       // Video recording doesn't output any sound, so it's not necessary to check canPlay.
@@ -286,16 +294,21 @@ nsDOMCameraControl::StopRecording()
   if (!obs) {
     NS_WARNING("Could not get the Observer service for CameraControl::StopRecording.");
     return NS_ERROR_FAILURE;
   }
 
   obs->NotifyObservers(nullptr,
                        "recording-device-events",
                        NS_LITERAL_STRING("shutdown").get());
+  // Forward recording events to parent process.
+  // The events are gathered in chrome process and used for recording indicator
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    unused << ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("shutdown"));
+  }
 
   #ifdef MOZ_B2G
   if (mAudioChannelAgent) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
   }
   #endif
 
--- a/dom/cellbroadcast/src/CellBroadcast.cpp
+++ b/dom/cellbroadcast/src/CellBroadcast.cpp
@@ -49,17 +49,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CellBroadcast,
                                                 nsDOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CellBroadcast)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozCellBroadcast)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozCellBroadcast)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCellBroadcast)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(CellBroadcast, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(CellBroadcast, nsDOMEventTargetHelper)
 
 CellBroadcast::CellBroadcast(nsPIDOMWindow *aWindow,
                              nsIRILContentHelper *aRIL)
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -63,22 +63,36 @@ DeviceStorageRequestChild::Recv__delete_
       nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
 
       nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
       jsval result = InterfaceToJsval(mRequest->GetOwner(), file, &NS_GET_IID(nsIDOMFile));
       mRequest->FireSuccess(result);
       break;
     }
 
-    case DeviceStorageResponseValue::TStatStorageResponse:
+    case DeviceStorageResponseValue::TFreeSpaceStorageResponse:
+    {
+      FreeSpaceStorageResponse r = aValue;
+      jsval result = JS_NumberValue(double(r.freeBytes()));
+      mRequest->FireSuccess(result);
+      break;
+    }
+
+    case DeviceStorageResponseValue::TUsedSpaceStorageResponse:
     {
-      StatStorageResponse r = aValue;
+      UsedSpaceStorageResponse r = aValue;
+      jsval result = JS_NumberValue(double(r.usedBytes()));
+      mRequest->FireSuccess(result);
+      break;
+    }
 
-      nsRefPtr<nsIDOMDeviceStorageStat> domstat = new nsDOMDeviceStorageStat(r.freeBytes(), r.totalBytes(), r.mountState());
-      jsval result = InterfaceToJsval(mRequest->GetOwner(), domstat, &NS_GET_IID(nsIDOMDeviceStorageStat));
+    case DeviceStorageResponseValue::TAvailableStorageResponse:
+    {
+      AvailableStorageResponse r = aValue;
+      jsval result = StringToJsval(mRequest->GetOwner(), r.mountState());
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TEnumerationResponse:
     {
       EnumerationResponse r = aValue;
       nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -81,32 +81,55 @@ DeviceStorageRequestParent::Dispatch()
       nsRefPtr<CancelableRunnable> r = new DeleteFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
-    case DeviceStorageParams::TDeviceStorageStatParams:
+    case DeviceStorageParams::TDeviceStorageFreeSpaceParams:
     {
-      DeviceStorageStatParams p = mParams;
+      DeviceStorageFreeSpaceParams p = mParams;
 
       nsCOMPtr<nsIFile> f;
       NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
 
       nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
-      nsRefPtr<StatFileEvent> r = new StatFileEvent(this, dsf);
+      nsRefPtr<FreeSpaceFileEvent> r = new FreeSpaceFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
+    case DeviceStorageParams::TDeviceStorageUsedSpaceParams:
+    {
+      DeviceStorageUsedSpaceParams p = mParams;
+
+      nsCOMPtr<nsIFile> f;
+      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<UsedSpaceFileEvent> r = new UsedSpaceFileEvent(this, dsf);
+
+      nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      NS_ASSERTION(target, "Must have stream transport service");
+      target->Dispatch(r, NS_DISPATCH_NORMAL);
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageAvailableParams:
+    {
+      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this);
+      NS_DispatchToMainThread(r);
+      break;
+    }
+
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
 
       nsCOMPtr<nsIFile> f;
       NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
 
       nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
@@ -156,21 +179,37 @@ DeviceStorageRequestParent::EnsureRequir
     case DeviceStorageParams::TDeviceStorageDeleteParams:
     {
       DeviceStorageDeleteParams p = mParams;
       type = p.type();
       requestType = DEVICE_STORAGE_REQUEST_DELETE;
       break;
     }
 
-    case DeviceStorageParams::TDeviceStorageStatParams:
+    case DeviceStorageParams::TDeviceStorageFreeSpaceParams:
+    {
+      DeviceStorageFreeSpaceParams p = mParams;
+      type = p.type();
+      requestType = DEVICE_STORAGE_REQUEST_FREE_SPACE;
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageUsedSpaceParams:
     {
-      DeviceStorageStatParams p = mParams;
+      DeviceStorageUsedSpaceParams p = mParams;
       type = p.type();
-      requestType = DEVICE_STORAGE_REQUEST_STAT;
+      requestType = DEVICE_STORAGE_REQUEST_FREE_SPACE;
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageAvailableParams:
+    {
+      DeviceStorageAvailableParams p = mParams;
+      type = p.type();
+      requestType = DEVICE_STORAGE_REQUEST_AVAILABLE;
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
       type = p.type();
       requestType = DEVICE_STORAGE_REQUEST_READ;
@@ -227,16 +266,52 @@ DeviceStorageRequestParent::ActorDestroy
   MutexAutoLock lock(mMutex);
   mActorDestoryed = true;
   int32_t count = mRunnables.Length();
   for (int32_t index = 0; index < count; index++) {
     mRunnables[index]->Cancel();
   }
 }
 
+DeviceStorageRequestParent::PostFreeSpaceResultEvent::PostFreeSpaceResultEvent(DeviceStorageRequestParent* aParent,
+                                                                               int64_t aFreeSpace)
+  : CancelableRunnable(aParent)
+  , mFreeSpace(aFreeSpace)
+{
+}
+
+DeviceStorageRequestParent::PostFreeSpaceResultEvent::~PostFreeSpaceResultEvent() {}
+
+nsresult
+DeviceStorageRequestParent::PostFreeSpaceResultEvent::CancelableRun() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  FreeSpaceStorageResponse response(mFreeSpace);
+  unused << mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::PostUsedSpaceResultEvent::PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent,
+                                                                               int64_t aUsedSpace)
+  : CancelableRunnable(aParent)
+  , mUsedSpace(aUsedSpace)
+{
+}
+
+DeviceStorageRequestParent::PostUsedSpaceResultEvent::~PostUsedSpaceResultEvent() {}
+
+nsresult
+DeviceStorageRequestParent::PostUsedSpaceResultEvent::CancelableRun() {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  UsedSpaceStorageResponse response(mUsedSpace);
+  unused << mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
 DeviceStorageRequestParent::PostErrorEvent::PostErrorEvent(DeviceStorageRequestParent* aParent,
                                                            const char* aError)
   : CancelableRunnable(aParent)
 {
   CopyASCIItoUTF16(aError, mError);
 }
 
 DeviceStorageRequestParent::PostErrorEvent::~PostErrorEvent() {}
@@ -245,30 +320,29 @@ nsresult
 DeviceStorageRequestParent::PostErrorEvent::CancelableRun() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   ErrorResponse response(mError);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
-
 DeviceStorageRequestParent::PostSuccessEvent::PostSuccessEvent(DeviceStorageRequestParent* aParent)
   : CancelableRunnable(aParent)
 {
 }
 
 DeviceStorageRequestParent::PostSuccessEvent::~PostSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostSuccessEvent::CancelableRun() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
-  unused <<  mParent->Send__delete__(mParent, response);
+  unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostBlobSuccessEvent::PostBlobSuccessEvent(DeviceStorageRequestParent* aParent,
                                                                        DeviceStorageFile* aFile,
                                                                        uint32_t aLength,
                                                                        nsACString& aMimeType,
                                                                        uint64_t aLastModifiedDate)
@@ -297,17 +371,17 @@ DeviceStorageRequestParent::PostBlobSucc
     ErrorResponse response(NS_LITERAL_STRING(POST_ERROR_EVENT_UNKNOWN));
     unused << mParent->Send__delete__(mParent, response);
     return NS_OK;
   }
 
   BlobResponse response;
   response.blobParent() = actor;
 
-  unused <<  mParent->Send__delete__(mParent, response);
+  unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
                                                                                      InfallibleTArray<DeviceStorageFileValue>& aPaths)
   : CancelableRunnable(aParent)
   , mPaths(aPaths)
 {
@@ -315,17 +389,17 @@ DeviceStorageRequestParent::PostEnumerat
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::~PostEnumerationSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   EnumerationResponse response(mPaths);
-  unused <<  mParent->Send__delete__(mParent, response);
+  unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::WriteFileEvent::WriteFileEvent(DeviceStorageRequestParent* aParent,
                                                            DeviceStorageFile* aFile,
                                                            nsIInputStream* aInputStream)
   : CancelableRunnable(aParent)
   , mFile(aFile)
@@ -400,42 +474,65 @@ DeviceStorageRequestParent::DeleteFileEv
   else {
     r = new PostPathResultEvent(mParent, mFile->mPath);
   }
 
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
-DeviceStorageRequestParent::StatFileEvent::StatFileEvent(DeviceStorageRequestParent* aParent,
-                                                         DeviceStorageFile* aFile)
+DeviceStorageRequestParent::FreeSpaceFileEvent::FreeSpaceFileEvent(DeviceStorageRequestParent* aParent,
+                                                                   DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
 }
 
-DeviceStorageRequestParent::StatFileEvent::~StatFileEvent()
+DeviceStorageRequestParent::FreeSpaceFileEvent::~FreeSpaceFileEvent()
 {
 }
 
 nsresult
-DeviceStorageRequestParent::StatFileEvent::CancelableRun()
+DeviceStorageRequestParent::FreeSpaceFileEvent::CancelableRun()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIRunnable> r;
+  int64_t freeSpace = 0;
+  nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
+  if (NS_FAILED(rv)) {
+    freeSpace = 0;
+  }
+
+  r = new PostFreeSpaceResultEvent(mParent, freeSpace);
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+DeviceStorageRequestParent::UsedSpaceFileEvent::UsedSpaceFileEvent(DeviceStorageRequestParent* aParent,
+                                                                   DeviceStorageFile* aFile)
+  : CancelableRunnable(aParent)
+  , mFile(aFile)
+{
+}
+
+DeviceStorageRequestParent::UsedSpaceFileEvent::~UsedSpaceFileEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::UsedSpaceFileEvent::CancelableRun()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIRunnable> r;
   uint64_t diskUsage = 0;
   DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage, mFile->mStorageType);
-  int64_t freeSpace = 0;
-  nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
-  if (NS_FAILED(rv)) {
-    freeSpace = 0;
-  }
 
-  r = new PostStatResultEvent(mParent, freeSpace, diskUsage);
+  r = new PostUsedSpaceResultEvent(mParent, diskUsage);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::ReadFileEvent::ReadFileEvent(DeviceStorageRequestParent* aParent,
                                                          DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
@@ -547,48 +644,44 @@ DeviceStorageRequestParent::PostPathResu
 }
 
 nsresult
 DeviceStorageRequestParent::PostPathResultEvent::CancelableRun()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
-  unused <<  mParent->Send__delete__(mParent, response);
+  unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
-DeviceStorageRequestParent::PostStatResultEvent::PostStatResultEvent(DeviceStorageRequestParent* aParent,
-                                                                     int64_t aFreeBytes,
-                                                                     int64_t aTotalBytes)
+DeviceStorageRequestParent::PostAvailableResultEvent::PostAvailableResultEvent(DeviceStorageRequestParent* aParent)
   : CancelableRunnable(aParent)
-  , mFreeBytes(aFreeBytes)
-  , mTotalBytes(aTotalBytes)
 {
 }
 
-DeviceStorageRequestParent::PostStatResultEvent::~PostStatResultEvent()
+DeviceStorageRequestParent::PostAvailableResultEvent::~PostAvailableResultEvent()
 {
 }
 
 nsresult
-DeviceStorageRequestParent::PostStatResultEvent::CancelableRun()
+DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsString state;
   state.Assign(NS_LITERAL_STRING("available"));
 #ifdef MOZ_WIDGET_GONK
   nsresult rv = GetSDCardStatus(state);
   if (NS_FAILED(rv)) {
     state.Assign(NS_LITERAL_STRING("unavailable"));
   }
 #endif
 
-  StatStorageResponse response(mFreeBytes, mTotalBytes, state);
-  unused <<  mParent->Send__delete__(mParent, response);
+  AvailableStorageResponse response(state);
+  unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 
 } // namespace devicestorage
 } // namespace dom
 } // namespace mozilla
--- a/dom/devicestorage/DeviceStorageRequestParent.h
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -128,25 +128,35 @@ private:
     public:
       DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
       virtual ~DeleteFileEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
   };
 
-  class StatFileEvent : public CancelableRunnable
+  class FreeSpaceFileEvent : public CancelableRunnable
   {
     public:
-      StatFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
-      virtual ~StatFileEvent();
+      FreeSpaceFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      virtual ~FreeSpaceFileEvent();
       virtual nsresult CancelableRun();
      private:
        nsRefPtr<DeviceStorageFile> mFile;
-   };
+  };
+
+  class UsedSpaceFileEvent : public CancelableRunnable
+  {
+    public:
+      UsedSpaceFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      virtual ~UsedSpaceFileEvent();
+      virtual nsresult CancelableRun();
+     private:
+       nsRefPtr<DeviceStorageFile> mFile;
+  };
 
   class ReadFileEvent : public CancelableRunnable
   {
     public:
       ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
       virtual ~ReadFileEvent();
       virtual nsresult CancelableRun();
     private:
@@ -171,27 +181,45 @@ private:
       PostPathResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
       virtual ~PostPathResultEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
       nsString mPath;
   };
 
-  class PostStatResultEvent : public CancelableRunnable
+ class PostFreeSpaceResultEvent : public CancelableRunnable
+ {
+    public:
+      PostFreeSpaceResultEvent(DeviceStorageRequestParent* aParent,
+                               int64_t aFreeSpace);
+      virtual ~PostFreeSpaceResultEvent();
+      virtual nsresult CancelableRun();
+    private:
+      int64_t mFreeSpace;
+ };
+
+ class PostUsedSpaceResultEvent : public CancelableRunnable
  {
     public:
-      PostStatResultEvent(DeviceStorageRequestParent* aParent,
-                          int64_t aFreeBytes,
-                          int64_t aTotalBytes);
-      virtual ~PostStatResultEvent();
+      PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent,
+                               int64_t aUsedSpace);
+      virtual ~PostUsedSpaceResultEvent();
       virtual nsresult CancelableRun();
     private:
-      int64_t mFreeBytes, mTotalBytes;
-   };
+      int64_t mUsedSpace;
+ };
+
+ class PostAvailableResultEvent : public CancelableRunnable
+ {
+    public:
+      PostAvailableResultEvent(DeviceStorageRequestParent* aParent);
+      virtual ~PostAvailableResultEvent();
+      virtual nsresult CancelableRun();
+ };
 
 protected:
   bool AddRunnable(CancelableRunnable* aRunnable) {
     MutexAutoLock lock(mMutex);
     if (mActorDestoryed)
       return false;
 
     mRunnables.AppendElement(aRunnable);
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -32,30 +32,40 @@ struct DeviceStorageFileValue
   nsString fullpath;
 };
 
 struct EnumerationResponse
 {
   DeviceStorageFileValue[] paths;
 };
 
-struct StatStorageResponse
+struct FreeSpaceStorageResponse
 {
   int64_t freeBytes;
-  int64_t totalBytes;
+};
+
+struct UsedSpaceStorageResponse
+{
+  int64_t usedBytes;
+};
+
+struct AvailableStorageResponse
+{
   nsString mountState;
 };
 
 union DeviceStorageResponseValue
 {
   ErrorResponse;
   SuccessResponse;
   BlobResponse;
   EnumerationResponse;
-  StatStorageResponse;
+  FreeSpaceStorageResponse;
+  UsedSpaceStorageResponse;
+  AvailableStorageResponse;
 };
 
 sync protocol PDeviceStorageRequest {
     manager PContent;
 child:
     __delete__(DeviceStorageResponseValue response);
 };
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -203,17 +203,19 @@ DeviceStorageTypeChecker::GetPermissionF
 }
 
 nsresult
 DeviceStorageTypeChecker::GetAccessForRequest(const DeviceStorageRequestType aRequestType, nsACString& aAccessResult)
 {
   switch(aRequestType) {
     case DEVICE_STORAGE_REQUEST_READ:
     case DEVICE_STORAGE_REQUEST_WATCH:
-    case DEVICE_STORAGE_REQUEST_STAT:
+    case DEVICE_STORAGE_REQUEST_FREE_SPACE:
+    case DEVICE_STORAGE_REQUEST_USED_SPACE:
+    case DEVICE_STORAGE_REQUEST_AVAILABLE:
       aAccessResult.AssignLiteral("read");
       break;
     case DEVICE_STORAGE_REQUEST_WRITE:
     case DEVICE_STORAGE_REQUEST_DELETE:
       aAccessResult.AssignLiteral("write");
       break;
     case DEVICE_STORAGE_REQUEST_CREATE:
       aAccessResult.AssignLiteral("create");
@@ -829,18 +831,17 @@ jsval nsIFileToJsval(nsPIDOMWindow* aWin
   if (aFile->mEditable) {
     // TODO - needs janv's file handle support.
     return JSVAL_NULL;
   }
 
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(aFile->mFile, aFile->mPath,
                                                 EmptyString());
   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
- }
-
+}
 
 jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWindow, "Null Window");
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
@@ -1221,55 +1222,46 @@ nsDOMDeviceStorageCursor::IPDLRelease()
 
 void
 nsDOMDeviceStorageCursor::RequestComplete()
 {
   NS_ASSERTION(!mOkToCallContinue, "mOkToCallContinue must be false");  
   mOkToCallContinue = true;
 }
 
-class PostStatResultEvent : public nsRunnable
+class PostAvailableResultEvent : public nsRunnable
 {
 public:
-  PostStatResultEvent(nsRefPtr<DOMRequest>& aRequest, uint64_t aFreeBytes, uint64_t aTotalBytes)
-    : mFreeBytes(aFreeBytes)
-    , mTotalBytes(aTotalBytes)
-    {
-      mRequest.swap(aRequest);
-    }
-
-  ~PostStatResultEvent() {}
+  PostAvailableResultEvent(DOMRequest* aRequest)
+    : mRequest(aRequest)
+  {
+  }
+
+  ~PostAvailableResultEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     nsString state;
     state.Assign(NS_LITERAL_STRING("available"));
 #ifdef MOZ_WIDGET_GONK
     nsresult rv = GetSDCardStatus(state);
     if (NS_FAILED(rv)) {
       state.Assign(NS_LITERAL_STRING("unavailable"));
     }
 #endif
 
-    nsRefPtr<nsIDOMDeviceStorageStat> domstat = new nsDOMDeviceStorageStat(mFreeBytes, mTotalBytes, state);
-
-    jsval result = InterfaceToJsval(mRequest->GetOwner(),
-                                    domstat,
-                                    &NS_GET_IID(nsIDOMDeviceStorageStat));
-
+    jsval result = StringToJsval(mRequest->GetOwner(), state);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
-  uint64_t mFreeBytes, mTotalBytes;
-  nsString mState;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, DeviceStorageFile* aFile)
     : mFile(aFile)
@@ -1278,37 +1270,49 @@ public:
     }
 
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, const nsAString & aPath)
     : mPath(aPath)
     {
       mRequest.swap(aRequest);
     }
 
+  PostResultEvent(nsRefPtr<DOMRequest>& aRequest, const int64_t aValue)
+    : mValue(aValue)
+    {
+      mRequest.swap(aRequest);
+    }
+
   ~PostResultEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     jsval result = JSVAL_NULL;
+    nsPIDOMWindow* window = mRequest->GetOwner();
+
     if (mFile) {
-      result = nsIFileToJsval(mRequest->GetOwner(), mFile);
-    } else {
-      result = StringToJsval(mRequest->GetOwner(), mPath);
+      result = nsIFileToJsval(window, mFile);
+    } else if (mPath.Length()) {
+      result = StringToJsval(window, mPath);
+    }
+    else {
+      result = JS_NumberValue(double(mValue));
     }
 
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsString mPath;
+  int64_t mValue;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class WriteFileEvent : public nsRunnable
 {
 public:
   WriteFileEvent(nsIDOMBlob* aBlob,
                  DeviceStorageFile *aFile,
@@ -1424,40 +1428,66 @@ public:
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
-class StatFileEvent : public nsRunnable
+class UsedSpaceFileEvent : public nsRunnable
 {
 public:
-  StatFileEvent(DeviceStorageFile* aFile, nsRefPtr<DOMRequest>& aRequest)
+  UsedSpaceFileEvent(DeviceStorageFile* aFile, nsRefPtr<DOMRequest>& aRequest)
   : mFile(aFile)
     {
       mRequest.swap(aRequest);
     }
 
-  ~StatFileEvent() {}
+  ~UsedSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
     nsCOMPtr<nsIRunnable> r;
     uint64_t diskUsage = 0;
     DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage, mFile->mStorageType);
+
+    r = new PostResultEvent(mRequest, diskUsage);
+    NS_DispatchToMainThread(r);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<DeviceStorageFile> mFile;
+  nsRefPtr<DOMRequest> mRequest;
+};
+
+class FreeSpaceFileEvent : public nsRunnable
+{
+public:
+  FreeSpaceFileEvent(DeviceStorageFile* aFile, nsRefPtr<DOMRequest>& aRequest)
+  : mFile(aFile)
+    {
+      mRequest.swap(aRequest);
+    }
+
+  ~FreeSpaceFileEvent() {}
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    nsCOMPtr<nsIRunnable> r;
     int64_t freeSpace = 0;
     nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
     if (NS_FAILED(rv)) {
       freeSpace = 0;
     }
 
-    r = new PostStatResultEvent(mRequest, freeSpace, diskUsage);
+    r = new PostResultEvent(mRequest, freeSpace);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
@@ -1686,28 +1716,53 @@ public:
           DeviceStorageDeleteParams params(mFile->mStorageType, fullpath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new DeleteFileEvent(mFile, mRequest);
         break;
       }
 
-      case DEVICE_STORAGE_REQUEST_STAT:
+      case DEVICE_STORAGE_REQUEST_FREE_SPACE:
+      {
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+          DeviceStorageFreeSpaceParams params(mFile->mStorageType, fullpath);
+          ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
+        r = new FreeSpaceFileEvent(mFile, mRequest);
+        break;
+      }
+
+      case DEVICE_STORAGE_REQUEST_USED_SPACE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageStatParams params(mFile->mStorageType, fullpath);
+          DeviceStorageUsedSpaceParams params(mFile->mStorageType, fullpath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
-        r = new StatFileEvent(mFile, mRequest);
+        r = new UsedSpaceFileEvent(mFile, mRequest);
         break;
       }
 
+      case DEVICE_STORAGE_REQUEST_AVAILABLE:
+      {
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+          DeviceStorageAvailableParams params(mFile->mStorageType);
+          ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
+        r = new PostAvailableResultEvent(mRequest);
+        NS_DispatchToMainThread(r);
+        return NS_OK;
+      }
+
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
         mDeviceStorage->mAllowedToWatchFile = true;
         return NS_OK;
       }
     }
 
     if (r) {
@@ -2007,28 +2062,70 @@ nsDOMDeviceStorage::Delete(const JS::Val
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
                                  win, mPrincipal, dsf, request);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMDeviceStorage::Stat(nsIDOMDOMRequest** aRetval)
+nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval)
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
-  nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_STAT,
+  nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
+                                                     win,
+                                                     mPrincipal,
+                                                     dsf,
+                                                     request);
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(win);
+  NS_ADDREF(*aRetval = request);
+
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
+                                                     win,
+                                                     mPrincipal,
+                                                     dsf,
+                                                     request);
+  NS_DispatchToMainThread(r);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(win);
+  NS_ADDREF(*aRetval = request);
+
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
@@ -2176,59 +2273,16 @@ nsDOMDeviceStorage::DispatchMountChangeE
     return;
   }
 
   bool ignore;
   DispatchEvent(ce, &ignore);
 }
 #endif
 
-DOMCI_DATA(DeviceStorageStat, nsDOMDeviceStorageStat)
-
-NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorageStat)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorageStat)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceStorageStat)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_ADDREF(nsDOMDeviceStorageStat)
-NS_IMPL_RELEASE(nsDOMDeviceStorageStat)
-
-nsDOMDeviceStorageStat::nsDOMDeviceStorageStat(uint64_t aFreeBytes, uint64_t aTotalBytes, nsAString& aState)
-  : mFreeBytes(aFreeBytes)
-  , mTotalBytes(aTotalBytes)
-  , mState(aState)
-{
-}
-
-nsDOMDeviceStorageStat::~nsDOMDeviceStorageStat()
-{
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageStat::GetTotalBytes(uint64_t *aTotalBytes)
-{
-  *aTotalBytes = mTotalBytes;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageStat::GetFreeBytes(uint64_t *aFreeBytes)
-{
-  *aFreeBytes = mFreeBytes;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageStat::GetState(nsAString& aState)
-{
-  aState.Assign(mState);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
 {
   if (!strcmp(aTopic, "file-watcher-update")) {
 
     DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
     Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
     return NS_OK;
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -10,17 +10,16 @@ class nsPIDOMWindow;
 
 #include "DOMRequest.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIClassInfo.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIDOMDeviceStorageCursor.h"
-#include "nsIDOMDeviceStorageStat.h"
 #include "nsIDOMWindow.h"
 #include "nsIURI.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 #include "nsWeakPtr.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
@@ -40,17 +39,19 @@ class nsPIDOMWindow;
 #define POST_ERROR_EVENT_UNKNOWN                     "Unknown"
 
 enum DeviceStorageRequestType {
     DEVICE_STORAGE_REQUEST_READ,
     DEVICE_STORAGE_REQUEST_WRITE,
     DEVICE_STORAGE_REQUEST_CREATE,
     DEVICE_STORAGE_REQUEST_DELETE,
     DEVICE_STORAGE_REQUEST_WATCH,
-    DEVICE_STORAGE_REQUEST_STAT
+    DEVICE_STORAGE_REQUEST_FREE_SPACE,
+    DEVICE_STORAGE_REQUEST_USED_SPACE,
+    DEVICE_STORAGE_REQUEST_AVAILABLE
 };
 
 class DeviceStorageTypeChecker MOZ_FINAL
 {
 public:
   static DeviceStorageTypeChecker* CreateOrGet();
 
   DeviceStorageTypeChecker();
@@ -117,31 +118,16 @@ public:
 
 private:
   ~nsDOMDeviceStorageCursor();
 
   nsRefPtr<DeviceStorageFile> mFile;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
-class nsDOMDeviceStorageStat MOZ_FINAL
-  : public nsIDOMDeviceStorageStat
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMDEVICESTORAGESTAT
-
-  nsDOMDeviceStorageStat(uint64_t aFreeBytes, uint64_t aTotalBytes, nsAString& aState);
-
-private:
-  ~nsDOMDeviceStorageStat();
-  uint64_t mFreeBytes, mTotalBytes;
-  nsString mState;
-};
-
 //helpers
 jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString);
 jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile);
 jsval InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID);
 
 #ifdef MOZ_WIDGET_GONK
 nsresult GetSDCardStatus(nsAString& aState);
 #endif
--- a/dom/devicestorage/test/Makefile.in
+++ b/dom/devicestorage/test/Makefile.in
@@ -22,17 +22,19 @@ MOCHITEST_FILES	= \
 		test_addCorrectType.html \
 		test_basic.html \
 		test_enumerate.html \
 		test_enumerateMultipleContinue.html \
 		test_overwrite.html \
 		test_dotdot.html \
 		test_enumerateOptions.html \
 		test_lastModificationFilter.html \
-		test_stat.html \
+		test_freeSpace.html \
+		test_usedSpace.html \
+		test_available.html \
 		test_watch.html \
 		test_watchOther.html \
 		test_823965.html \
 		devicestorage_common.js \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES =\
 		test_app_permissions.html \
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_available.html
@@ -0,0 +1,48 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html> <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=834595
+-->
+<head>
+  <title>Test for the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=834595">Mozilla Bug 834595</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+devicestorage_setup();
+
+function availableSuccess(e) {
+  isnot(e.target.result, null, "result should not be null");
+  devicestorage_cleanup();
+}
+
+function availableError(e) {
+  ok(false, "availableError was called");
+  devicestorage_cleanup();
+}
+
+var storage = navigator.getDeviceStorage("pictures");
+
+request = storage.available();
+ok(request, "Should have a non-null request");
+
+request.onsuccess = availableSuccess;
+request.onerror = availableError;
+
+</script>
+</pre>
+</body>
+</html>
rename from dom/devicestorage/test/test_stat.html
rename to dom/devicestorage/test/test_freeSpace.html
--- a/dom/devicestorage/test/test_stat.html
+++ b/dom/devicestorage/test/test_freeSpace.html
@@ -12,48 +12,46 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="devicestorage_common.js"></script>
 
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 devicestorage_setup();
 
-function statSuccess(e) {
-  ok(e.target.result.freeBytes  > 0, "free bytes should exist and be greater than zero");
-  ok(e.target.result.totalBytes > 0, "total bytes should exist and be greater than zero");
+function freeSpaceSuccess(e) {
+  ok(e.target.result  > 0, "free bytes should exist and be greater than zero");
   devicestorage_cleanup();
 }
 
-function statError(e) {
-  ok(false, "statError was called");
+function freeSpaceError(e) {
+  ok(false, "freeSpaceError was called");
   devicestorage_cleanup();
 }
 
 var storage = navigator.getDeviceStorage("pictures");
-ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
-  request = storage.stat();
+  request = storage.freeSpace();
   ok(request, "Should have a non-null request");
 
-  request.onsuccess = statSuccess;
-  request.onerror = statError;
+  request.onsuccess = freeSpaceSuccess;
+  request.onerror = freeSpaceError;
 }
 
 request = storage.addNamed(createRandomBlob('image/png'), "a/b.png");
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
copy from dom/devicestorage/test/test_stat.html
copy to dom/devicestorage/test/test_usedSpace.html
--- a/dom/devicestorage/test/test_stat.html
+++ b/dom/devicestorage/test/test_usedSpace.html
@@ -12,48 +12,46 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="devicestorage_common.js"></script>
 
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 devicestorage_setup();
 
-function statSuccess(e) {
-  ok(e.target.result.freeBytes  > 0, "free bytes should exist and be greater than zero");
-  ok(e.target.result.totalBytes > 0, "total bytes should exist and be greater than zero");
+function usedSpaceSuccess(e) {
+  ok(e.target.result > 0, "total bytes should exist and be greater than zero");
   devicestorage_cleanup();
 }
 
-function statError(e) {
-  ok(false, "statError was called");
+function usedSpaceError(e) {
+  ok(false, "usedSpaceError was called");
   devicestorage_cleanup();
 }
 
 var storage = navigator.getDeviceStorage("pictures");
-ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
-  request = storage.stat();
+  request = storage.usedSpace();
   ok(request, "Should have a non-null request");
 
-  request.onsuccess = statSuccess;
-  request.onerror = statError;
+  request.onsuccess = usedSpaceSuccess;
+  request.onerror = usedSpaceError;
 }
 
 request = storage.addNamed(createRandomBlob('image/png'), "a/b.png");
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
--- a/dom/icc/interfaces/SimToolKit.idl
+++ b/dom/icc/interfaces/SimToolKit.idl
@@ -586,8 +586,20 @@ dictionary MozStkLocalInfo
 
   /**
    * Language Information
    *
    * @see ISO 639-1, Alpha-2 code
    */
   DOMString language;
 };
+
+dictionary MozStkGeneralEvent
+{
+  /**
+   * The type of this event, MozStkGeneralEvent can be used for all Stk Event
+   * requires no more parameter than event type, including
+   * nsIDOMMozIccManager.STK_EVENT_TYPE_USER_ACTIVITY
+   * nsIDOMMozIccManager.STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE
+   * HCI Connectivity Event(Not defined in interface yet)
+   */
+  unsigned short eventType;
+};
--- a/dom/icc/src/IccManager.cpp
+++ b/dom/icc/src/IccManager.cpp
@@ -30,17 +30,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IccManager,
                                                 nsDOMEventTargetHelper)
   tmp->mProvider = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IccManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozIccManager)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozIccManager)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozIccManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(IccManager, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(IccManager, nsDOMEventTargetHelper)
 
 IccManager::IccManager()
 {
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -588,37 +588,26 @@ IDBTransaction::Abort(nsresult aErrorCod
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   return AbortInternal(aErrorCode, DOMError::CreateForNSResult(aErrorCode));
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
                                                   IDBWrapperCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError);
-
-  for (uint32_t i = 0; i < tmp->mCreatedObjectStores.Length(); i++) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedObjectStores[i]");
-    cb.NoteXPCOMChild(static_cast<nsIIDBObjectStore*>(
-                      tmp->mCreatedObjectStores[i].get()));
-  }
-  for (uint32_t i = 0; i < tmp->mDeletedObjectStores.Length(); i++) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDeletedObjectStores[i]");
-    cb.NoteXPCOMChild(static_cast<nsIIDBObjectStore*>(
-                      tmp->mDeletedObjectStores[i].get()));
-  }
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCreatedObjectStores)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache)
   // Don't unlink mDatabase!
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mError);
-
-  tmp->mCreatedObjectStores.Clear();
-  tmp->mDeletedObjectStores.Clear();
-
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCreatedObjectStores)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction)
   NS_INTERFACE_MAP_ENTRY(nsIIDBTransaction)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBTransaction)
 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
 
--- a/dom/interfaces/devicestorage/Makefile.in
+++ b/dom/interfaces/devicestorage/Makefile.in
@@ -14,17 +14,16 @@ LIBRARY_NAME     = domdevicestorage_s
 XPIDL_MODULE     = dom_devicestorage
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
 	nsIDOMDeviceStorage.idl \
-	nsIDOMDeviceStorageStat.idl \
 	nsIDOMDeviceStorageCursor.idl \
 	nsIDOMNavigatorDeviceStorage.idl \
 	nsIDOMDeviceStorageChangeEvent.idl
 
 include $(topsrcdir)/config/rules.mk
 
 
 XPIDL_FLAGS += \
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -11,17 +11,17 @@ interface nsIDOMDeviceStorageChangeEvent
 interface nsIDOMEventListener;
 interface nsIFile;
 
 dictionary DeviceStorageEnumerationParameters
 {
   jsval since;
 };
 
-[scriptable, uuid(7f69936f-2948-4733-ba41-c7e1d657a88b), builtinclass]
+[scriptable, uuid(c611b701-ddfc-456d-893a-3b3fcb30d9fd), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     [implicit_jscontext] attribute jsval onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     [implicit_jscontext]
     nsIDOMDOMRequest get(in jsval aName);
@@ -33,12 +33,16 @@ interface nsIDOMDeviceStorage : nsIDOMEv
     nsIDOMDOMRequest delete(in jsval aName);
 
     [optional_argc, implicit_jscontext]
     nsIDOMDeviceStorageCursor enumerate([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options);
 
     [optional_argc, implicit_jscontext]
     nsIDOMDeviceStorageCursor enumerateEditable([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options);
 
-    nsIDOMDOMRequest stat();
+    nsIDOMDOMRequest freeSpace();
+
+    nsIDOMDOMRequest usedSpace();
+
+    nsIDOMDOMRequest available();
 
     [noscript] readonly attribute nsIFile rootDirectory;
 };
deleted file mode 100644
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorageStat.idl
+++ /dev/null
@@ -1,15 +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 "domstubs.idl"
-
-[scriptable, uuid(0e22289a-469d-42c0-98dd-ae9831bd6a6d)]
-interface nsIDOMDeviceStorageStat : nsISupports
-{
-  readonly attribute uint64_t totalBytes;
-  readonly attribute uint64_t freeBytes;
-
-  // "shared", "available", "unavailable"
-  readonly attribute DOMString state;
-};
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -999,32 +999,17 @@ ContentChild::RecvNotifyVisited(const UR
 }
 
 bool
 ContentChild::RecvAsyncMessage(const nsString& aMsg,
                                      const ClonedMessageData& aData)
 {
   nsRefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::sChildProcessManager;
   if (cpm) {
-    const SerializedStructuredCloneBuffer& buffer = aData.data();
-    const InfallibleTArray<PBlobChild*>& blobChildList = aData.blobsChild();
-    StructuredCloneData cloneData;
-    cloneData.mData = buffer.data;
-    cloneData.mDataLength = buffer.dataLength;
-    if (!blobChildList.IsEmpty()) {
-      uint32_t length = blobChildList.Length();
-      cloneData.mClosure.mBlobs.SetCapacity(length);
-      for (uint32_t i = 0; i < length; ++i) {
-        BlobChild* blobChild = static_cast<BlobChild*>(blobChildList[i]);
-        MOZ_ASSERT(blobChild);
-        nsCOMPtr<nsIDOMBlob> blob = blobChild->GetBlob();
-        MOZ_ASSERT(blob);
-        cloneData.mClosure.mBlobs.AppendElement(blob);
-      }
-    }
+    StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForChild(aData);
     cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()),
                         aMsg, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 bool
 ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1225,16 +1225,28 @@ ContentParent::RecvBroadcastVolume(const
     }
     return true;
 #else
     NS_WARNING("ContentParent::RecvBroadcastVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
     return false;
 #endif
 }
 
+bool
+ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus)
+{
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+        obs->NotifyObservers(nullptr, "recording-device-events", aRecordingStatus.get());
+    } else {
+        NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
+    }
+    return true;
+}
+
 NS_IMPL_THREADSAFE_ISUPPORTS3(ContentParent,
                               nsIObserver,
                               nsIThreadObserver,
                               nsIDOMGeoPositionCallback)
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
@@ -2073,61 +2085,30 @@ ContentParent::RecvShowAlertNotification
 
 bool
 ContentParent::RecvSyncMessage(const nsString& aMsg,
                                const ClonedMessageData& aData,
                                InfallibleTArray<nsString>* aRetvals)
 {
   nsRefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
-    const SerializedStructuredCloneBuffer& buffer = aData.data();
-    const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
-    StructuredCloneData cloneData;
-    cloneData.mData = buffer.data;
-    cloneData.mDataLength = buffer.dataLength;
-    if (!blobParents.IsEmpty()) {
-      uint32_t length = blobParents.Length();
-      cloneData.mClosure.mBlobs.SetCapacity(length);
-      for (uint32_t index = 0; index < length; index++) {
-        BlobParent* blobParent = static_cast<BlobParent*>(blobParents[index]);
-        MOZ_ASSERT(blobParent);
-        nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
-        MOZ_ASSERT(blob);
-        cloneData.mClosure.mBlobs.AppendElement(blob);
-  }
-    }
+    StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
                         aMsg, true, &cloneData, nullptr, aRetvals);
   }
   return true;
 }
 
 bool
 ContentParent::RecvAsyncMessage(const nsString& aMsg,
                                       const ClonedMessageData& aData)
 {
   nsRefPtr<nsFrameMessageManager> ppm = mMessageManager;
   if (ppm) {
-    const SerializedStructuredCloneBuffer& buffer = aData.data();
-    const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
-    StructuredCloneData cloneData;
-    cloneData.mData = buffer.data;
-    cloneData.mDataLength = buffer.dataLength;
-    if (!blobParents.IsEmpty()) {
-      uint32_t length = blobParents.Length();
-      cloneData.mClosure.mBlobs.SetCapacity(length);
-      for (uint32_t index = 0; index < length; index++) {
-        BlobParent* blobParent = static_cast<BlobParent*>(blobParents[index]);
-        MOZ_ASSERT(blobParent);
-        nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
-        MOZ_ASSERT(blob);
-        cloneData.mClosure.mBlobs.AppendElement(blob);
-      }
-    }
-
+    StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
                         aMsg, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 bool
 ContentParent::RecvFilePathUpdateNotify(const nsString& aType, const nsString& aFilePath, const nsCString& aReason)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -339,16 +339,18 @@ private:
     virtual bool RecvAudioChannelRegisterType(const AudioChannelType& aType);
     virtual bool RecvAudioChannelUnregisterType(const AudioChannelType& aType,
                                                 const bool& aElementHidden);
 
     virtual bool RecvAudioChannelChangedNotification();
 
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
 
+    virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus);
+
     virtual void ProcessingError(Result what) MOZ_OVERRIDE;
 
     GeckoChildProcessHost* mSubprocess;
     ChildOSPrivileges mOSPrivileges;
 
     uint64_t mChildID;
     int32_t mGeolocationWatchID;
     int mRunToCompletionDepth;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -70,22 +70,33 @@ struct FontListEntry {
     nsString  faceName;
     nsCString filepath;
     uint16_t  weight;
     int16_t   stretch;
     uint8_t   italic;
     uint8_t   index;
 };
 
-struct DeviceStorageStatParams
+struct DeviceStorageFreeSpaceParams
 {
   nsString type;
   nsString fullpath;
 };
 
+struct DeviceStorageUsedSpaceParams
+{
+  nsString type;
+  nsString fullpath;
+};
+
+struct DeviceStorageAvailableParams
+{
+  nsString type;
+};
+
 struct DeviceStorageAddParams
 {
   nsString type;
   PBlob blob;
   nsString name;
   nsString fullpath;
 };
 
@@ -110,17 +121,19 @@ struct DeviceStorageEnumerationParams
 };
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
   DeviceStorageEnumerationParams;
-  DeviceStorageStatParams;
+  DeviceStorageFreeSpaceParams;
+  DeviceStorageUsedSpaceParams;
+  DeviceStorageAvailableParams;
 };
 
 // An IPCTabContext which corresponds to a PBrowser opened by a child when it
 // receives window.open().
 //
 // If isBrowserElement is false, this PopupIPCTabContext corresponds to an app
 // frame, and the frame's app-id and app-frame-owner-app-id will be equal to the
 // opener's values.
@@ -432,14 +445,16 @@ parent:
     async AudioChannelChangedNotification();
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aFilepath,
                                nsCString aReason);
     // get nsIVolumeService to broadcast volume information
     async BroadcastVolume(nsString volumeName);
 
+    async RecordingDeviceEvents(nsString recordingStatus);
+
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData);
 };
 
 }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1373,16 +1373,17 @@ TabChild::DispatchSynthesizedMouseEvent(
   MOZ_ASSERT(aMsg == NS_MOUSE_MOVE || aMsg == NS_MOUSE_BUTTON_DOWN ||
              aMsg == NS_MOUSE_BUTTON_UP);
 
   nsMouseEvent event(true, aMsg, NULL,
       nsMouseEvent::eReal, nsMouseEvent::eNormal);
   event.refPoint = aRefPoint;
   event.time = aTime;
   event.button = nsMouseEvent::eLeftButton;
+  event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   if (aMsg != NS_MOUSE_MOVE) {
     event.clickCount = 1;
   }
 
   DispatchWidgetEvent(event);
 }
 
 static nsDOMTouch*
@@ -1714,38 +1715,17 @@ TabChild::RecvLoadRemoteScript(const nsS
 }
 
 bool
 TabChild::RecvAsyncMessage(const nsString& aMessage,
                            const ClonedMessageData& aData)
 {
   if (mTabChildGlobal) {
     nsFrameScriptCx cx(static_cast<nsIWebBrowserChrome*>(this), this);
-
-    const SerializedStructuredCloneBuffer& buffer = aData.data();
-    const InfallibleTArray<PBlobChild*>& blobChildList = aData.blobsChild();
-
-    StructuredCloneData cloneData;
-    cloneData.mData = buffer.data;
-    cloneData.mDataLength = buffer.dataLength;
-
-    if (!blobChildList.IsEmpty()) {
-      uint32_t length = blobChildList.Length();
-      cloneData.mClosure.mBlobs.SetCapacity(length);
-      for (uint32_t i = 0; i < length; ++i) {
-        BlobChild* blobChild = static_cast<BlobChild*>(blobChildList[i]);
-        MOZ_ASSERT(blobChild);
-
-        nsCOMPtr<nsIDOMBlob> blob = blobChild->GetBlob();
-        MOZ_ASSERT(blob);
-
-        cloneData.mClosure.mBlobs.AppendElement(blob);
-      }
-    }
-
+    StructuredCloneData cloneData = UnpackClonedMessageDataForChild(aData);
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
     mm->ReceiveMessage(static_cast<nsIDOMEventTarget*>(mTabChildGlobal),
                        aMessage, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -504,57 +504,25 @@ TabParent::TryCapture(const nsGUIEvent& 
   return true;
 }
 
 bool
 TabParent::RecvSyncMessage(const nsString& aMessage,
                            const ClonedMessageData& aData,
                            InfallibleTArray<nsString>* aJSONRetVal)
 {
-  const SerializedStructuredCloneBuffer& buffer = aData.data();
-  const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
-  StructuredCloneData cloneData;
-  cloneData.mData = buffer.data;
-  cloneData.mDataLength = buffer.dataLength;
-  if (!blobParents.IsEmpty()) {
-    uint32_t length = blobParents.Length();
-    cloneData.mClosure.mBlobs.SetCapacity(length);
-    for (uint32_t i = 0; i < length; ++i) {
-      BlobParent* blobParent = static_cast<BlobParent*>(blobParents[i]);
-      MOZ_ASSERT(blobParent);
-      nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
-      MOZ_ASSERT(blob);
-      cloneData.mClosure.mBlobs.AppendElement(blob);
-    }
-  }
+  StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
   return ReceiveMessage(aMessage, true, &cloneData, aJSONRetVal);
 }
 
 bool
 TabParent::RecvAsyncMessage(const nsString& aMessage,
                                   const ClonedMessageData& aData)
 {
-    const SerializedStructuredCloneBuffer& buffer = aData.data();
-  const InfallibleTArray<PBlobParent*>& blobParents = aData.blobsParent();
-
-    StructuredCloneData cloneData;
-    cloneData.mData = buffer.data;
-    cloneData.mDataLength = buffer.dataLength;
-
-  if (!blobParents.IsEmpty()) {
-    uint32_t length = blobParents.Length();
-      cloneData.mClosure.mBlobs.SetCapacity(length);
-    for (uint32_t i = 0; i < length; ++i) {
-      BlobParent* blobParent = static_cast<BlobParent*>(blobParents[i]);
-      MOZ_ASSERT(blobParent);
-      nsCOMPtr<nsIDOMBlob> blob = blobParent->GetBlob();
-        MOZ_ASSERT(blob);
-        cloneData.mClosure.mBlobs.AppendElement(blob);
-      }
-    }
+  StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
   return ReceiveMessage(aMessage, false, &cloneData, nullptr);
 }
 
 bool
 TabParent::RecvSetCursor(const uint32_t& aCursor)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
--- a/dom/media/Makefile.in
+++ b/dom/media/Makefile.in
@@ -51,9 +51,10 @@ LOCAL_INCLUDES += \
 DIRS += bridge
 endif
 
 TEST_DIRS += \
   tests/mochitest \
   $(NULL)
 
 include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -24,16 +24,20 @@
 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
 #include "MediaEngineDefault.h"
 #if defined(MOZ_WEBRTC)
 #include "MediaEngineWebRTC.h"
 #endif
 
 namespace mozilla {
 
+#ifdef LOG
+#undef LOG
+#endif
+
 #ifdef PR_LOGGING
 PRLogModuleInfo*
 GetMediaManagerLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
     sLog = PR_NewLogModule("MediaManager");
   return sLog;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -1,24 +1,27 @@
 /* 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/dom/ContentChild.h"
 #include "mozilla/Services.h"
+#include "mozilla/unused.h"
 #include "nsIMediaManager.h"
 
 #include "nsHashKeys.h"
 #include "nsGlobalWindow.h"
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsObserverService.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
+#include "nsXULAppAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
 #include "prlog.h"
 
 namespace mozilla {
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaManagerLog();
@@ -44,20 +47,30 @@ class GetUserMediaNotificationEvent: pub
       if (!obs) {
         NS_WARNING("Could not get the Observer service for GetUserMedia recording notification.");
         return NS_ERROR_FAILURE;
       }
       if (mStatus) {
         obs->NotifyObservers(nullptr,
             "recording-device-events",
             NS_LITERAL_STRING("starting").get());
+        // Forward recording events to parent process.
+        // The events are gathered in chrome process and used for recording indicator
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          unused << mozilla::dom::ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("starting"));
+        }
       } else {
         obs->NotifyObservers(nullptr,
             "recording-device-events",
             NS_LITERAL_STRING("shutdown").get());
+        // Forward recording events to parent process.
+        // The events are gathered in chrome process and used for recording indicator
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          unused << mozilla::dom::ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("shutdown"));
+        }
       }
       return NS_OK;
     }
 
   protected:
     GetUserMediaStatus mStatus;
 };
 
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -430,44 +430,65 @@ PeerConnection.prototype = {
 
     this._queueOrRun({
       func: this._pc.createOffer,
       args: [constraints],
       wait: true
     });
   },
 
-  createAnswer: function(onSuccess, onError, constraints, provisional) {
+  _createAnswer: function(onSuccess, onError, constraints, provisional) {
+    this._onCreateAnswerSuccess = onSuccess;
+    this._onCreateAnswerFailure = onError;
+
     if (!this.remoteDescription) {
-      throw new Error("setRemoteDescription not called");
+      this._observer.onCreateAnswerError(3); // PC_INVALID_REMOTE_SDP
+      /*
+        This needs to be matched to spec -- see bug 834270. The final
+        code will be of the form:
+
+      this._observer.onCreateAnswerError(ci.IPeerConnection.kInvalidState,
+                                         "setRemoteDescription not called");
+      */
+      return;
     }
 
     if (this.remoteDescription.type != "offer") {
-      throw new Error("No outstanding offer");
+      this._observer.onCreateAnswerError(3); // PC_INVALID_REMOTE_SDP
+      /*
+        This needs to be matched to spec -- see bug 834270. The final
+        code will be of the form:
+
+      this._observer.onCreateAnswerError(ci.IPeerConnection.kInvalidState,
+                                         "No outstanding offer");
+      */
+      return;
     }
 
+    // TODO: Implement provisional answer.
+
+    this._pc.createAnswer(constraints);
+  },
+
+  createAnswer: function(onSuccess, onError, constraints, provisional) {
     if (!constraints) {
       constraints = {};
     }
 
     if (!this._validateConstraints(constraints)) {
       throw new Error("createAnswer passed invalid constraints");
     }
 
-    this._onCreateAnswerSuccess = onSuccess;
-    this._onCreateAnswerFailure = onError;
-
     if (!provisional) {
       provisional = false;
     }
 
-    // TODO: Implement provisional answer.
     this._queueOrRun({
-      func: this._pc.createAnswer,
-      args: [constraints],
+      func: this._createAnswer,
+      args: [onSuccess, onError, constraints, provisional],
       wait: true
     });
   },
 
   setLocalDescription: function(desc, onSuccess, onError) {
     // TODO -- if we have two setLocalDescriptions in the
     // queue,this code overwrites the callbacks for the first
     // one with the callbacks for the second one. See Bug 831759.
@@ -765,23 +786,21 @@ PeerConnectionObserver.prototype = {
         iceGatherCb("gathering");
         break;
       case Ci.IPeerConnection.kIceWaiting:
         iceCb("starting");
         this._dompc._executeNext();
         break;
       case Ci.IPeerConnection.kIceChecking:
         iceCb("checking");
-        this._dompc._executeNext();
         break;
       case Ci.IPeerConnection.kIceConnected:
         // ICE gathering complete.
         iceCb("connected");
         iceGatherCb("complete");
-        this._dompc._executeNext();
         break;
       case Ci.IPeerConnection.kIceFailed:
         iceCb("failed");
         break;
       default:
         // Unknown state!
         break;
     }
@@ -791,66 +810,60 @@ PeerConnectionObserver.prototype = {
     if (this._dompc.onaddstream) {
       try {
         this._dompc.onaddstream.onCallback({
           stream: stream, type: type,
           __exposedProps__: { stream: "r", type: "r" }
         });
       } catch(e) {}
     }
-    this._dompc._executeNext();
   },
 
   onRemoveStream: function(stream, type) {
     if (this._dompc.onremovestream) {
       try {
         this._dompc.onremovestream.onCallback({
           stream: stream, type: type,
           __exposedProps__: { stream: "r", type: "r" }
         });
       } catch(e) {}
     }
-    this._dompc._executeNext();
   },
 
   foundIceCandidate: function(cand) {
     if (this._dompc.onicecandidate) {
       try {
         this._dompc.onicecandidate.onCallback({
           candidate: cand,
           __exposedProps__: { candidate: "rw" }
         });
       } catch(e) {}
     }
-    this._dompc._executeNext();
   },
 
   notifyDataChannel: function(channel) {
     if (this._dompc.ondatachannel) {
       try {
         this._dompc.ondatachannel.onCallback(channel);
       } catch(e) {}
     }
-    this._dompc._executeNext();
   },
 
   notifyConnection: function() {
     if (this._dompc.onconnection) {
       try {
         this._dompc.onconnection.onCallback();
       } catch(e) {}
     }
-    this._dompc._executeNext();
   },
 
   notifyClosedConnection: function() {
     if (this._dompc.onclosedconnection) {
       try {
         this._dompc.onclosedconnection.onCallback();
       } catch(e) {}
     }
-    this._dompc._executeNext();
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList, IceCandidate, SessionDescription, PeerConnection]
 );
--- a/dom/media/nsIDOMMediaStream.idl
+++ b/dom/media/nsIDOMMediaStream.idl
@@ -1,15 +1,22 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
- 
+
+// undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
+%{C++
+#ifdef GetCurrentTime
+#undef GetCurrentTime
+#endif
+%}
+
 [scriptable, builtinclass, uuid(f37c2871-4cb7-4672-bb28-c2d601f7cc9e)]
 interface nsIDOMMediaStream : nsISupports
 {
   readonly attribute double currentTime;
 };
 
 [scriptable, builtinclass, uuid(210a16e3-2a38-4ae9-b0f6-0fb5a8252814)]
 interface nsIDOMLocalMediaStream : nsIDOMMediaStream
--- a/dom/media/tests/mochitest/Makefile.in
+++ b/dom/media/tests/mochitest/Makefile.in
@@ -16,16 +16,17 @@ MOCHITEST_FILES = \
   test_getUserMedia_basicVideo.html \
   test_getUserMedia_basicVideoAudio.html \
   test_peerConnection_basicAudio.html \
   test_peerConnection_basicAudioVideo.html \
   test_peerConnection_basicAudioVideoCombined.html \
   test_peerConnection_basicVideo.html \
   test_peerConnection_bug827843.html \
   test_peerConnection_bug825703.html \
+  test_peerConnection_bug834153.html \
   head.js \
   mediaStreamPlayback.js \
   pc.js \
   $(NULL)
 
 # The following tests are leaking and cannot be run by default yet
 ifdef MOZ_WEBRTC_LEAKING_TESTS
 MOCHITEST_FILES += \
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
@@ -24,44 +24,40 @@ https://bugzilla.mozilla.org/show_bug.cg
   var audioLocal;
   var audioPCLocal;
   var audioPCRemote;
 
   var pcLocal;
   var pcRemote;
 
   var test_data = {
-    pcLocal: { audio: [], video: []},
-    pcRemote: { audio: [], video: []}
+    pcLocal: [],
+    pcRemote: []
   };
 
   runTest(function () {
     audioLocal = document.getElementById("audioLocal");
     audioPCLocal = document.getElementById("audioPCLocal");
     audioPCRemote = document.getElementById("audioPCRemote");
 
     pcLocal = new mozRTCPeerConnection();
     pcRemote = new mozRTCPeerConnection();
 
     pcLocal.onaddstream = function (aObj) {
-      test_data.pcLocal[aObj.type].push(aObj.stream);
+      test_data.pcLocal.push(aObj.stream);
 
-      if (aObj.type === "audio") {
-        audioPCRemote.mozSrcObject = aObj.stream;
-        audioPCRemote.play();
-      }
+      audioPCRemote.mozSrcObject = aObj.stream;
+      audioPCRemote.play();
     };
 
     pcRemote.onaddstream = function (aObj) {
-      test_data.pcRemote[aObj.type].push(aObj.stream);
+      test_data.pcRemote.push(aObj.stream);
 
-      if (aObj.type === "audio") {
-        audioPCLocal.mozSrcObject = aObj.stream;
-        audioPCLocal.play();
-      }
+      audioPCLocal.mozSrcObject = aObj.stream;
+      audioPCLocal.play();
     };
 
     navigator.mozGetUserMedia({audio: true, fake: true}, function onSuccess(aLocalInputStream) {
       pcLocal.addStream(aLocalInputStream);
 
       navigator.mozGetUserMedia({audio: true, fake: true}, function onSuccess(aRemoteInputStream) {
         pcRemote.addStream(aRemoteInputStream);
 
@@ -69,28 +65,22 @@ https://bugzilla.mozilla.org/show_bug.cg
         audioLocal.play();
 
         PeerConnection.handShake(pcLocal, pcRemote, function () {
           is(pcLocal.localStreams.length, 1,
              "A single local stream has been attached to the local peer");
           is(pcRemote.localStreams.length, 1,
              "A single local stream has been attached to the remote peer");
 
-          is(test_data.pcLocal.audio.length, 1,
-             "A remote audio stream has been attached to the local peer");
-          is(test_data.pcLocal.video.length, 0,
-             "A temporary remote video stream has been attached to the local peer");
-          is(test_data.pcRemote.audio.length, 1,
-             "A remote audio stream has been attached to the remote peer");
-          is(test_data.pcRemote.video.length, 0,
-             "A temporary remote video stream has been attached to the remote peer");
+          // TODO: check that the streams are of the expected types.
+	  // Bug 834837.
 
-          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal.audio[0]) !== -1,
+          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal[0]) !== -1,
              "Remote audio stream for local peer is accessible");
-          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote.audio[0]) !== -1,
+          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote[0]) !== -1,
              "Remote audio stream for remote peer is accessible");
 
           info("For now simply disconnect. We will add checks for media in a follow-up bug");
           disconnect();
         });
       }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
   }, true);
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
@@ -10,18 +10,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=796890">Basic audio-video peer connection</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  <audio id="audioPCLocal" controls></audio>
-  <audio id="audioPCRemote" controls></audio>
   <audio id="audioLocal" controls></audio>
 
   <video id="videoPCLocal" width="160" height="120" controls></video>
   <video id="videoPCRemote" width="160" height="120" controls></video>
   <video id="videoLocal" width="160" height="120" controls></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
@@ -32,64 +30,44 @@ https://bugzilla.mozilla.org/show_bug.cg
   var videoLocal;
   var videoPCLocal;
   var videoPCRemote;
 
   var pcLocal;
   var pcRemote;
 
   var test_data = {
-    pcLocal: { audio: [], video: []},
-    pcRemote: { audio: [], video: []}
+    pcLocal: [],
+    pcRemote: []
   };
 
   runTest(function () {
     audioLocal = document.getElementById("audioLocal");
     audioPCLocal = document.getElementById("audioPCLocal");
     audioPCRemote = document.getElementById("audioPCRemote");
 
     videoLocal = document.getElementById("videoLocal");
     videoPCLocal = document.getElementById("videoPCLocal");
     videoPCRemote = document.getElementById("videoPCRemote");
 
     pcLocal = new mozRTCPeerConnection();
     pcRemote = new mozRTCPeerConnection();
 
     pcLocal.onaddstream = function (aObj) {
-      test_data.pcLocal[aObj.type].push(aObj.stream);
+      test_data.pcLocal.push(aObj.stream);
 
-      switch (aObj.type) {
-        case "audio":
-          audioPCRemote.mozSrcObject = aObj.stream;
-          audioPCRemote.play();
-          break;
-        case "video":
-          videoPCRemote.mozSrcObject = aObj.stream;
-          videoPCRemote.play();
-          break;
-        default:
-          ok(false, "Not supported type of MediaStream for local peer: " + aObj.type);
-      }
+      videoPCRemote.mozSrcObject = aObj.stream;
+      videoPCRemote.play();
     };
 
     pcRemote.onaddstream = function (aObj) {
-      test_data.pcRemote[aObj.type].push(aObj.stream);
+      test_data.pcRemote.push(aObj.stream);
 
-      switch (aObj.type) {
-        case "audio":
-          audioPCLocal.mozSrcObject = aObj.stream;
-          audioPCLocal.play();
-          break;
-        case "video":
-          videoPCLocal.mozSrcObject = aObj.stream;
-          videoPCLocal.play();
-          break;
-        default:
-          ok(false, "Not supported type of MediaStream for remote peer: " + aObj.type);
-      }
+      videoPCLocal.mozSrcObject = aObj.stream;
+      videoPCLocal.play();
     };
 
     navigator.mozGetUserMedia({audio: true, fake: true},
                               function onSuccess(aLocalAudioInputStream) {
       pcLocal.addStream(aLocalAudioInputStream);
 
       audioLocal.mozSrcObject = aLocalAudioInputStream;
       audioLocal.play();
@@ -110,33 +88,27 @@ https://bugzilla.mozilla.org/show_bug.cg
             pcRemote.addStream(aRemoteVideoInputStream);
 
             PeerConnection.handShake(pcLocal, pcRemote, function () {
               is(pcLocal.localStreams.length, 2,
                  "Two local streams have been attached to the local peer");
               is(pcRemote.localStreams.length, 2,
                  "Two local local streams have been attached to the remote peer");
 
-              is(test_data.pcLocal.video.length, 1,
-                 "A remote video stream has been attached to the local peer");
-              is(test_data.pcLocal.audio.length, 1,
-                 "A remote audio stream has been attached to the local peer");
-              is(test_data.pcRemote.video.length, 1,
-                 "A remote video stream has been attached to the remote peer");
-              is(test_data.pcRemote.audio.length, 1,
-                 "A remote audio stream has been attached to the remote peer");
+              is(test_data.pcLocal.length, 1,
+                 "A remote stream has been attached to the local peer");
+              is(test_data.pcRemote.length, 1,
+                 "A remote stream has been attached to the remote peer");
 
-              ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal.audio[0]) !== -1,
-                 "Remote audio stream for local peer is accessible");
-              ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal.video[0]) !== -1,
-                 "Remote video stream for local peer is accessible");
-              ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote.audio[0]) !== -1,
-                 "Remote audio stream for remote peer is accessible");
-              ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote.video[0]) !== -1,
-                 "Remote video stream for remote peer is accessible");
+	      // TODO: check that the streams are of the expected types.
+	      // Bug 834837.
+              ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal[0]) !== -1,
+                 "Remote stream for local peer is accessible");
+              ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote[0]) !== -1,
+                 "Remote stream for remote peer is accessible");
 
               info("For now simply disconnect. We will add checks for media in a follow-up bug");
               disconnect();
             });
           }, unexpectedCallbackAndFinish);
         }, unexpectedCallbackAndFinish);
       }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
@@ -10,86 +10,58 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=796890">Basic audio-video peer connection</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  <audio id="audioPCLocal" controls></audio>
-  <audio id="audioPCRemote" controls></audio>
   <audio id="audioLocal" controls></audio>
 
   <video id="videoPCLocal" width="160" height="120" controls></video>
   <video id="videoPCRemote" width="160" height="120" controls></video>
   <video id="videoLocal" width="160" height="120" controls></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
   var audioLocal;
-  var audioPCLocal;
-  var audioPCRemote;
-
   var videoLocal;
   var videoPCLocal;
   var videoPCRemote;
 
   var pcLocal;
   var pcRemote;
 
   var test_data = {
-    pcLocal: { audio: [], video: []},
-    pcRemote: { audio: [], video: []}
+    pcLocal: [],
+    pcRemote: []
   };
 
   runTest(function () {
     audioLocal = document.getElementById("audioLocal");
-    audioPCLocal = document.getElementById("audioPCLocal");
-    audioPCRemote = document.getElementById("audioPCRemote");
-
     videoLocal = document.getElementById("videoLocal");
     videoPCLocal = document.getElementById("videoPCLocal");
     videoPCRemote = document.getElementById("videoPCRemote");
 
     pcLocal = new mozRTCPeerConnection();
     pcRemote = new mozRTCPeerConnection();
 
     pcLocal.onaddstream = function (aObj) {
-      test_data.pcLocal[aObj.type].push(aObj.stream);
+      test_data.pcLocal.push(aObj.stream);
 
-      switch (aObj.type) {
-        case "audio":
-          audioPCRemote.mozSrcObject = aObj.stream;
-          audioPCRemote.play();
-          break;
-        case "video":
-          videoPCRemote.mozSrcObject = aObj.stream;
-          videoPCRemote.play();
-          break;
-        default:
-          ok(false, "Not supported type of MediaStream for local peer: " + aObj.type);
-      }
+      videoPCRemote.mozSrcObject = aObj.stream;
+      videoPCRemote.play();
     };
 
     pcRemote.onaddstream = function (aObj) {
-      test_data.pcRemote[aObj.type].push(aObj.stream);
+      test_data.pcRemote.push(aObj.stream);
 
-      switch (aObj.type) {
-        case "audio":
-          audioPCLocal.mozSrcObject = aObj.stream;
-          audioPCLocal.play();
-          break;
-        case "video":
-          videoPCLocal.mozSrcObject = aObj.stream;
-          videoPCLocal.play();
-          break;
-        default:
-          ok(false, "Not supported type of MediaStream for remote peer: " + aObj.type);
-      }
+      videoPCLocal.mozSrcObject = aObj.stream;
+      videoPCLocal.play();
     };
 
     navigator.mozGetUserMedia({audio: true, video: true, fake: true},
                               function onSuccess(aLocalInputStream) {
       pcLocal.addStream(aLocalInputStream);
 
       navigator.mozGetUserMedia({audio: true, video: true, fake: true},
                                 function onSuccess(aRemoteInputStream) {
@@ -99,37 +71,31 @@ https://bugzilla.mozilla.org/show_bug.cg
         videoLocal.play();
 
         PeerConnection.handShake(pcLocal, pcRemote, function () {
           is(pcLocal.localStreams.length, 1,
              "A single local stream has been attached to the local peer");
           is(pcRemote.localStreams.length, 1,
              "A single local stream has been attached to the remote peer");
 
-          // Bug 816780 - onaddstream fires twice on a peerconnection when you add a stream with both audio and video
-          is(test_data.pcLocal.video.length, 1,
-             "A remote video stream has been attached to the local peer");
-          is(test_data.pcLocal.audio.length, 1,
-             "A remote audio stream has been attached to the local peer");
-          is(test_data.pcRemote.video.length, 1,
-             "A remote video stream has been attached to the remote peer");
-          is(test_data.pcRemote.audio.length, 1,
-             "A remote audio stream has been attached to the remote peer");
+          is(test_data.pcLocal.length, 1,
+             "A remote stream has been attached to the local peer");
+          is(test_data.pcRemote.length, 1,
+             "A remote stream has been attached to the remote peer");
 
-          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal.audio[0]) !== -1,
-             "Remote audio stream for local peer is accessible");
-          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal.video[0]) !== -1,
-             "Remote video stream for local peer is accessible");
-          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote.audio[0]) !== -1,
-             "Remote audio stream for remote peer is accessible");
-          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote.video[0]) !== -1,
-             "Remote video stream for remote peer is accessible");
+	  // TODO: check that the streams are of the expected types.
+	  // Bug 834837.
+
+          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal[0]) !== -1,
+             "Remote stream for local peer is accessible");
+          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote[0]) !== -1,
+             "Remote stream for remote peer is accessible");
 
           info("For now simply disconnect. We will add checks for media in a follow-up bug");
-          disconnect();
+	  disconnect();
         });
       }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
   }, true);
 
   function disconnect() {
     pcLocal.close();
     pcRemote.close();
--- a/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
@@ -24,44 +24,40 @@ https://bugzilla.mozilla.org/show_bug.cg
   var videoLocal;
   var videoPCLocal;
   var videoPCRemote;
 
   var pcLocal;
   var pcRemote;
 
   var test_data = {
-    pcLocal: { audio: [], video: []},
-    pcRemote: { audio: [], video: []}
+    pcLocal: [],
+    pcRemote: []
   };
 
   runTest(function () {
     videoLocal = document.getElementById("videoLocal");
     videoPCLocal = document.getElementById("videoPCLocal");
     videoPCRemote = document.getElementById("videoPCRemote");
 
     pcLocal = new mozRTCPeerConnection();
     pcRemote = new mozRTCPeerConnection();
 
     pcLocal.onaddstream = function (aObj) {
-      test_data.pcLocal[aObj.type].push(aObj.stream);
+      test_data.pcLocal.push(aObj.stream);
 
-      if (aObj.type === "video") {
-        videoPCRemote.mozSrcObject = aObj.stream;
-        videoPCRemote.play();
-      }
+      videoPCRemote.mozSrcObject = aObj.stream;
+      videoPCRemote.play();
     };
 
     pcRemote.onaddstream = function (aObj) {
-      test_data.pcRemote[aObj.type].push(aObj.stream);
+      test_data.pcRemote.push(aObj.stream);
 
-      if (aObj.type === "video") {
-        videoPCLocal.mozSrcObject = aObj.stream;
-        videoPCLocal.play();
-      }
+      videoPCLocal.mozSrcObject = aObj.stream;
+      videoPCLocal.play();
     };
 
     navigator.mozGetUserMedia({video: true, fake: true}, function onSuccess(aLocalInputStream) {
       pcLocal.addStream(aLocalInputStream);
 
       navigator.mozGetUserMedia({video: true, fake: true}, function onSuccess(aRemoteInputStream) {
         pcRemote.addStream(aRemoteInputStream);
 
@@ -69,28 +65,22 @@ https://bugzilla.mozilla.org/show_bug.cg
         videoLocal.play();
 
         PeerConnection.handShake(pcLocal, pcRemote, function () {
           is(pcLocal.localStreams.length, 1,
              "A single local stream has been attached to the local peer");
           is(pcRemote.localStreams.length, 1,
              "A single local stream has been attached to the remote peer");
 
-          is(test_data.pcLocal.video.length, 1,
-             "A remote video stream has been attached to the local peer");
-          is(test_data.pcLocal.audio.length, 0,
-             "A temporary remote audio stream has been attached to the local peer");
-          is(test_data.pcRemote.video.length, 1,
-             "A remote video stream has been attached to the remote peer");
-          is(test_data.pcRemote.audio.length, 0,
-             "A temporary remote audio stream has been attached to the remote peer");
+          // TODO: check that the streams are of the expected types.
+	  // Bug 834837.
 
-          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal.video[0]) !== -1,
+          ok(PeerConnection.findStream(pcLocal.remoteStreams, test_data.pcLocal[0]) !== -1,
              "Remote video stream for local peer is accessible");
-          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote.video[0]) !== -1,
+          ok(PeerConnection.findStream(pcRemote.remoteStreams, test_data.pcRemote[0]) !== -1,
              "Remote video stream for remote peer is accessible");
 
           info("For now simply disconnect. We will add checks for media in a follow-up bug");
           disconnect();
         });
       }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
   }, true);
--- a/dom/media/tests/mochitest/test_peerConnection_bug827843.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug827843.html
@@ -24,44 +24,40 @@ https://bugzilla.mozilla.org/show_bug.cg
   var audioLocal;
   var audioPCLocal;
   var audioPCRemote;
 
   var pcLocal;
   var pcRemote;
 
   var test_data = {
-    pcLocal: { audio: [], video: []},
-    pcRemote: { audio: [], video: []}
+    pcLocal: [],
+    pcRemote: []
   };
 
   runTest(function () {
     audioLocal = document.getElementById("audioLocal");
     audioPCLocal = document.getElementById("audioPCLocal");
     audioPCRemote = document.getElementById("audioPCRemote");
 
     pcLocal = new mozRTCPeerConnection();
     pcRemote = new mozRTCPeerConnection();
 
     pcLocal.onaddstream = function (aObj) {
-      test_data.pcLocal[aObj.type].push(aObj.stream);
+      test_data.pcLocal.push(aObj.stream);
 
-      if (aObj.type === "audio") {
-        audioPCRemote.mozSrcObject = aObj.stream;
-        audioPCRemote.play();
-      }
+      audioPCRemote.mozSrcObject = aObj.stream;
+      audioPCRemote.play();
     };
 
     pcRemote.onaddstream = function (aObj) {
-      test_data.pcRemote[aObj.type].push(aObj.stream);
+      test_data.pcRemote.push(aObj.stream);
 
-      if (aObj.type === "audio") {
-        audioPCLocal.mozSrcObject = aObj.stream;
-        audioPCLocal.play();
-      }
+      audioPCLocal.mozSrcObject = aObj.stream;
+      audioPCLocal.play();
     };
 
     navigator.mozGetUserMedia({audio: true, fake: true}, function onSuccess(aLocalInputStream) {
       pcLocal.addStream(aLocalInputStream);
 
       navigator.mozGetUserMedia({audio: true, fake: true}, function onSuccess(aRemoteInputStream) {
         pcRemote.addStream(aRemoteInputStream);
 
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_bug834153.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=834153
+-->
+<head>
+  <meta charset="utf-8">
+    <title>Bug 834153: Queue CreateAnswer in PeerConnection.js</title>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="application/javascript" src="head.js"></script>
+  </meta>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=834153">Queue CreateAnswer in PeerConnection.js</a>
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+function croak(msg) {
+  ok(0,msg);
+  SimpleTest.finish();
+}
+
+runTest(function () {
+  var pc1;
+
+  pc1 = new mozRTCPeerConnection();
+  ok(pc1, "pc1 is non-null");
+
+  pc1.createOffer(
+    function(d) {
+      pc2 = new mozRTCPeerConnection();
+      ok(pc2, "pc2 is non-null");
+
+      // The whole point of this test is not to wait for the
+      // setRemoteDescription call to succesfully complete, so we
+      // don't do anything in its callbacks.
+      pc2.setRemoteDescription(d,function(x){},function(x){});
+      pc2.createAnswer(
+        function (d) {
+          is(d.type,"answer","CreateAnswer created an answer");
+          SimpleTest.finish();
+        },
+        function(err){croak("createAnswer failed: " + err);}
+      );
+    },
+    function(err){croak("createOffer failed: " + err);}
+  );
+}, true);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/network/interfaces/Makefile.in
+++ b/dom/network/interfaces/Makefile.in
@@ -25,16 +25,17 @@ XPIDLSRCS = \
 
 ifdef MOZ_B2G_RIL
 XPIDLSRCS += \
   nsIDOMMobileConnection.idl \
   nsIMobileConnectionProvider.idl \
   nsINavigatorMobileConnection.idl \
   nsIDOMNetworkStatsManager.idl \
   nsIDOMNetworkStats.idl \
+  nsIDOMCFStateChangeEvent.idl \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 XPIDL_FLAGS += \
   -I$(topsrcdir)/dom/interfaces/base \
   -I$(topsrcdir)/dom/interfaces/events \
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsIDOMCFStateChangeEvent.idl
@@ -0,0 +1,54 @@
+/* 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 "nsIDOMEvent.idl"
+
+[scriptable, builtinclass, uuid(9342c4eb-b6b3-414c-892f-c3630ce35c40)]
+interface nsIDOMCFStateChangeEvent : nsIDOMEvent
+{
+  /**
+   * Indicates about errors while setting up the Call forwarding rule.
+   */
+  readonly attribute bool success;
+
+  /**
+   * Indicates what to do with the rule.
+   *
+   * One of the CALL_FORWARD_ACTION_* constants. It will be either disable (0),
+   * enable (1), query status (2), registration (3), or erasure (4).
+   *
+   * @see 3GPP nsIDOMMozMobileCFInfo.CALL_FORWARD_ACTION_* values.
+   * @see 3GPP TS 27.007 7.11 "mode".
+   */
+  readonly attribute unsigned short action;
+
+  /**
+   * Indicates the reason the call is being forwarded.
+   *
+   * One of the CALL_FORWARD_REASON_* constants. It will be either
+   * unconditional (0), mobile busy (1), no reply (2), not reachable (3),
+   * all call forwarding (4), or all conditional call forwarding (5).
+   *
+   * @see 3GPP nsIDOMMozMobileCFInfo.CALL_FORWARD_REASON_* values.
+   * @see 3GPP TS 27.007 7.11 "reason".
+   */
+  readonly attribute unsigned short reason;
+
+  /**
+   * Phone number of forwarding address.
+   */
+  readonly attribute DOMString number;
+
+  /**
+   * When "no reply" is enabled or queried, this gives the time in
+   * seconds to wait before call is forwarded.
+   */
+  readonly attribute unsigned short timeSeconds;
+
+  /**
+   * Service for which the call forward is set up. It should be one of the
+   * nsIDOMMozMobileConnectionInfo.ICC_SERVICE_CLASS_* values.
+   */
+  readonly attribute unsigned short serviceClass;
+};
--- a/dom/network/interfaces/nsIDOMMobileConnection.idl
+++ b/dom/network/interfaces/nsIDOMMobileConnection.idl
@@ -8,17 +8,17 @@ interface nsIDOMEventListener;
 interface nsIDOMDOMRequest;
 interface nsIDOMMozMobileICCInfo;
 interface nsIDOMMozMobileConnectionInfo;
 interface nsIDOMMozMobileNetworkInfo;
 interface nsIDOMMozMobileCellInfo;
 interface nsIDOMMozIccManager;
 interface nsIDOMMozMobileCFInfo;
 
-[scriptable, builtinclass, uuid(cc4947eb-aa39-4c25-878d-618dc4d4d2bc)]
+[scriptable, builtinclass, uuid(4175a903-61e6-4fa7-af0d-1e41632ee2dd)]
 interface nsIDOMMozMobileConnection : nsIDOMEventTarget
 {
   const long ICC_SERVICE_CLASS_VOICE = (1 << 0);
   const long ICC_SERVICE_CLASS_DATA = (1 << 1);
   const long ICC_SERVICE_CLASS_FAX = (1 << 2);
   const long ICC_SERVICE_CLASS_SMS = (1 << 3);
   const long ICC_SERVICE_CLASS_DATA_SYNC = (1 << 4);
   const long ICC_SERVICE_CLASS_DATA_ASYNC = (1 << 5);
@@ -303,16 +303,22 @@ interface nsIDOMMozMobileConnection : ns
    */
   [implicit_jscontext] attribute jsval ondataerror;
 
   /**
    * The 'icccardlockerror' event is notified whenever 'unlockCardLock' or
    * 'setCardLock' fails.
    */
   [implicit_jscontext] attribute jsval onicccardlockerror;
+
+  /**
+   * The 'oncfstatechange' event is notified whenever the call forwarding
+   * state changes.
+   */
+  [implicit_jscontext] attribute jsval oncfstatechange;
 };
 
 [scriptable, uuid(5ea0e4a9-4684-40da-9930-8ebb61d187f3)]
 interface nsIDOMMozMobileConnectionInfo : nsISupports
 {
   /**
    * State of the connection.
    *
new file mode 100644
--- /dev/null
+++ b/dom/network/src/CFStateChangeEvent.cpp
@@ -0,0 +1,90 @@
+/* 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 "CFStateChangeEvent.h"
+#include "nsIDOMClassInfo.h"
+#include "nsDOMClassInfoID.h"
+#include "nsContentUtils.h"
+
+DOMCI_DATA(CFStateChangeEvent, mozilla::dom::network::CFStateChangeEvent)
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+already_AddRefed<CFStateChangeEvent>
+CFStateChangeEvent::Create(bool aSuccess,
+                           uint16_t aAction,
+                           uint16_t aReason,
+                           nsAString& aNumber,
+                           uint16_t aTimeSeconds,
+                           uint16_t aServiceClass)
+{
+  NS_ASSERTION(!aNumber.IsEmpty(), "Empty number!");
+
+  nsRefPtr<CFStateChangeEvent> event = new CFStateChangeEvent();
+
+  event->mSuccess = aSuccess;
+  event->mAction = aAction;
+  event->mReason = aReason;
+  event->mNumber = aNumber;
+  event->mTimeSeconds = aTimeSeconds;
+  event->mServiceClass = aServiceClass;
+
+  return event.forget();
+}
+
+NS_IMPL_ADDREF_INHERITED(CFStateChangeEvent, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(CFStateChangeEvent, nsDOMEvent)
+
+NS_INTERFACE_MAP_BEGIN(CFStateChangeEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCFStateChangeEvent)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CFStateChangeEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMETHODIMP
+CFStateChangeEvent::GetSuccess(bool* aSuccess)
+{
+  *aSuccess = mSuccess;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CFStateChangeEvent::GetAction(uint16_t* aAction)
+{
+  *aAction = mAction;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CFStateChangeEvent::GetReason(uint16_t* aReason)
+{
+  *aReason = mReason;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CFStateChangeEvent::GetNumber(nsAString& aNumber)
+{
+  aNumber.Assign(mNumber);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CFStateChangeEvent::GetTimeSeconds(uint16_t* aTimeSeconds)
+{
+  *aTimeSeconds = mTimeSeconds;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CFStateChangeEvent::GetServiceClass(uint16_t* aServiceClass)
+{
+  *aServiceClass = mServiceClass;
+  return NS_OK;
+}
+
+}
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/network/src/CFStateChangeEvent.h
@@ -0,0 +1,70 @@
+/* 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 mozilla_dom_network_cfstatechangeevent_h
+#define mozilla_dom_network_cfstatechangeevent_h
+
+#include "nsIDOMCFStateChangeEvent.h"
+#include "nsDOMEvent.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+class CFStateChangeEvent : public nsDOMEvent,
+                           public nsIDOMCFStateChangeEvent
+{
+  bool mSuccess;
+  uint16_t mAction;
+  uint16_t mReason;
+  nsString mNumber;
+  uint16_t mTimeSeconds;
+  uint16_t mServiceClass;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_TO_NSDOMEVENT
+  NS_DECL_NSIDOMCFSTATECHANGEEVENT
+
+  static already_AddRefed<CFStateChangeEvent>
+  Create(bool aSuccess,
+         uint16_t aAction,
+         uint16_t aReason,
+         nsAString& aNumber,
+         uint16_t aTimeSeconds,
+         uint16_t aServiceClass);
+
+  nsresult
+  Dispatch(nsIDOMEventTarget* aTarget, const nsAString& aEventType)
+  {
+    NS_ASSERTION(aTarget, "Null pointer!");
+    NS_ASSERTION(!aEventType.IsEmpty(), "Empty event type!");
+
+    nsresult rv = InitEvent(aEventType, false, false);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    SetTrusted(true);
+
+    nsDOMEvent* thisEvent = this;
+
+    bool dummy;
+    rv = aTarget->DispatchEvent(thisEvent, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+private:
+  CFStateChangeEvent()
+  : nsDOMEvent(nullptr, nullptr)
+  { }
+
+  ~CFStateChangeEvent()
+  { }
+};
+
+}
+}
+}
+
+#endif // mozilla_dom_network_cfstatechangeevent_h
--- a/dom/network/src/Connection.cpp
+++ b/dom/network/src/Connection.cpp
@@ -25,17 +25,16 @@ namespace network {
 
 const char* Connection::sMeteredPrefName     = "dom.network.metered";
 const bool  Connection::sMeteredDefaultValue = false;
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_0(Connection, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Connection)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozConnection)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozConnection)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozConnection)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(Connection, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(Connection, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(Connection, change)
 
--- a/dom/network/src/Makefile.in
+++ b/dom/network/src/Makefile.in
@@ -53,16 +53,17 @@ CPPSRCS = \
   DataErrorEvent.cpp \
   TCPSocketParent.cpp \
   TCPSocketChild.cpp \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
   MobileConnection.cpp \
+  CFStateChangeEvent.cpp \
   $(NULL)
 endif
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/content/events/src \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -8,75 +8,79 @@
 #include "nsDOMEvent.h"
 #include "nsIObserverService.h"
 #include "USSDReceivedEvent.h"
 #include "DataErrorEvent.h"
 #include "mozilla/Services.h"
 #include "IccManager.h"
 #include "GeneratedEvents.h"
 #include "nsIDOMICCCardLockErrorEvent.h"
+#include "CFStateChangeEvent.h"
 
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "nsJSON.h"
 #include "jsapi.h"
 
 #include "mozilla/dom/USSDReceivedEventBinding.h"
+#include "mozilla/dom/CFStateChangeEventBinding.h"
 
 #define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
 
 #define VOICECHANGE_EVENTNAME      NS_LITERAL_STRING("voicechange")
 #define DATACHANGE_EVENTNAME       NS_LITERAL_STRING("datachange")
 #define CARDSTATECHANGE_EVENTNAME  NS_LITERAL_STRING("cardstatechange")
 #define ICCINFOCHANGE_EVENTNAME    NS_LITERAL_STRING("iccinfochange")
 #define USSDRECEIVED_EVENTNAME     NS_LITERAL_STRING("ussdreceived")
 #define DATAERROR_EVENTNAME        NS_LITERAL_STRING("dataerror")
 #define ICCCARDLOCKERROR_EVENTNAME NS_LITERAL_STRING("icccardlockerror")
+#define CFSTATECHANGE_EVENTNAME    NS_LITERAL_STRING("cfstatechange")
 
 DOMCI_DATA(MozMobileConnection, mozilla::dom::network::MobileConnection)
 
 namespace mozilla {
 namespace dom {
 namespace network {
 
 const char* kVoiceChangedTopic     = "mobile-connection-voice-changed";
 const char* kDataChangedTopic      = "mobile-connection-data-changed";
 const char* kCardStateChangedTopic = "mobile-connection-cardstate-changed";
 const char* kIccInfoChangedTopic   = "mobile-connection-iccinfo-changed";
 const char* kUssdReceivedTopic     = "mobile-connection-ussd-received";
 const char* kDataErrorTopic        = "mobile-connection-data-error";
 const char* kIccCardLockErrorTopic = "mobile-connection-icccardlock-error";
+const char* kCfStateChangedTopic   = "mobile-connection-cfstate-change";
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MobileConnection,
                                                   nsDOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MobileConnection,
                                                 nsDOMEventTargetHelper)
   tmp->mProvider = nullptr;
   tmp->mIccManager = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MobileConnection)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozMobileConnection)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozMobileConnection)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozMobileConnection)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MobileConnection, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MobileConnection, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(MobileConnection, cardstatechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, iccinfochange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, voicechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, datachange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, ussdreceived)
 NS_IMPL_EVENT_HANDLER(MobileConnection, dataerror)
 NS_IMPL_EVENT_HANDLER(MobileConnection, icccardlockerror)
+NS_IMPL_EVENT_HANDLER(MobileConnection, cfstatechange)
 
 MobileConnection::MobileConnection()
 {
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
   // Not being able to acquire the provider isn't fatal since we check
   // for it explicitly below.
   if (!mProvider) {
@@ -99,16 +103,17 @@ MobileConnection::Init(nsPIDOMWindow* aW
 
   obs->AddObserver(this, kVoiceChangedTopic, false);
   obs->AddObserver(this, kDataChangedTopic, false);
   obs->AddObserver(this, kCardStateChangedTopic, false);
   obs->AddObserver(this, kIccInfoChangedTopic, false);
   obs->AddObserver(this, kUssdReceivedTopic, false);
   obs->AddObserver(this, kDataErrorTopic, false);
   obs->AddObserver(this, kIccCardLockErrorTopic, false);
+  obs->AddObserver(this, kCfStateChangedTopic, false);
 
   mIccManager = new icc::IccManager();
   mIccManager->Init(aWindow);
 }
 
 void
 MobileConnection::Shutdown()
 {
@@ -120,16 +125,17 @@ MobileConnection::Shutdown()
 
   obs->RemoveObserver(this, kVoiceChangedTopic);
   obs->RemoveObserver(this, kDataChangedTopic);
   obs->RemoveObserver(this, kCardStateChangedTopic);
   obs->RemoveObserver(this, kIccInfoChangedTopic);
   obs->RemoveObserver(this, kUssdReceivedTopic);
   obs->RemoveObserver(this, kDataErrorTopic);
   obs->RemoveObserver(this, kIccCardLockErrorTopic);
+  obs->RemoveObserver(this, kCfStateChangedTopic);
 
   if (mIccManager) {
     mIccManager->Shutdown();
     mIccManager = nullptr;
   }
 }
 
 // nsIObserver
@@ -234,16 +240,35 @@ MobileConnection::Observe(nsISupports* a
                                  false, false, lockType, retryCount);
     e->SetTrusted(true);
     bool dummy;
     DispatchEvent(event, &dummy);
 
     return NS_OK;
   }
 
+  if (!strcmp(aTopic, kCfStateChangedTopic)) {
+    mozilla::dom::CFStateChangeEventDict dict;
+    bool ok = dict.Init(nsDependentString(aData));
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+    nsRefPtr<CFStateChangeEvent> event =
+      CFStateChangeEvent::Create(dict.mSuccess,
+                                 dict.mAction,
+                                 dict.mReason,
+                                 dict.mNumber,
+                                 dict.mTimeSeconds,
+                                 dict.mServiceClass);
+    NS_ASSERTION(event, "This should never fail!");
+
+    nsresult rv = event->Dispatch(ToIDOMEventTarget(), CFSTATECHANGE_EVENTNAME);
+    NS_ENSURE_SUCCESS(rv, rv);
+    return NS_OK;
+  }
+
   MOZ_NOT_REACHED("Unknown observer topic!");
   return NS_OK;
 }
 
 // nsIDOMMozMobileConnection
 
 NS_IMETHODIMP
 MobileConnection::GetCardState(nsAString& cardState)
--- a/dom/plugins/base/android/ANPAudio.cpp
+++ b/dom/plugins/base/android/ANPAudio.cpp
@@ -93,21 +93,23 @@ struct ANPAudioTrack {
   jclass at_class;
 
   unsigned int rate;
   unsigned int channels;
   unsigned int bufferSize;
   unsigned int isStopped;
   unsigned int keepGoing;
 
-  mozilla::Mutex* lock;
+  mozilla::Mutex lock;
 
   void* user;
   ANPAudioCallbackProc proc;
   ANPSampleFormat format;
+
+  ANPAudioTrack() : lock("ANPAudioTrack") { }
 };
 
 class AudioRunnable : public nsRunnable
 {
 public:
   NS_DECL_NSIRUNNABLE
 
   AudioRunnable(ANPAudioTrack* aAudioTrack) {
@@ -146,17 +148,17 @@ AudioRunnable::Run()
   buffer.bufferData = (void*) byte;
 
   while (true)
   {
     // reset the buffer size
     buffer.size = mTrack->bufferSize;
     
     {
-      mozilla::MutexAutoLock lock(*mTrack->lock);
+      mozilla::MutexAutoLock lock(mTrack->lock);
 
       if (!mTrack->keepGoing)
         break;
 
       // Get data from the plugin
       mTrack->proc(kMoreData_ANPAudioEvent, mTrack->user, &buffer);
     }
 
@@ -183,33 +185,31 @@ AudioRunnable::Run()
     } while(wroteSoFar < buffer.size);
   }
 
   jenv->CallVoidMethod(mTrack->output_unit, at.release);
 
   jenv->DeleteGlobalRef(mTrack->output_unit);
   jenv->DeleteGlobalRef(mTrack->at_class);
 
-  delete mTrack->lock;
-
-  free(mTrack);
-
+  delete mTrack;
+  
   jenv->ReleaseByteArrayElements(bytearray, byte, 0);
 
   return NS_OK;
 }
 
 ANPAudioTrack*
 anp_audio_newTrack(uint32_t sampleRate,    // sampling rate in Hz
                    ANPSampleFormat format,
                    int channelCount,       // MONO=1, STEREO=2
                    ANPAudioCallbackProc proc,
                    void* user)
 {
-  ANPAudioTrack *s = (ANPAudioTrack*) malloc(sizeof(ANPAudioTrack));
+  ANPAudioTrack *s = new ANPAudioTrack();
   if (s == NULL) {
     return NULL;
   }
 
   JNIEnv *jenv = GetJNIForThread();
   if (!jenv)
     return NULL;
 
@@ -217,17 +217,16 @@ anp_audio_newTrack(uint32_t sampleRate, 
   s->rate = sampleRate;
   s->channels = channelCount;
   s->bufferSize = s->rate * s->channels;
   s->isStopped = true;
   s->keepGoing = false;
   s->user = user;
   s->proc = proc;
   s->format = format;
-  s->lock = new mozilla::Mutex("ANPAudioTrack");
 
   int jformat;
   switch (format) {
   case kPCM16Bit_ANPSampleFormat:
     jformat = ENCODING_PCM_16BIT;
     break;
   case kPCM8Bit_ANPSampleFormat:
     jformat = ENCODING_PCM_8BIT;
@@ -283,17 +282,17 @@ anp_audio_newTrack(uint32_t sampleRate, 
 
 void
 anp_audio_deleteTrack(ANPAudioTrack* s)
 {
   if (s == NULL) {
     return;
   }
 
-  mozilla::MutexAutoLock lock(*s->lock);
+  mozilla::MutexAutoLock lock(s->lock);
   s->keepGoing = false;
 
   // deallocation happens in the AudioThread.  There is a
   // potential leak if anp_audio_start is never called, but
   // we do not see that from flash.
 }
 
 void
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -257,32 +257,43 @@ PluginModuleParent::TimeoutChanged(const
       // The timeout value used by the child for its parent
       int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
       unused << static_cast<PluginModuleParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
     }
     return 0;
 }
 
 void
-PluginModuleParent::CleanupFromTimeout()
+PluginModuleParent::CleanupFromTimeout(const bool aFromHangUI)
 {
     if (mShutdown) {
       return;
     }
 
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack, try again
         MessageLoop::current()->PostDelayedTask(
             FROM_HERE,
             mTaskFactory.NewRunnableMethod(
-                &PluginModuleParent::CleanupFromTimeout), 10);
+                &PluginModuleParent::CleanupFromTimeout, aFromHangUI), 10);
         return;
     }
 
-    Close();
+    /* If the plugin container was terminated by the Plugin Hang UI, 
+       then either the I/O thread detects a channel error, or the 
+       main thread must set the error (whomever gets there first).
+       OTOH, if we terminate and return false from 
+       ShouldContinueFromReplyTimeout, then the channel state has 
+       already been set to ChannelTimeout and we should call the 
+       regular Close function. */
+    if (aFromHangUI) {
+        GetIPCChannel()->CloseWithError();
+    } else {
+        Close();
+    }
 }
 
 #ifdef XP_WIN
 namespace {
 
 uint64_t
 FileTimeToUTC(const FILETIME& ftime) 
 {
@@ -460,27 +471,29 @@ PluginModuleParent::TerminateChildProces
 
     if (!GetProcessCpuUsage(processHandles, mPluginCpuUsageOnHang)) {
       mPluginCpuUsageOnHang.Clear();
     }
 #endif
 
     // this must run before the error notification from the channel,
     // or not at all
-    if (aMsgLoop == MessageLoop::current()) {
-        aMsgLoop->PostTask(
-            FROM_HERE,
-            mTaskFactory.NewRunnableMethod(
-                &PluginModuleParent::CleanupFromTimeout));
-    } else {
+    bool isFromHangUI = aMsgLoop != MessageLoop::current();
+    if (isFromHangUI) {
         // If we're posting from a different thread we can't create
         // the task via mTaskFactory
         aMsgLoop->PostTask(FROM_HERE,
                            NewRunnableMethod(this,
-                               &PluginModuleParent::CleanupFromTimeout));
+                               &PluginModuleParent::CleanupFromTimeout,
+                               isFromHangUI));
+    } else {
+        aMsgLoop->PostTask(
+            FROM_HERE,
+            mTaskFactory.NewRunnableMethod(
+                &PluginModuleParent::CleanupFromTimeout, isFromHangUI));
     }
 
     if (!KillProcess(OtherProcess(), 1, false))
         NS_WARNING("failed to kill subprocess!");
 }
 
 #ifdef XP_WIN
 void
@@ -965,17 +978,19 @@ bool
 PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
 {
 #ifndef MOZ_X11
     NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
 #else
     NS_ABORT_IF_FALSE(0 > mPluginXSocketFdDup.get(),
                       "Already backed up X resources??");
     mPluginXSocketFdDup.forget();
-    mPluginXSocketFdDup.reset(aXSocketFd.PlatformHandle());
+    if (aXSocketFd.IsValid()) {
+      mPluginXSocketFdDup.reset(aXSocketFd.PlatformHandle());
+    }
 #endif
     return true;
 }
 
 void
 PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
                                           int32_t status, void* notifyData)
 {
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -291,17 +291,17 @@ private:
 
 private:
     CrashReporterParent* CrashReporter();
 
 #ifdef MOZ_CRASHREPORTER
     void ProcessFirstMinidump();
     void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
 #endif
-    void CleanupFromTimeout();
+    void CleanupFromTimeout(const bool aByHangUI);
     void SetChildTimeout(const int32_t aChildTimeout);
     static int TimeoutChanged(const char* aPref, void* aModule);
     void NotifyPluginCrashed();
 
     PluginProcessParent* mSubprocess;
     // the plugin thread in mSubprocess
     NativeThreadId mPluginThread;
     bool mShutdown;
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -103,16 +103,21 @@ MOCHITEST_CHROME_FILES = \
   test_bug479979.xul \
   test_refresh_navigator_plugins.html \
   $(NULL)
 
 ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 MOCHITEST_CHROME_FILES += \
   test_privatemode.xul \
   $(NULL)
+else
+MOCHITEST_CHROME_FILES += \
+  privatemode_perwindowpb.xul \
+  test_privatemode_perwindowpb.xul \
+  $(NULL)
 endif
 
 ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 MOCHITEST_FILES += \
   test_instance_re-parent-windowed.html \
   test_visibility.html \
   $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/privatemode_perwindowpb.xul
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window title="NPAPI Private Mode Tests"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <title>NPAPI Private Mode Tests</title>
+<body xmlns="http://www.w3.org/1999/xhtml">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+<embed id="plugin2" type="application/x-test" width="200" height="200"></embed>
+</body>
+</window>
copy from dom/plugins/test/mochitest/test_privatemode.xul
copy to dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul
--- a/dom/plugins/test/mochitest/test_privatemode.xul
+++ b/dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul
@@ -11,27 +11,20 @@
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 <embed id="plugin2" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
 function runTests() {
-  // don't run these tests if we can't get ahold of the private browsing service
-  var privateBrowsing = null;
-  try {
-    privateBrowsing = Components.classes["@mozilla.org/privatebrowsing;1"].getService(Components.interfaces.nsIPrivateBrowsingService);
-  } catch (e) {
-    ok(true, "no Private Browsing service");
-    SimpleTest.finish();
-    return;
-  }
-
   var pluginElement1 = document.getElementById("plugin1");
   var pluginElement2 = document.getElementById("plugin2");
 
   var state1 = false;
   var state2 = false;
   var exceptionThrown = false;
 
   try {
@@ -42,57 +35,66 @@ function runTests() {
   }
   is(exceptionThrown, false, "Exception thrown getting private mode state.");
   is(state1, false, "Browser returned incorrect private mode state.");
   is(state2, false, "Browser returned incorrect private mode state.");
 
   pluginElement1.setCookie("foo");
   is(pluginElement1.getCookie(), "foo", "Cookie was set and retrieved correctly in public mode.");
 
-  // change private mode pref
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
-  var keepCurrentSession;
-  try {
-    keepCurrentSession = prefs.getBoolPref("browser.privatebrowsing.keep_current_session");
-  } catch (e) {
-    keepCurrentSession = false
+  // open a window with private mode and get the references of the elements.
+  var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIWebNavigation)
+                    .QueryInterface(Ci.nsIDocShellTreeItem)
+                    .rootTreeItem
+                    .QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindow);
+  var contentPage = "chrome://mochitests/content/chrome/dom/plugins/test/privatemode_perwindowpb.xul";
+
+  function testOnWindow(aIsPrivate, aCallback) {
+    var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+    win.addEventListener("load", function onLoad() {
+      win.removeEventListener("load", onLoad, false);
+      win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+        if (win.content.location.href == "about:privatebrowsing") {
+          win.gBrowser.loadURI(contentPage);
+          return;
+        }
+        win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+        win.gBrowser.selectedBrowser.focus();
+        SimpleTest.executeSoon(function() { aCallback(win); });
+      }, true);
+      SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); });
+    }, true);
   }
-  prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
-  privateBrowsing.privateBrowsingEnabled = true;
-
-  try {
-    state1 = pluginElement1.lastReportedPrivateModeState();
-    state2 = pluginElement2.lastReportedPrivateModeState();
-  } catch (e) {
-    exceptionThrown = true;
-  }
-  is(exceptionThrown, false, "Exception thrown getting private mode state.");
-  is(state1, true, "Private mode state reported incorrectly.");
-  is(state2, true, "Private mode state reported incorrectly.");
 
-  var officialState1, officialState2;
-  try {
-    officialState1 = pluginElement1.queryPrivateModeState();
-    officialState2 = pluginElement2.queryPrivateModeState();
-  } catch (e) {
-    exceptionThrown = true;
-  }
-  is(exceptionThrown, false, "Exception thrown getting private mode state.");
-  is(officialState1, state1, "Private mode reported and queried is inconsistent.");
-  is(officialState2, state2, "Private mode reported and queried is inconsistent.");
+  testOnWindow(true, function(aWin) {
+    pluginElement1 = aWin.gBrowser.contentDocument.getElementById("plugin1");
+    pluginElement2 = aWin.gBrowser.contentDocument.getElementById("plugin2");
 
-  // It would be nice to assert that we don't see the public cookie in private mode,
-  // but the NPAPI complains when the resulting string is empty.
-  // is(pluginElement1.getCookie(), "", "Public cookie was not retrieved in private mode.");
-  pluginElement1.setCookie("bar");
-  is(pluginElement1.getCookie(), "bar", "Cookie was set and retrieved correctly in private mode.");
+    var officialState1, officialState2;
+    try {
+      officialState1 = pluginElement1.queryPrivateModeState();
+      officialState2 = pluginElement2.queryPrivateModeState();
+    } catch (e) {
+      exceptionThrown = true;
+    }
+    is(exceptionThrown, false, "Exception thrown getting private mode state.");
+    is(officialState1, true, "Querying private mode reported incorrectly");
+    is(officialState2, true, "Querying private mode reported incorrectly");
 
-  // reset preference states
-  privateBrowsing.privateBrowsingEnabled = false;
-  prefs.setBoolPref("browser.privatebrowsing.keep_current_session", keepCurrentSession);
+    // It would be nice to assert that we don't see the public cookie in private mode,
+    // but the NPAPI complains when the resulting string is empty.
+    // is(pluginElement1.getCookie(), "", "Public cookie was not retrieved in private mode.");
+    pluginElement1.setCookie("bar");
+    is(pluginElement1.getCookie(), "bar", "Cookie was set and retrieved correctly in private mode.");
 
-  is(pluginElement1.getCookie(), "foo", "Private cookie was not retrieved in public mode.");
+    aWin.close();
 
-  SimpleTest.finish();
+    pluginElement1 = document.getElementById("plugin1");
+    is(pluginElement1.getCookie(), "foo", "Private cookie was not retrieved in public mode.");
+
+    SimpleTest.finish();
+  });
 }
 ]]>
 </script>
 </window>
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/sms/src/SmsManager.cpp
@@ -33,17 +33,16 @@ namespace mozilla {
 namespace dom {
 namespace sms {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_0(SmsManager, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SmsManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsManager)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozSmsManager)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozSmsManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(SmsManager, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SmsManager, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(SmsManager, received)
 NS_IMPL_EVENT_HANDLER(SmsManager, sending)
--- a/dom/sms/src/SmsRequest.cpp
+++ b/dom/sms/src/SmsRequest.cpp
@@ -51,17 +51,16 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INH
                                                nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SmsRequest)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsRequest)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDOMRequest)
   NS_INTERFACE_MAP_ENTRY(nsISmsRequest)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozSmsRequest)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozSmsRequest)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(SmsRequest, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SmsRequest, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(SmsRequest, success)
 NS_IMPL_EVENT_HANDLER(SmsRequest, error)
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -341,23 +341,21 @@ nsresult nsJSThunk::EvaluateScript(nsICh
     }
 
     // If we took the sandbox path above, v might be in the sandbox
     // compartment.
     if (!JS_WrapValue(cx, &v)) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    if (NS_FAILED(rv)) {
-        rv = NS_ERROR_MALFORMED_URI;
-    }
-    else if (v.isUndefined()) {
-        rv = NS_ERROR_DOM_RETVAL_UNDEFINED;
-    }
-    else {
+    if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
+        return NS_ERROR_MALFORMED_URI;
+    } else if (v.isUndefined()) {
+        return NS_ERROR_DOM_RETVAL_UNDEFINED;
+    } else {
         nsDependentJSString result;
         if (!result.init(cx, v)) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
 
         char *bytes;
         uint32_t bytesLen;
         NS_NAMED_LITERAL_CSTRING(isoCharset, "ISO-8859-1");
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -72,28 +72,30 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:SendMMI:Return:KO",
   "RIL:CancelMMI:Return:OK",
   "RIL:CancelMMI:Return:KO",
   "RIL:StkCommand",
   "RIL:StkSessionEnd",
   "RIL:DataError",
   "RIL:SetCallForwardingOption",
   "RIL:GetCallForwardingOption",
-  "RIL:CellBroadcastReceived"
+  "RIL:CellBroadcastReceived",
+  "RIL:CfStateChanged"
 ];
 
 const kVoiceChangedTopic     = "mobile-connection-voice-changed";
 const kDataChangedTopic      = "mobile-connection-data-changed";
 const kCardStateChangedTopic = "mobile-connection-cardstate-changed";
 const kIccInfoChangedTopic   = "mobile-connection-iccinfo-changed";
 const kUssdReceivedTopic     = "mobile-connection-ussd-received";
 const kStkCommandTopic       = "icc-manager-stk-command";
 const kStkSessionEndTopic    = "icc-manager-stk-session-end";
 const kDataErrorTopic        = "mobile-connection-data-error";
 const kIccCardLockErrorTopic = "mobile-connection-icccardlock-error";
+const kCfStateChangedTopic   = "mobile-connection-cfstate-change";
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
@@ -1002,16 +1004,25 @@ RILContentHelper.prototype = {
         Services.obs.notifyObservers(null, kDataErrorTopic, msg.json.error);
         break;
       case "RIL:GetCallForwardingOption":
         this.handleGetCallForwardingOption(msg.json);
         break;
       case "RIL:SetCallForwardingOption":
         this.handleSetCallForwardingOption(msg.json);
         break;
+      case "RIL:CfStateChanged":
+        let result = JSON.stringify({success: msg.json.success,
+                                     action: msg.json.action,
+                                     reason: msg.json.reason,
+                                     number: msg.json.number,
+                                     timeSeconds: msg.json.timeSeconds,
+                                     serviceClass: msg.json.serviceClass});
+        Services.obs.notifyObservers(null, kCfStateChangedTopic, result);
+        break;
       case "RIL:CellBroadcastReceived":
         let message = new CellBroadcastMessage(msg.json);
         this._deliverCallback("_cellBroadcastCallbacks",
                               "notifyMessageReceived",
                               [message]);
         break;
     }
   },
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -49,16 +49,17 @@ const kSmsSendingObserverTopic          
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 const kSysMsgListenerReadyObserverTopic  = "system-message-listener-ready";
 const kSysClockChangeObserverTopic       = "system-clock-change";
 const kTimeNitzAutomaticUpdateEnabled    = "time.nitz.automatic-update.enabled";
+const kTimeNitzAvailable                 = "time.nitz.available";
 const kCellBroadcastSearchList           = "ril.cellbroadcast.searchlist";
 
 const DOM_SMS_DELIVERY_RECEIVED          = "received";
 const DOM_SMS_DELIVERY_SENDING           = "sending";
 const DOM_SMS_DELIVERY_SENT              = "sent";
 const DOM_SMS_DELIVERY_ERROR             = "error";
 
 const CALL_WAKELOCK_TIMEOUT              = 5000;
@@ -283,16 +284,19 @@ function RadioInterfaceLayer() {
   lock.get("ril.supl.passwd", this);
   lock.get("ril.supl.httpProxyHost", this);
   lock.get("ril.supl.httpProxyPort", this);
 
   // Read the 'time.nitz.automatic-update.enabled' setting to see if
   // we need to adjust the system clock time and time zone by NITZ.
   lock.get(kTimeNitzAutomaticUpdateEnabled, this);
 
+  // Set "time.nitz.available" to false when starting up.
+  this.setNitzAvailable(false);
+
   // Read the Cell Broadcast Search List setting, string of integers or integer
   // ranges separated by comma, to set listening channels.
   lock.get(kCellBroadcastSearchList, this);
 
   this._messageManagerByRequest = {};
 
   // Manage message targets in terms of permission. Only the authorized and
   // registered contents can receive related messages.
@@ -1596,16 +1600,24 @@ RadioInterfaceLayer.prototype = {
    * Handle data call list.
    */
   handleDataCallList: function handleDataCallList(message) {
     this._deliverDataCallCallback("receiveDataCallList",
                                   [message.datacalls, message.datacalls.length]);
   },
 
   /**
+   * Set the setting value of "time.nitz.available".
+   */
+  setNitzAvailable: function setNitzAvailable(value) {
+    gSettingsService.createLock().set(kTimeNitzAvailable, value, null,
+                                      "fromInternalSetting");
+  },
+
+  /**
    * Set the NITZ message in our system time.
    */
   setNitzTime: function setNitzTime(message) {
     // To set the system clock time. Note that there could be a time diff
     // between when the NITZ was received and when the time is actually set.
     gTimeService.set(
       message.networkTimeInMS + (Date.now() - message.receiveTimeInMS));
 
@@ -1625,16 +1637,19 @@ RadioInterfaceLayer.prototype = {
       gSettingsService.createLock().set("time.timezone", timeZoneStr, null);
     }
   },
 
   /**
    * Handle the NITZ message.
    */
   handleNitzTime: function handleNitzTime(message) {
+    // Got the NITZ info received from the ril_worker.
+    this.setNitzAvailable(true);
+
     // Cache the latest NITZ message whenever receiving it.
     this._lastNitzMessage = message;
 
     // Set the received NITZ time if the setting is enabled.
     if (this._nitzAutomaticUpdateEnabled) {
       this.setNitzTime(message);
     }
   },
@@ -1717,32 +1732,41 @@ RadioInterfaceLayer.prototype = {
 
   handleQueryCallForwardStatus: function handleQueryCallForwardStatus(message) {
     debug("handleQueryCallForwardStatus: " + JSON.stringify(message));
     this._sendRequestResults("RIL:GetCallForwardingOption", message);
   },
 
   handleSetCallForward: function handleSetCallForward(message) {
     debug("handleSetCallForward: " + JSON.stringify(message));
-    this._sendRequestResults("RIL:SetCallForwardingOption", message);
+    this._sendTargetMessage("mobileconnection", "RIL:CfStateChanged", message);
+
+    let messageType;
+    if (message.isSendMMI) {
+      messageType = message.success ? "RIL:SendMMI:Return:OK" :
+                                      "RIL:SendMMI:Return:KO";
+    } else {
+      messageType = "RIL:SetCallForwardingOption";
+    }
+    this._sendRequestResults(messageType, message);
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     switch (topic) {
       case kSysMsgListenerReadyObserverTopic:
         Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic);
         this._sysMsgListenerReady = true;
         this._resendQueuedTargetMessage();
         this._ensureRadioState();
         break;
       case kMozSettingsChangedObserverTopic:
         let setting = JSON.parse(data);
-        this.handle(setting.key, setting.value);
+        this.handleSettingsChange(setting.key, setting.value, setting.message);
         break;
       case "xpcom-shutdown":
         ppmm.removeMessageListener("child-process-shutdown", this);
         for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) {
           ppmm.removeMessageListener(msgname, this);
         }
         for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
           ppmm.removeMessageListener(msgname, this);
@@ -1802,16 +1826,30 @@ RadioInterfaceLayer.prototype = {
 
   // Remember the last NITZ message so that we can set the time based on
   // the network immediately when users enable network-based time.
   _lastNitzMessage: null,
 
   // Cell Broadcast settings values.
   _cellBroadcastSearchListStr: null,
 
+  handleSettingsChange: function handleSettingsChange(aName, aResult, aMessage) {
+    // Don't allow any content processes to modify the setting
+    // "time.nitz.available" except for the chrome process.
+    let isNitzAvailable = (this._lastNitzMessage !== null);
+    if (aName === kTimeNitzAvailable && aMessage !== "fromInternalSetting" &&
+        aResult !== isNitzAvailable) {
+      debug("Content processes cannot modify 'time.nitz.available'. Restore!");
+      // Restore the setting to the current value.
+      this.setNitzAvailable(isNitzAvailable);
+    }
+
+    this.handle(aName, aResult);
+  },
+
   // nsISettingsServiceCallback
   handle: function handle(aName, aResult) {
     switch(aName) {
       case "ril.radio.disabled":
         debug("'ril.radio.disabled' is now " + aResult);
         this._radioEnabled = !aResult;
         this._ensureRadioState();
         break;
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -235,17 +235,17 @@ interface nsIRILContentHelper : nsIMobil
   attribute bool microphoneMuted;
   attribute bool speakerEnabled;
 
   readonly attribute nsIDOMMozVoicemailStatus voicemailStatus;
   readonly attribute DOMString voicemailNumber;
   readonly attribute DOMString voicemailDisplayName;
 };
 
-[scriptable, uuid(886251a4-18d9-4a54-a6a1-57d5c1d61ceb)]
+[scriptable, uuid(7cb249b8-7ec9-4e31-a043-293121574dd0)]
 interface nsIICCRecords : nsISupports
 {
   /**
    * Integrated Circuit Card Identifier.
    */
   readonly attribute DOMString iccid;
 
   /**
@@ -277,16 +277,31 @@ interface nsIICCRecords : nsISupports
    * Abbreviated dialling numbers
    */
   readonly attribute jsval adn;
 
   /**
    * Fixed Dialling Numbers
    */
   readonly attribute jsval fdn;
+
+  /**
+   * Service Provider Name (SPN) of the subscriber's home network.
+   */
+  readonly attribute DOMString spn;
+
+  /**
+   * Network name must be a part of displayed carrier name.
+   */
+  readonly attribute boolean isDisplayNetworkNameRequired;
+
+  /**
+   * Service provider name must be a part of displayed carrier name.
+   */
+  readonly attribute boolean isDisplaySpnRequired;
 };
 
 [scriptable, uuid(c0c5cb9f-6372-4b5a-b74c-baacc2da5e4f)]
 interface nsIVoicemailInfo : nsISupports
 {
   readonly attribute DOMString number;
 
   readonly attribute DOMString displayName;
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -124,20 +124,20 @@ this.REQUEST_EXIT_EMERGENCY_CALLBACK_MOD
 this.REQUEST_GET_SMSC_ADDRESS = 100;
 this.REQUEST_SET_SMSC_ADDRESS = 101;
 this.REQUEST_REPORT_SMS_MEMORY_STATUS = 102;
 this.REQUEST_REPORT_STK_SERVICE_IS_RUNNING = 103;
 this.REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE = 104;
 this.REQUEST_ISIM_AUTHENTICATION = 105;
 this.REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU = 106;
 this.REQUEST_STK_SEND_ENVELOPE_WITH_STATUS = 107;
+this.REQUEST_VOICE_RADIO_TECH = 108;
 this.REQUEST_DIAL_EMERGENCY_CALL = 10016;
 
 // Akami/Maguro specific parcel types.
-this.REQUEST_VOICE_RADIO_TECH = 105;
 this.REQUEST_IMS_REGISTRATION_STATE = 106;
 this.REQUEST_IMS_SEND_SMS = 107;
 this.REQUEST_GET_DATA_CALL_PROFILE = 108;
 this.REQUEST_SET_UICC_SUBSCRIPTION = 109;
 this.REQUEST_SET_DATA_SUBSCRIPTION = 110;
 this.REQUEST_GET_UICC_SUBSCRIPTION = 111;
 this.REQUEST_GET_DATA_SUBSCRIPTION = 112;
 this.REQUEST_SET_SUBSCRIPTION_MODE = 113;
@@ -243,17 +243,17 @@ RIL_ERROR_TO_GECKO_ERROR[ERROR_PASSWORD_
 
 // 3GPP 23.040 clause 9.2.3.6 TP-Message-Reference(TP-MR):
 // The number of times the MS automatically repeats the SMS-SUBMIT shall be in
 // the range 1 to 3 but the precise number is an implementation matter.
 this.SMS_RETRY_MAX = 3;
 
 this.RADIO_STATE_OFF = 0;
 this.RADIO_STATE_UNAVAILABLE = 1;
-this.RADIO_STATE_ON = 2;
+this.RADIO_STATE_ON = 10; // RIL v7
 
 // RIL v5 legacy constants:
 this.RADIO_STATE_SIM_NOT_READY = 2;
 this.RADIO_STATE_SIM_LOCKED_OR_ABSENT = 3;
 this.RADIO_STATE_SIM_READY = 4;
 this.RADIO_STATE_RUIM_NOT_READY = 5;
 this.RADIO_STATE_RUIM_READY = 6;
 this.RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7;
@@ -378,16 +378,17 @@ this.NETWORK_CREG_TECH_EVDO0 = 7;
 this.NETWORK_CREG_TECH_EVDOA = 8;
 this.NETWORK_CREG_TECH_HSDPA = 9;
 this.NETWORK_CREG_TECH_HSUPA = 10;
 this.NETWORK_CREG_TECH_HSPA = 11;
 this.NETWORK_CREG_TECH_EVDOB = 12;
 this.NETWORK_CREG_TECH_EHRPD = 13;
 this.NETWORK_CREG_TECH_LTE = 14;
 this.NETWORK_CREG_TECH_HSPAP = 15;
+this.NETWORK_CREG_TECH_GSM = 16;
 
 this.CALL_STATE_ACTIVE = 0;
 this.CALL_STATE_HOLDING = 1;
 this.CALL_STATE_DIALING = 2;
 this.CALL_STATE_ALERTING = 3;
 this.CALL_STATE_INCOMING = 4;
 this.CALL_STATE_WAITING = 5;
 this.CALL_STATE_BUSY = 6;
@@ -898,17 +899,17 @@ this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCA
 this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_NMR     = 0;
 
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SET_UP_EVENT_LIST  = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_MT_CALL                = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_CALL_CONNECTED         = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_CALL_DISCONNECTED      = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_LOCATION_STATUS        = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_USER_ACTIVITY          = 0;
-this.STK_TERMINAL_SUPPORT_EVENT_IDLE_SCREEN_AVAILABLE  = 0;
+this.STK_TERMINAL_SUPPORT_EVENT_IDLE_SCREEN_AVAILABLE  = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_CARD_READER_STATUS     = 0;
 
 this.STK_TERMINAL_SUPPORT_PROACTIVE_TIMER_START_STOP   = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_TIMER_GET_CURRENT  = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_DATE    = 1;
 this.STK_TERMINAL_SUPPORT_GET_INKEY                    = 1;
 this.STK_TERMINAL_SUPPORT_SET_UP_IDLE_MODE_TEXT        = 1;
 this.STK_TERMINAL_SUPPORT_RUN_AT_COMMAND               = 0;
@@ -2320,16 +2321,17 @@ this.GECKO_RADIO_TECH = [
   "evdoa",
   "hsdpa",
   "hsupa",
   "hspa",
   "evdob",
   "ehrpd",
   "lte",
   "hspa+",
+  "gsm"
 ];
 
 this.GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN = -1;
 
 // Call forwarding action. Must be in sync with nsIDOMMozMobileCFInfo interface
 this.CALL_FORWARD_ACTION_DISABLE = 0;
 this.CALL_FORWARD_ACTION_ENABLE = 1;
 this.CALL_FORWARD_ACTION_QUERY_STATUS = 2;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -752,16 +752,27 @@ let RIL = {
   initRILState: function initRILState() {
     /**
      * One of the RADIO_STATE_* constants.
      */
     this.radioState = GECKO_RADIOSTATE_UNAVAILABLE;
     this._isInitialRadioState = true;
 
     /**
+     * True if we are on CDMA network.
+     */
+    this._isCdma = false;
+
+    /**
+     * Set when radio is ready but radio tech is unknown. That is, we are
+     * waiting for REQUEST_VOICE_RADIO_TECH
+     */
+    this._waitingRadioTech = false;
+
+    /**
      * ICC status. Keeps a reference of the data response to the
      * getICCStatus request.
      */
     this.iccStatus = null;
 
     /**
      * Card state
      */
@@ -1451,16 +1462,20 @@ let RIL = {
     Buf.writeUint32(on ? 1 : 0);
     Buf.sendParcel();
   },
 
   getVoiceRegistrationState: function getVoiceRegistrationState() {
     Buf.simpleRequest(REQUEST_VOICE_REGISTRATION_STATE);
   },
 
+  getVoiceRadioTechnology: function getVoiceRadioTechnology() {
+    Buf.simpleRequest(REQUEST_VOICE_RADIO_TECH);
+  },
+
   getDataRegistrationState: function getDataRegistrationState() {
     Buf.simpleRequest(REQUEST_DATA_REGISTRATION_STATE);
   },
 
   getOperator: function getOperator() {
     Buf.simpleRequest(REQUEST_OPERATOR);
   },
 
@@ -2129,16 +2144,18 @@ let RIL = {
         options.reason = MMI_SC_TO_CF_REASON[sc];
         options.number = mmi.sia;
         options.serviceClass = this._siToServiceClass(mmi.sib);
         if (options.action == CALL_FORWARD_ACTION_QUERY_STATUS) {
           this.queryCallForwardStatus(options);
           return;
         }
 
+        options.rilMessageType = "setCallForward";
+        options.isSendMMI = true;
         options.timeSeconds = mmi.sic;
         this.setCallForward(options);
         return;
 
       // Change the current ICC PIN number.
       case MMI_SC_PIN:
         // As defined in TS.122.030 6.6.2 to change the ICC PIN we should expect
         // an MMI code of the form **04*OLD_PIN*NEW_PIN*NEW_PIN#, where old PIN
@@ -2580,16 +2597,22 @@ let RIL = {
       case STK_EVENT_TYPE_CALL_CONNECTED:  // Fall through
         command.deviceId = {
           sourceId: (command.event.isIssuedByRemote ?
                      STK_DEVICE_ID_NETWORK : STK_DEVICE_ID_ME),
           destinationId: STK_DEVICE_ID_SIM
         };
         command.transactionId = 0;
         break;
+      case STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE:
+        command.deviceId = {
+          sourceId: STK_DEVICE_ID_DISPLAY,
+          destinationId: STK_DEVICE_ID_SIM
+        };
+        break;
     }
     this.sendICCEnvelopeCommand(command);
   },
 
   /**
    * Send REQUEST_STK_SEND_ENVELOPE_COMMAND to ICC.
    *
    * @param tag
@@ -3292,16 +3315,57 @@ let RIL = {
       throw new Error("Invalid network tuple (should be 5 or 6 digits): " + networkTuple);
     }
 
     network.mcc = mcc;
     network.mnc = mnc;
   },
 
   /**
+   * Process radio technology change.
+   */
+  _processRadioTech: function _processRadioTech(radioTech) {
+    let isCdma = true;
+    this.radioTech = radioTech;
+
+    switch(radioTech) {
+    case NETWORK_CREG_TECH_GPRS:
+    case NETWORK_CREG_TECH_EDGE:
+    case NETWORK_CREG_TECH_UMTS:
+    case NETWORK_CREG_TECH_HSDPA:
+    case NETWORK_CREG_TECH_HSUPA:
+    case NETWORK_CREG_TECH_HSPA:
+    case NETWORK_CREG_TECH_LTE:
+    case NETWORK_CREG_TECH_HSPAP:
+    case NETWORK_CREG_TECH_GSM:
+      isCdma = false;
+    };
+
+    if (DEBUG) {
+      debug("Radio tech is set to: " + GECKO_RADIO_TECH[radioTech] +
+            ", it is a " + (isCdma?"cdma":"gsm") + " technology");
+    }
+
+    // We should request SIM information when
+    //  1. Radio state has been changed, so we are waiting for radioTech or
+    //  2. isCdma is different from this._isCdma.
+    if (this._waitingRadioTech || isCdma != this._isCdma) {
+      this._isCdma = isCdma;
+      this._waitingRadioTech = false;
+      if (this._isCdma) {
+        this.getDeviceIdentity();
+      } else {
+        this.getIMEI();
+        this.getIMEISV();
+      }
+       this.getICCStatus();
+    }
+  },
+
+  /**
    * Helper for returning the TOA for the given dial string.
    */
   _toaFromString: function _toaFromString(number) {
     let toa = TOA_UNKNOWN;
     if (number && number.length > 0 && number[0] == '+') {
       toa = TOA_INTERNATIONAL;
     }
     return toa;
@@ -4950,16 +5014,26 @@ RIL[REQUEST_STK_SEND_ENVELOPE_WITH_STATU
   if (!responsePduLen) {
     this.acknowledgeSMS(success, success ? PDU_FCS_OK
                                          : PDU_FCS_USIM_DATA_DOWNLOAD_ERROR);
     return;
   }
 
   this.acknowledgeIncomingGsmSmsWithPDU(success, responsePduLen, options);
 };
+RIL[REQUEST_VOICE_RADIO_TECH] = function REQUEST_VOICE_RADIO_TECH(length, options) {
+  if (options.rilRequestError) {
+    if (DEBUG) {
+      debug("Error when getting voice radio tech: " + options.rilRequestError);
+    }
+    return;
+  }
+  let radioTech = Buf.readUint32List();
+  this._processRadioTech(radioTech[0]);
+};
 RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED() {
   let radioState = Buf.readUint32();
 
   // Ensure radio state at boot time.
   if (this._isInitialRadioState) {
     this._isInitialRadioState = false;
     if (radioState != RADIO_STATE_OFF) {
       this.setRadioPower({on: false});
@@ -4979,43 +5053,68 @@ RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHA
   if (DEBUG) {
     debug("Radio state changed from '" + this.radioState +
           "' to '" + newState + "'");
   }
   if (this.radioState == newState) {
     return;
   }
 
-  // TODO hardcoded for now (see bug 726098)
-  let cdma = false;
+  switch (radioState) {
+  case RADIO_STATE_SIM_READY:
+  case RADIO_STATE_SIM_NOT_READY:
+  case RADIO_STATE_SIM_LOCKED_OR_ABSENT:
+    this._isCdma = false;
+    this._waitingRadioTech = false;
+    break;
+  case RADIO_STATE_RUIM_READY:
+  case RADIO_STATE_RUIM_NOT_READY:
+  case RADIO_STATE_RUIM_LOCKED_OR_ABSENT:
+  case RADIO_STATE_NV_READY:
+  case RADIO_STATE_NV_NOT_READY:
+    this._isCdma = true;
+    this._waitingRadioTech = false;
+    break;
+  case RADIO_STATE_ON: // RIL v7
+    // This value is defined in RIL v7, we will retrieve radio tech by another
+    // request. We leave _isCdma untouched, and it will be set once we get the
+    // radio technology.
+    this._waitingRadioTech = true;
+    this.getVoiceRadioTechnology();
+    break;
+  }
 
   if ((this.radioState == GECKO_RADIOSTATE_UNAVAILABLE ||
        this.radioState == GECKO_RADIOSTATE_OFF) &&
        newState == GECKO_RADIOSTATE_READY) {
     // The radio became available, let's get its info.
-    if (cdma) {
-      this.getDeviceIdentity();
-    } else {
-      this.getIMEI();
-      this.getIMEISV();
+    if (!this._waitingRadioTech) {
+      if (this._isCdma) {
+        this.getDeviceIdentity();
+      } else {
+        this.getIMEI();
+        this.getIMEISV();
+      }
     }
     this.getBasebandVersion();
     this.updateCellBroadcastConfig();
   }
 
   this.radioState = newState;
   this.sendDOMMessage({
     rilMessageType: "radiostatechange",
     radioState: newState
   });
 
   // If the radio is up and on, so let's query the card state.
   // On older RILs only if the card is actually ready, though.
+  // If _waitingRadioTech is set, we don't need to get icc status now.
   if (radioState == RADIO_STATE_UNAVAILABLE ||
-      radioState == RADIO_STATE_OFF) {
+      radioState == RADIO_STATE_OFF ||
+      this._waitingRadioTech) {
     return;
   }
   this.getICCStatus();
 };
 RIL[UNSOLICITED_RESPONSE_CALL_STATE_CHANGED] = function UNSOLICITED_RESPONSE_CALL_STATE_CHANGED() {
   this.getCurrentCalls();
 };
 RIL[UNSOLICITED_RESPONSE_VOICE_NETWORK_STATE_CHANGED] = function UNSOLICITED_RESPONSE_VOICE_NETWORK_STATE_CHANGED() {
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -27,17 +27,17 @@ function newUint8Worker() {
     index += offset;
   };
 
   worker.debug = do_print;
 
   return worker;
 }
 
-function newUint8WithOutgoingIndexWorker() {
+function newUint8SupportOutgoingIndexWorker() {
   let worker = newWorker();
   let index = 4;          // index for read
   let buf = [0, 0, 0, 0]; // Preserved parcel size
 
   worker.Buf.writeUint8 = function (value) {
     if (worker.Buf.outgoingIndex >= buf.length) {
       buf.push(value);
     } else {
@@ -1366,17 +1366,17 @@ add_test(function test_parse_pbr_tlvs() 
 
   run_next_test();
 });
 
 /**
  * Verify Event Download Command : Location Status
  */
 add_test(function test_stk_event_download_location_status() {
-  let worker = newUint8WithOutgoingIndexWorker();
+  let worker = newUint8SupportOutgoingIndexWorker();
   let buf = worker.Buf;
   let pduHelper = worker.GsmPDUHelper;
 
   buf.sendParcel = function () {
     // Type
     do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND)
 
     // Token : we don't care
@@ -1445,18 +1445,18 @@ add_test(function test_stk_event_downloa
   worker.RIL.sendStkEventDownload({
     event: event
   });
 });
 
 /**
  * Verify STK terminal response
  */
-add_test(function test_stk_event_download_location_status() {
-  let worker = newUint8WithOutgoingIndexWorker();
+add_test(function test_stk_terminal_response() {
+  let worker = newUint8SupportOutgoingIndexWorker();
   let buf = worker.Buf;
   let pduHelper = worker.GsmPDUHelper;
 
   buf.sendParcel = function () {
     // Type
     do_check_eq(this.readUint32(), REQUEST_STK_SEND_TERMINAL_RESPONSE)
 
     // Token : we don't care
@@ -1508,8 +1508,56 @@ add_test(function test_stk_event_downloa
         isPacked: true
       }
     },
     input: "Mozilla",
     resultCode: STK_RESULT_OK
   };
   worker.RIL.sendStkTerminalResponse(response);
 });
+
+/**
+ * Verify Event Download Command : Idle Screen Available
+ */
+add_test(function test_stk_event_download_idle_screen_available() {
+  let worker = newUint8SupportOutgoingIndexWorker();
+  let buf = worker.Buf;
+  let pduHelper = worker.GsmPDUHelper;
+
+  buf.sendParcel = function () {
+    // Type
+    do_check_eq(this.readUint32(), REQUEST_STK_SEND_ENVELOPE_COMMAND)
+
+    // Token : we don't care
+    this.readUint32();
+
+    // Data Size, 18 = 2 * (2 + TLV_DEVICE_ID_SIZE(4) + TLV_EVENT_LIST_SIZE(3))
+    do_check_eq(this.readUint32(), 18);
+
+    // BER tag
+    do_check_eq(pduHelper.readHexOctet(), BER_EVENT_DOWNLOAD_TAG);
+
+    // BER length, 7 = TLV_DEVICE_ID_SIZE(4) + TLV_EVENT_LIST_SIZE(3)
+    do_check_eq(pduHelper.readHexOctet(), 7);
+
+    // Device Identities, Type-Length-Value(Source ID-Destination ID)
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_DEVICE_ID |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 2);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_DISPLAY);
+    do_check_eq(pduHelper.readHexOctet(), STK_DEVICE_ID_SIM);
+
+    // Event List, Type-Length-Value
+    do_check_eq(pduHelper.readHexOctet(), COMPREHENSIONTLV_TAG_EVENT_LIST |
+                                          COMPREHENSIONTLV_FLAG_CR);
+    do_check_eq(pduHelper.readHexOctet(), 1);
+    do_check_eq(pduHelper.readHexOctet(), STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE);
+
+    run_next_test();
+  };
+
+  let event = {
+    eventType: STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE
+  };
+  worker.RIL.sendStkEventDownload({
+    event: event
+  });
+});
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -65,17 +65,16 @@ var interfaceNamesInGlobalScope =
     "Pkcs11",
     "NotifyAudioAvailableEvent",
     "Array",
     "SVGZoomAndPan",
     "XULPopupElement",
     "MediaError",
     "DeviceStorageCursor",
     "DeviceStorageChangeEvent",
-    "DeviceStorageStat",
     "PageTransitionEvent",
     "DataContainerEvent",
     "MozCSSKeyframesRule",
     "SVGAnimatedInteger",
     "TouchEvent",
     "OpenWindowEventDetail",
     "IDBIndex",
     "EventListener",
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CFStateChangeEvent.webidl
@@ -0,0 +1,12 @@
+/* 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/. */
+
+dictionary CFStateChangeEventDict {
+  boolean success = false;
+  short action = -1;
+  short reason = -1;
+  DOMString? number = null;
+  short timeSeconds = -1;
+  short serviceClass = -1;
+};
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -193,16 +193,17 @@ webidl_files += \
   MediaStreamList.webidl \
   RTCIceServer.webidl \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_RIL
 webidl_files += \
   USSDReceivedEvent.webidl \
+  CFStateChangeEvent.webidl \
   $(NULL)
 endif
 
 ifdef ENABLE_TESTS
 test_webidl_files := \
   TestCodeGen.webidl \
   TestDictionary.webidl \
   TestExampleGen.webidl \
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -67,18 +67,16 @@ GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
   $(PROCESSEDJAVAFILES) \
   gecko.ap_  \
   res/values/strings.xml \
   R.java \
   $(NULL)
 
-GARBAGE_DIRS += classes res
-
 # Bug 567884 - Need a way to find appropriate icons during packaging
 ifeq ($(MOZ_APP_NAME),fennec)
 ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png
 ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png
 
 # we released these builds to the public with shared IDs and need to keep them
 ifeq (org.mozilla.firefox,$(ANDROID_PACKAGE_NAME))
 DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID"
@@ -129,17 +127,16 @@ RES_DIRS= \
 include $(topsrcdir)/config/rules.mk
 
 # Override the Java settings with some specific android settings
 include $(topsrcdir)/config/android-common.mk
 
 # Note that we're going to set up a dependency directly between embed_android.dex and the java files
 # Instead of on the .class files, since more than one .class file might be produced per .java file
 classes.dex: $(JAVAFILES) $(PROCESSEDJAVAFILES) R.java
-	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES)) $(PROCESSEDJAVAFILES) R.java
 	$(DX) --dex --output=$@ classes
 
 AndroidManifest.xml $(PROCESSEDJAVAFILES): % : %.in
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
              $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
 
 res/drawable/icon.png: $(MOZ_APP_ICON)
--- a/gfx/angle/Makefile.in
+++ b/gfx/angle/Makefile.in
@@ -127,32 +127,21 @@ CPPSRCS += \
 # End gpy translation. Remainder of targets are in src/libEGL/Makefile.in and
 # src/libGLESv2/Makefile.in
 
 ifdef MOZ_ANGLE_RENDERER
 
 # libEGL depends on (links against!) libGLESv2!
 DIRS = src/libGLESv2 src/libEGL
 
-
-space := $(NULL) $(NULL)
-
-# We use a trick from http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
-# to escape possible spaces in MOZ_D3DX9_CAB and MOZ_D3DCOMPILER_CAB
-
+libs::
 ifdef MOZ_D3DX9_CAB
-libs:: $(DIST)/bin/$(MOZ_D3DX9_DLL)
-
-$(DIST)/bin/$(MOZ_D3DX9_DLL): $(subst $(space),\$(space),$(MOZ_D3DX9_CAB))
 	expand "$(MOZ_D3DX9_CAB)" -F:$(MOZ_D3DX9_DLL) "$(DIST)/bin"
 endif
 ifdef MOZ_D3DCOMPILER_CAB
-libs:: $(DIST)/bin/$(MOZ_D3DCOMPILER_DLL)
-
-$(DIST)/bin/$(MOZ_D3DCOMPILER_DLL): $(subst $(space),\$(space),$(MOZ_D3DCOMPILER_CAB))
 	expand "$(MOZ_D3DCOMPILER_CAB)" -F:$(MOZ_D3DCOMPILER_DLL) "$(DIST)/bin"
 endif
 
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 # We have to filter out -pedantic, because of
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -187,16 +187,24 @@ AsyncPanZoomController::AsyncPanZoomCont
   Preferences::GetUint("apzc.asyncscroll.throttle", &mAsyncScrollThrottleTime);
   Preferences::GetUint("apzc.asyncscroll.timeout", &mAsyncScrollTimeout);
 }
 
 AsyncPanZoomController::~AsyncPanZoomController() {
 
 }
 
+void
+AsyncPanZoomController::Destroy()
+{
+  // These memebrs can only be used on the controller/UI thread.
+  mGeckoContentController = nullptr;
+  mGestureEventListener = nullptr;
+}
+
 /* static */float
 AsyncPanZoomController::GetTouchStartTolerance()
 {
   return gTouchStartTolerance;
 }
 
 static gfx::Point
 WidgetSpaceToCompensatedViewportSpace(const gfx::Point& aPoint,
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -74,16 +74,22 @@ public:
                          GestureBehavior aGestures = DEFAULT_GESTURES);
   ~AsyncPanZoomController();
 
   // --------------------------------------------------------------------------
   // These methods must only be called on the controller/UI thread.
   //
 
   /**
+   * Shut down the controller/UI thread state and prepare to be
+   * deleted (which may happen from any thread).
+   */
+  void Destroy();
+
+  /**
    * General handler for incoming input events. Manipulates the frame metrics
    * basde on what type of input it is. For example, a PinchGestureEvent will
    * cause scaling. This should only be called externally to this class.
    * HandleInputEvent() should be used internally.
    */
   nsEventStatus ReceiveInputEvent(const InputData& aEvent);
 
   /**
--- a/gfx/layers/ipc/Axis.h
+++ b/gfx/layers/ipc/Axis.h
@@ -178,17 +178,17 @@ protected:
   int32_t mStartPos;
   float mVelocity;
   // Acceleration is represented by an int, which is the power we raise a
   // constant to and then multiply the velocity by whenever it is sampled. We do
   // this only when we detect that the user wants to do a fast fling; that is,
   // they are flinging multiple times in a row very quickly, probably trying to
   // reach one of the extremes of the page.
   int32_t mAcceleration;
-  nsRefPtr<AsyncPanZoomController> mAsyncPanZoomController;
+  AsyncPanZoomController* mAsyncPanZoomController;
 };
 
 class AxisX : public Axis {
 public:
   AxisX(AsyncPanZoomController* mAsyncPanZoomController);
   virtual float GetPointOffset(const gfx::Point& aPoint);
   virtual float GetRectLength(const gfx::Rect& aRect);
   virtual float GetRectOffset(const gfx::Rect& aRect);
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -188,18 +188,29 @@ ContainerRender(Container* aContainer,
   nsIntRect cachedScissor = aContainer->gl()->ScissorRect();
   aContainer->gl()->PushScissorRect();
   aContainer->mSupportsComponentAlphaChildren = false;
 
   float opacity = aContainer->GetEffectiveOpacity();
   const gfx3DMatrix& transform = aContainer->GetEffectiveTransform();
   bool needsFramebuffer = aContainer->UseIntermediateSurface();
   if (needsFramebuffer) {
+    nsIntRect framebufferRect = visibleRect;
+    // we're about to create a framebuffer backed by textures to use as an intermediate
+    // surface. What to do if its size (as given by framebufferRect) would exceed the
+    // maximum texture size supported by the GL? The present code chooses the compromise
+    // of just clamping the framebuffer's size to the max supported size.
+    // This gives us a lower resolution rendering of the intermediate surface (children layers).
+    // See bug 827170 for a discussion.
+    GLint maxTexSize;
+    aContainer->gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTexSize);
+    framebufferRect.width = std::min(framebufferRect.width, maxTexSize);
+    framebufferRect.height = std::min(framebufferRect.height, maxTexSize);
+
     LayerManagerOGL::InitMode mode = LayerManagerOGL::InitModeClear;
-    nsIntRect framebufferRect = visibleRect;
     if (aContainer->GetEffectiveVisibleRegion().GetNumRects() == 1 && 
         (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE))
     {
       // don't need a background, we're going to paint all opaque stuff
       aContainer->mSupportsComponentAlphaChildren = true;
       mode = LayerManagerOGL::InitModeNone;
     } else {
       const gfx3DMatrix& transform3D = aContainer->GetEffectiveTransform();
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -637,17 +637,21 @@ bool Channel::ChannelImpl::ProcessOutgoi
     char buf[tmp];
 
     if (message_send_bytes_written_ == 0 &&
         !msg->file_descriptor_set()->empty()) {
       // This is the first chunk of a message which has descriptors to send
       struct cmsghdr *cmsg;
       const unsigned num_fds = msg->file_descriptor_set()->size();
 
-      DCHECK_LE(num_fds, FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE);
+      if (num_fds > FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
+        LOG(FATAL) << "Too many file descriptors!";
+        // This should not be reached.
+        return false;
+      }
 
       msgh.msg_control = buf;
       msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds);
       cmsg = CMSG_FIRSTHDR(&msgh);
       cmsg->cmsg_level = SOL_SOCKET;
       cmsg->cmsg_type = SCM_RIGHTS;
       cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
       msg->file_descriptor_set()->GetDescriptors(
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -50,17 +50,17 @@ FileDescriptor::DuplicateInCurrentProces
     if (DuplicateHandle(GetCurrentProcess(), aHandle, GetCurrentProcess(),
                         &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
 #else // XP_WIN
     if ((newHandle = dup(aHandle)) != INVALID_HANDLE) {
 #endif
       mHandle = newHandle;
       return;
     }
-    NS_WARNING("Failed to duplicate file descriptor!");
+    NS_WARNING("Failed to duplicate file handle for current process!");
   }
 
   mHandle = INVALID_HANDLE;
 }
 
 void
 FileDescriptor::CloseCurrentProcessHandle()
 {
@@ -70,17 +70,17 @@ FileDescriptor::CloseCurrentProcessHandl
   // Don't actually close handles created by another process.
   if (mHandleCreatedByOtherProcess) {
     return;
   }
 
   if (IsValid()) {
 #ifdef XP_WIN
     if (!CloseHandle(mHandle)) {
-      NS_WARNING("Failed to close file handle!");
+      NS_WARNING("Failed to close file handle for current process!");
     }
 #else // XP_WIN
     HANDLE_EINTR(close(mHandle));
 #endif
     mHandle = INVALID_HANDLE;
   }
 }
 
@@ -90,23 +90,26 @@ FileDescriptor::ShareTo(const FileDescri
 {
   PlatformHandleType newHandle;
 #ifdef XP_WIN
   if (IsValid()) {
     if (DuplicateHandle(GetCurrentProcess(), mHandle, aOtherProcess,
                         &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
       return newHandle;
     }
-    NS_WARNING("Failed to duplicate file handle!");
+    NS_WARNING("Failed to duplicate file handle for other process!");
   }
   return INVALID_HANDLE;
 #else // XP_WIN
   if (IsValid()) {
     newHandle = dup(mHandle);
-    return base::FileDescriptor(newHandle, /* auto_close */ true);
+    if (IsValid(newHandle)) {
+      return base::FileDescriptor(newHandle, /* auto_close */ true);
+    }
+    NS_WARNING("Failed to duplicate file handle for other process!");
   }
   return base::FileDescriptor();
 #endif
 
   MOZ_NOT_REACHED("Must not get here!");
   return PickleType();
 }
 
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -4321,17 +4321,20 @@ class _GenerateProtocolActorCode(ipdl.as
 
         read = MethodDefn(self.readMethodDecl(outtype, var))
         ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
                                          args=[ msgvar, itervar,
                                                 ExprAddrOf(picklevar) ])))
         ifread.addifstmt(StmtReturn.FALSE)
 
         ifnvalid = StmtIf(ExprNot(ExprCall(ExprSelect(tmpvar, '.', 'IsValid'))))
-        ifnvalid.addifstmt(StmtReturn.FALSE)
+        ifnvalid.addifstmt(
+            _protocolErrorBreakpoint('[' +
+                                     _actorName(self.protocol.name, self.side) +
+                                     '] Received an invalid file descriptor!'))
 
         read.addstmts([
             StmtDecl(Decl(_fdPickleType(), picklevar.name)),
             ifread,
             Whitespace.NL,
             StmtDecl(Decl(_fdType(), tmpvar.name),
                      init=ExprCall(ExprVar('FileDescriptor'),
                                    args=[ _fdBackstagePass(), picklevar ])),
--- a/js/jsd/jsd.h
+++ b/js/jsd/jsd.h
@@ -205,17 +205,18 @@ struct JSDThreadState
 };
 
 struct JSDStackFrameInfo
 {
     JSCList             links;        /* we are part of a JSCList */
     JSDThreadState*     jsdthreadstate;
     JSDScript*          jsdscript;
     uintptr_t           pc;
-    JSStackFrame*       fp;
+    bool                isConstructing;
+    JSAbstractFramePtr  frame;
 };
 
 #define GOT_PROTO   ((short) (1 << 0))
 #define GOT_PROPS   ((short) (1 << 1))
 #define GOT_PARENT  ((short) (1 << 2))
 #define GOT_CTOR    ((short) (1 << 3))
 
 struct JSDValue
@@ -342,17 +343,17 @@ jsd_DestroyScriptManager(JSDContext* jsd
 extern JSDScript*
 jsd_FindJSDScript(JSDContext*  jsdc,
                   JSScript     *script);
 
 extern JSDScript*
 jsd_FindOrCreateJSDScript(JSDContext    *jsdc,
                           JSContext     *cx,
                           JSScript      *script,
-                          JSStackFrame  *fp);
+                          JSAbstractFramePtr frame);
 
 extern JSDProfileData*
 jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script);
 
 extern uint32_t
 jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script);
 
 extern void
@@ -973,38 +974,38 @@ jsd_GetPropertyAlias(JSDContext* jsdc, J
 
 extern unsigned
 jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop);
 
 /**************************************************/
 /* Stepping Functions */
 
 extern void *
-jsd_FunctionCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
-                     JSBool *ok, void *closure);
+jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
+                     JSBool before, JSBool *ok, void *closure);
 
 extern void *
-jsd_TopLevelCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
-                     JSBool *ok, void *closure);
+jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
+                     JSBool before, JSBool *ok, void *closure);
 
 /**************************************************/
 /* Object Functions */
 
 extern JSBool
 jsd_InitObjectManager(JSDContext* jsdc);
 
 extern void
 jsd_DestroyObjectManager(JSDContext* jsdc);
 
 extern void
 jsd_DestroyObjects(JSDContext* jsdc);
 
 extern void
 jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj,
-                 JSStackFrame *fp);
+                 JSAbstractFramePtr frame);
 
 extern JSDObject*
 jsd_IterateObjects(JSDContext* jsdc, JSDObject** iterp);
 
 extern JSObject*
 jsd_GetWrappedObject(JSDContext* jsdc, JSDObject* jsdobj);
 
 extern const char*
--- a/js/jsd/jsd_hook.cpp
+++ b/js/jsd/jsd_hook.cpp
@@ -29,17 +29,17 @@ jsd_InterruptHandler(JSContext *cx, JSSc
     hook     = jsdc->interruptHook;
     hookData = jsdc->interruptHookData;
     JSD_UNLOCK();
 
     if (!hook)
         return JSTRAP_CONTINUE;
     
     JSD_LOCK_SCRIPTS(jsdc);
-    jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, NULL);
+    jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr());
     JSD_UNLOCK_SCRIPTS(jsdc);
     if( ! jsdscript )
         return JSTRAP_CONTINUE;
 
 #ifdef LIVEWIRE
     if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) )
         return JSTRAP_CONTINUE;
 #endif
@@ -67,17 +67,17 @@ jsd_DebuggerHandler(JSContext *cx, JSScr
     JSD_LOCK();
     hook     = jsdc->debuggerHook;
     hookData = jsdc->debuggerHookData;
     JSD_UNLOCK();
     if(!hook)
         return JSTRAP_CONTINUE;
 
     JSD_LOCK_SCRIPTS(jsdc);
-    jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, NULL);
+    jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr());
     JSD_UNLOCK_SCRIPTS(jsdc);
     if( ! jsdscript )
         return JSTRAP_CONTINUE;
 
     return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD,
                                  hook, hookData, rval);
 }
 
@@ -101,17 +101,17 @@ jsd_ThrowHandler(JSContext *cx, JSScript
     JSD_LOCK();
     hook     = jsdc->throwHook;
     hookData = jsdc->throwHookData;
     JSD_UNLOCK();
     if (!hook)
         return JSTRAP_CONTINUE;
 
     JSD_LOCK_SCRIPTS(jsdc);
-    jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, NULL);
+    jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr());
     JSD_UNLOCK_SCRIPTS(jsdc);
     if( ! jsdscript )
         return JSTRAP_CONTINUE;
 
     JS_GetPendingException(cx, rval);
 
     return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW,
                                  hook, hookData, rval);
--- a/js/jsd/jsd_obj.cpp
+++ b/js/jsd/jsd_obj.cpp
@@ -75,38 +75,38 @@ static void
         jsd_DropAtom(jsdc, jsdobj->ctorURL);
     if(jsdobj->ctorName)
         jsd_DropAtom(jsdc, jsdobj->ctorName);
     free(jsdobj);
 }
 
 void
 jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj,
-                 JSStackFrame *fp)
+                 JSAbstractFramePtr frame)
 {
     JSDObject* jsdobj;
     JSScript* script;
     JSDScript* jsdscript;
     const char* ctorURL;
     JSString* ctorNameStr;
     const char* ctorName;
 
     JSD_LOCK_OBJECTS(jsdc);
     jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj);
     if( jsdobj && !jsdobj->ctorURL )
     {
-        script = JS_GetFrameScript(cx, fp);
+        script = frame.script();
         if( script )
         {
             ctorURL = JS_GetScriptFilename(cx, script);
             if( ctorURL )
                 jsdobj->ctorURL = jsd_AddAtom(jsdc, ctorURL);
 
             JSD_LOCK_SCRIPTS(jsdc);
-            jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, fp);
+            jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame);
             JSD_UNLOCK_SCRIPTS(jsdc);
             if( jsdscript && (ctorNameStr = jsd_GetScriptFunctionId(jsdc, jsdscript)) ) {
                 if( (ctorName = JS_EncodeString(cx, ctorNameStr)) ) {
                     jsdobj->ctorName = jsd_AddAtom(jsdc, ctorName);
                     JS_free(cx, (void *) ctorName);
                 }
             }
             jsdobj->ctorLineno = JS_GetScriptBaseLineNumber(cx, script);
--- a/js/jsd/jsd_scpt.cpp
+++ b/js/jsd/jsd_scpt.cpp
@@ -273,29 +273,32 @@ jsd_FindJSDScript( JSDContext*  jsdc,
     JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
     return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script);
 }
 
 JSDScript *
 jsd_FindOrCreateJSDScript(JSDContext    *jsdc,
                           JSContext     *cx,
                           JSScript      *script,
-                          JSStackFrame  *fp)
+                          JSAbstractFramePtr frame)
 {
     JSDScript *jsdscript;
     JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 
     jsdscript = jsd_FindJSDScript(jsdc, script);
     if (jsdscript)
         return jsdscript;
 
     /* Fallback for unknown scripts: create a new script. */
-    if (!fp)
-        JS_BrokenFrameIterator(cx, &fp);
-    if (fp)
+    if (!frame) {
+        JSBrokenFrameIterator iter(cx);
+        if (!iter.done())
+            frame = iter.abstractFramePtr();
+    }
+    if (frame)
         jsdscript = _newJSDScript(jsdc, cx, script);
 
     return jsdscript;
 }
 
 JSDProfileData*
 jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script)
 {
--- a/js/jsd/jsd_stak.cpp
+++ b/js/jsd/jsd_stak.cpp
@@ -24,17 +24,18 @@ void JSD_ASSERT_VALID_STACK_FRAME(JSDSta
 }
 #endif
 
 static JSDStackFrameInfo* 
 _addNewFrame(JSDContext*        jsdc,
              JSDThreadState*    jsdthreadstate,
              JSScript*          script,
              uintptr_t          pc,
-             JSStackFrame*      fp)
+             bool               isConstructing,
+             JSAbstractFramePtr frame)
 {
     JSDStackFrameInfo* jsdframe;
     JSDScript*         jsdscript = NULL;
 
     JSD_LOCK_SCRIPTS(jsdc);
     jsdscript = jsd_FindJSDScript(jsdc, script);
     JSD_UNLOCK_SCRIPTS(jsdc);
     if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
@@ -47,18 +48,19 @@ static JSDStackFrameInfo*
         jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
 
     jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
     if( ! jsdframe )
         return NULL;
 
     jsdframe->jsdthreadstate = jsdthreadstate;
     jsdframe->jsdscript      = jsdscript;
+    jsdframe->isConstructing = isConstructing;
     jsdframe->pc             = pc;
-    jsdframe->fp             = fp;
+    jsdframe->frame          = frame;
 
     JS_APPEND_LINK(&jsdframe->links, &jsdthreadstate->stack);
     jsdthreadstate->stackDepth++;
 
     return jsdframe;
 }
 
 static void
@@ -69,60 +71,62 @@ static void
     if( jsdframe )
         free(jsdframe);
 }
 
 JSDThreadState*
 jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
 {
     JSDThreadState* jsdthreadstate;
-    JSStackFrame *  iter = NULL;
-    JSStackFrame *  fp;
 
     jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState));
     if( ! jsdthreadstate )
         return NULL;
 
     jsdthreadstate->context = cx;
     jsdthreadstate->thread = JSD_CURRENT_THREAD();
     JS_INIT_CLIST(&jsdthreadstate->stack);
     jsdthreadstate->stackDepth = 0;
 
     JS_BeginRequest(jsdthreadstate->context);
-    while( NULL != (fp = JS_BrokenFrameIterator(cx, &iter)) )
+
+    JSBrokenFrameIterator iter(cx);
+    while(!iter.done())
     {
-        JSScript* script = JS_GetFrameScript(cx, fp);
-        uintptr_t  pc = (uintptr_t) JS_GetFramePC(cx, fp);
+        JSAbstractFramePtr frame = iter.abstractFramePtr();
+        JSScript* script = frame.script();
+        uintptr_t  pc = (uintptr_t)iter.pc();
         jsval dummyThis;
 
         /*
          * don't construct a JSDStackFrame for dummy frames (those without a
          * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
          * isn't set.
          */
-        if (JS_GetFrameThis(cx, fp, &dummyThis))
+        if (frame.getThisValue(cx, &dummyThis))
         {
-            JSDStackFrameInfo *frame;
+            bool isConstructing = iter.isConstructing();
+            JSDStackFrameInfo *frameInfo = _addNewFrame( jsdc, jsdthreadstate, script, pc, isConstructing, frame );
 
-            frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp );
-
-            if ((jsdthreadstate->stackDepth == 0 && !frame) ||
-                (jsdthreadstate->stackDepth == 1 && frame &&
-                 frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript)))
+            if ((jsdthreadstate->stackDepth == 0 && !frameInfo) ||
+                (jsdthreadstate->stackDepth == 1 && frameInfo &&
+                 frameInfo->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frameInfo->jsdscript)))
             {
                 /*
                  * if we failed to create the first frame, or the top frame
                  * is not enabled for debugging, fail the entire thread state.
                  */
                 JS_INIT_CLIST(&jsdthreadstate->links);
                 JS_EndRequest(jsdthreadstate->context);
                 jsd_DestroyThreadState(jsdc, jsdthreadstate);
                 return NULL;
             }
         }
+
+        ++iter;
     }
     JS_EndRequest(jsdthreadstate->context);
 
     if (jsdthreadstate->stackDepth == 0)
     {
         free(jsdthreadstate);
         return NULL;
     }
@@ -257,17 +261,17 @@ jsd_GetCallObjectForStackFrame(JSDContex
 {
     JSObject* obj;
     JSDValue* jsdval = NULL;
 
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
-        obj = JS_GetFrameCallObject(jsdthreadstate->context, jsdframe->fp); 
+        obj = jsdframe->frame.callObject(jsdthreadstate->context);
         if(obj)                                                             
             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
 
     return jsdval;
 }
@@ -280,20 +284,20 @@ jsd_GetScopeChainForStackFrame(JSDContex
     JSObject* obj;
     JSDValue* jsdval = NULL;
 
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
         JS_BeginRequest(jsdthreadstate->context);
-        obj = JS_GetFrameScopeChain(jsdthreadstate->context, jsdframe->fp); 
+        obj = jsdframe->frame.scopeChain(jsdthreadstate->context);
         JS_EndRequest(jsdthreadstate->context);
-        if(obj)                                                             
-            jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
+        if(obj)
+            jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
 
     return jsdval;
 }
 
 JSDValue*
@@ -304,17 +308,17 @@ jsd_GetThisForStackFrame(JSDContext* jsd
     JSDValue* jsdval = NULL;
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
         JSBool ok;
         jsval thisval;
         JS_BeginRequest(jsdthreadstate->context);
-        ok = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp, &thisval);
+        ok = jsdframe->frame.getThisValue(jsdthreadstate->context, &thisval);
         JS_EndRequest(jsdthreadstate->context);
         if(ok)
             jsdval = JSD_NewValue(jsdc, thisval);
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
     return jsdval;
 }
@@ -325,18 +329,17 @@ jsd_GetIdForStackFrame(JSDContext* jsdc,
                        JSDStackFrameInfo* jsdframe)
 {
     JSString *rv = NULL;
     
     JSD_LOCK_THREADSTATES(jsdc);
     
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
-        JSFunction *fun = JS_GetFrameFunction (jsdthreadstate->context,
-                                               jsdframe->fp);
+        JSFunction *fun = jsdframe->frame.maybeFun();
         if( fun )
         {
             rv = JS_GetFunctionId (fun);
 
             /*
              * For compatibility we return "anonymous", not an empty string
              * here.
              */
@@ -354,34 +357,34 @@ jsd_IsStackFrameDebugger(JSDContext* jsd
                          JSDThreadState* jsdthreadstate,
                          JSDStackFrameInfo* jsdframe)
 {
     JSBool rv = JS_TRUE;
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
-        rv = JS_IsDebuggerFrame(jsdthreadstate->context, jsdframe->fp);
+        rv = jsdframe->frame.isDebuggerFrame();
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
     return rv;
 }
 
 JSBool
 jsd_IsStackFrameConstructing(JSDContext* jsdc, 
                              JSDThreadState* jsdthreadstate,
                              JSDStackFrameInfo* jsdframe)
 {
     JSBool rv = JS_TRUE;
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
-        rv = JS_IsConstructorFrame(jsdthreadstate->context, jsdframe->fp);
+        rv = jsdframe->isConstructing;
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
     return rv;
 }
 
 JSBool
 jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc, 
@@ -407,18 +410,18 @@ jsd_EvaluateUCScriptInStackFrame(JSDCont
 
     cx = jsdthreadstate->context;
     JS_ASSERT(cx);
 
     if (eatExceptions)
         exceptionState = JS_SaveExceptionState(cx);
     JS_ClearPendingException(cx);
     jsd_StartingEvalUsingFilename(jsdc, filename);
-    retval = JS_EvaluateUCInStackFrame(cx, jsdframe->fp, bytes, length, 
-                                       filename, lineno, rval);
+    retval = jsdframe->frame.evaluateUCInStackFrame(cx, bytes, length, filename, lineno,
+                                                    rval);
     jsd_FinishedEvalUsingFilename(jsdc, filename);
     if (eatExceptions)
         JS_RestoreExceptionState(cx, exceptionState);
 
     return retval;
 }
 
 JSBool
@@ -445,18 +448,18 @@ jsd_EvaluateScriptInStackFrame(JSDContex
 
     cx = jsdthreadstate->context;
     JS_ASSERT(cx);
 
     if (eatExceptions)
         exceptionState = JS_SaveExceptionState(cx);
     JS_ClearPendingException(cx);
     jsd_StartingEvalUsingFilename(jsdc, filename);
-    retval = JS_EvaluateInStackFrame(cx, jsdframe->fp, bytes, length,
-                                     filename, lineno, rval);
+    retval = jsdframe->frame.evaluateInStackFrame(cx, bytes, length, filename, lineno,
+                                                  rval);
     jsd_FinishedEvalUsingFilename(jsdc, filename);
     if (eatExceptions)
         JS_RestoreExceptionState(cx, exceptionState);
 
     return retval;
 }
 
 JSString*
--- a/js/jsd/jsd_step.cpp
+++ b/js/jsd/jsd_step.cpp
@@ -26,29 +26,29 @@ static char*
         if(!p) return "";
         memset(p, ' ', MAX_INDENT);
     }
     if(i > MAX_INDENT) return p;
     return p + MAX_INDENT-i;
 }
 
 static void
-_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp,
-                  JSBool before)
+_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame,
+                  bool isConstructing, JSBool before)
 {
     JSDScript* jsdscript = NULL;
     JSScript * script;
     static indent = 0;
     JSString* funName = NULL;
 
-    script = JS_GetFrameScript(cx, fp);
+    script = frame.script();
     if(script)
     {
         JSD_LOCK_SCRIPTS(jsdc);
-        jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, fp);
+        jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame);
         JSD_UNLOCK_SCRIPTS(jsdc);
         if(jsdscript)
             funName = JSD_GetScriptFunctionId(jsdc, jsdscript);
     }
 
     if(before)
         printf("%sentering ", _indentSpaces(indent++));
     else
@@ -58,59 +58,59 @@ static void
         printf("TOP_LEVEL");
     else
         JS_FileEscapedString(stdout, funName, 0);
 
     if(before)
     {
         jsval thisVal;
 
-        printf("%s this: ", JS_IsConstructorFrame(cx, fp) ? "constructing":"");
+        printf("%s this: ", isConstructing ? "constructing":"");
 
-        if (JS_GetFrameThis(cx, fp, &thisVal))
+        if (JS_GetFrameThis(cx, frame, &thisVal))
             printf("0x%0llx", (uintptr_t) thisVal);
         else
             puts("<unavailable>");
     }
     printf("\n");
     JS_ASSERT(indent >= 0);
 }
 #endif
 
 JSBool
-_callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
-          unsigned type, JSD_CallHookProc hook, void *hookData)
+_callHook(JSDContext *jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
+          JSBool before, unsigned type, JSD_CallHookProc hook, void *hookData)
 {
     JSDScript*        jsdscript;
     JSScript*         jsscript;
     JSBool            hookresult = JS_TRUE;
     
     if (!jsdc || !jsdc->inited)
         return JS_FALSE;
 
     if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA))
     {
         /* no hook to call, no profile data needs to be collected,
          * so there is nothing to do here.
          */
         return hookresult;
     }
-    
-    if (before && JS_IsConstructorFrame(cx, fp)) {
+
+    if (before && isConstructing) {
         jsval newObj;
-        if (!JS_GetFrameThis(cx, fp, &newObj))
+        if (!frame.getThisValue(cx, &newObj))
             return JS_FALSE;
-        jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), fp);
+        jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), frame);
     }
 
-    jsscript = JS_GetFrameScript(cx, fp);
+    jsscript = frame.script();
     if (jsscript)
     {
         JSD_LOCK_SCRIPTS(jsdc);
-        jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, fp);
+        jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, frame);
         JSD_UNLOCK_SCRIPTS(jsdc);
     
         if (jsdscript)
         {
             if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript))
             {
                 JSDProfileData *pdata;
                 pdata = jsd_GetScriptProfileData (jsdc, jsdscript);
@@ -218,67 +218,67 @@ JSBool
                         jsd_CallCallHook (jsdc, cx, type, hook, hookData);
                 else
                     hookresult = JS_TRUE;
             }
         }
     }
 
 #ifdef JSD_TRACE
-    _interpreterTrace(jsdc, cx, fp, before);
+    _interpreterTrace(jsdc, cx, frame, isConstructing, before);
     return JS_TRUE;
 #else
     return hookresult;
 #endif
 
 }
 
 void *
-jsd_FunctionCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
-                     JSBool *ok, void *closure)
+jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
+                     JSBool before, JSBool *ok, void *closure)
 {
     JSDContext*       jsdc;
     JSD_CallHookProc  hook;
     void*             hookData;
 
     jsdc = (JSDContext*) closure;
     
     /* local in case jsdc->functionHook gets cleared on another thread */
     JSD_LOCK();
     hook     = jsdc->functionHook;
     hookData = jsdc->functionHookData;
     JSD_UNLOCK();
     
-    if (_callHook (jsdc, cx, fp, before,
+    if (_callHook (jsdc, cx, frame, isConstructing, before,
                    (before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN,
                    hook, hookData))
     {
         return closure;
     }
     
     return NULL;
 }
 
 void *
-jsd_TopLevelCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
-                     JSBool *ok, void *closure)
+jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing,
+                     JSBool before, JSBool *ok, void *closure)
 {
     JSDContext*       jsdc;
     JSD_CallHookProc  hook;
     void*             hookData;
 
     jsdc = (JSDContext*) closure;
 
     /* local in case jsdc->toplevelHook gets cleared on another thread */
     JSD_LOCK();
     hook     = jsdc->toplevelHook;
     hookData = jsdc->toplevelHookData;
     JSD_UNLOCK();
     
-    if (_callHook (jsdc, cx, fp, before,
+    if (_callHook (jsdc, cx, frame, isConstructing, before,
                    (before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END,
                    hook, hookData))
     {
         return closure;
     }
     
     return NULL;
     
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -725,17 +725,17 @@ class OrderedHashSet
 
 /*** HashableValue *******************************************************************************/
 
 bool
 HashableValue::setValue(JSContext *cx, const Value &v)
 {
     if (v.isString()) {
         // Atomize so that hash() and equals() are fast and infallible.
-        JSString *str = AtomizeString(cx, v.toString(), DoNotInternAtom);
+        JSString *str = AtomizeString<CanGC>(cx, v.toString(), DoNotInternAtom);
         if (!str)
             return false;
         value = StringValue(str);
     } else if (v.isDouble()) {
         double d = v.toDouble();
         int32_t i;
         if (MOZ_DOUBLE_IS_INT32(d, &i)) {
             // Normalize int32_t-valued doubles to int32_t for faster hashing and testing.
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -119,17 +119,17 @@ obj_toSource(JSContext *cx, unsigned arg
 
     /* If outermost, we need parentheses to be an expression, not a block. */
     bool outermost = (cx->cycleDetectorSet.count() == 0);
 
     AutoCycleDetector detector(cx, obj);
     if (!detector.init())
         return false;
     if (detector.foundCycle()) {
-        JSString *str = js_NewStringCopyZ(cx, "{}");
+        JSString *str = js_NewStringCopyZ<CanGC>(cx, "{}");
         if (!str)
             return false;
         args.rval().setString(str);
         return true;
     }
 
     StringBuffer buf(cx);
     if (outermost && !buf.append('('))
@@ -758,17 +758,17 @@ obj_keys(JSContext *cx, unsigned argc, V
     AutoValueVector vals(cx);
     if (!vals.reserve(props.length()))
         return false;
     for (size_t i = 0, len = props.length(); i < len; i++) {
         jsid id = props[i];
         if (JSID_IS_STRING(id)) {
             vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
         } else if (JSID_IS_INT(id)) {
-            JSString *str = Int32ToString(cx, JSID_TO_INT(id));
+            JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
             if (!str)
                 return false;
             vals.infallibleAppend(StringValue(str));
         } else {
             JS_ASSERT(JSID_IS_OBJECT(id));
         }
     }
 
@@ -794,17 +794,17 @@ obj_getOwnPropertyNames(JSContext *cx, u
 
     AutoValueVector vals(cx);
     if (!vals.resize(keys.length()))
         return false;
 
     for (size_t i = 0, len = keys.length(); i < len; i++) {
          jsid id = keys[i];
          if (JSID_IS_INT(id)) {
-             JSString *str = Int32ToString(cx, JSID_TO_INT(id));
+             JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
              if (!str)
                  return false;
              vals[i].setString(str);
          } else if (JSID_IS_ATOM(id)) {
              vals[i].setString(JSID_TO_STRING(id));
          } else {
              vals[i].setObject(*JSID_TO_OBJECT(id));
          }
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -1827,17 +1827,17 @@ ParallelArrayObject::getGeneric(JSContex
     uint32_t index;
     if (IsDefinitelyIndex(idval, &index))
         return getElement(cx, obj, receiver, index, vp);
 
     Rooted<SpecialId> sid(cx);
     if (ValueIsSpecial(obj, &idval, &sid, cx))
         return getSpecial(cx, obj, receiver, sid, vp);
 
-    JSAtom *atom = ToAtom(cx, idval);
+    JSAtom *atom = ToAtom<CanGC>(cx, idval);
     if (!atom)
         return false;
 
     if (atom->isIndex(&index))
         return getElement(cx, obj, receiver, index, vp);
 
     Rooted<PropertyName*> name(cx, atom->asPropertyName());
     return getProperty(cx, obj, receiver, name, vp);
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -65,17 +65,17 @@ js::CreateRegExpMatchResult(JSContext *c
      *  input:          input string
      *  index:          start index for the match
      */
     RootedObject array(cx, NewDenseEmptyArray(cx));
     if (!array)
         return false;
 
     if (!input) {
-        input = js_NewStringCopyN(cx, chars.get(), length);
+        input = js_NewStringCopyN<CanGC>(cx, chars.get(), length);
         if (!input)
             return false;
     }
 
     RegExpMatchBuilder builder(cx, array);
     RootedValue undefinedValue(cx, UndefinedValue());
 
     size_t numPairs = matches.length();
@@ -280,17 +280,17 @@ CompileRegExpObject(JSContext *cx, RegEx
     if (sourceValue.isUndefined()) {
         source = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
         JSString *str = ToString(cx, sourceValue);
         if (!str)
             return false;
 
-        source = AtomizeString(cx, str);
+        source = AtomizeString<CanGC>(cx, str);
         if (!source)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (args.hasDefined(1)) {
         JSString *flagStr = ToString(cx, args[1]);
         if (!flagStr)
new file mode 100644
--- /dev/null
+++ b/js/src/config/makefiles/java-build.mk
@@ -0,0 +1,37 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# 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 INCLUDED_JAVA_BUILD_MK #{
+
+ifdef RES_FILES #{
+res-dep := .deps-copy-java-res
+
+GENERATED_DIRS += res
+GARBAGE        += $(res-dep)
+
+export:: $(res-dep)
+
+res-dep-preqs := \
+  $(addprefix $(srcdir)/,$(RES_FILES)) \
+  $(call mkdir_deps,res) \
+  $(if $(IS_LANGUAGE_REPACK),FORCE) \
+  $(NULL)
+
+# nop-build: only copy res/ files when needed
+$(res-dep): $(res-dep-preqs)
+	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
+	@$(TOUCH) $@
+endif #}
+
+
+ifdef JAVAFILES #{
+GENERATED_DIRS += classes
+endif #}
+
+INCLUDED_JAVA_BUILD_MK := 1
+
+endif #} INCLUDED_JAVA_BUILD_MK
--- a/js/src/config/makefiles/makeutils.mk
+++ b/js/src/config/makefiles/makeutils.mk
@@ -107,8 +107,11 @@ endif #}
 ###########################################################################
 ## Common makefile library loader
 ###########################################################################
 topORerr =$(if $(topsrcdir),$(topsrcdir),$(error topsrcdir is not defined))
 
 ifdef USE_AUTOTARGETS_MK # mkdir_deps
   include $(topORerr)/config/makefiles/autotargets.mk
 endif
+
+## copy(src, dst): recursive copy
+copy_dir = (cd $(1)/. && $(TAR) $(TAR_CREATE_FLAGS_QUIET) - .) | (cd $(2)/. && tar -xf -)
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -1095,16 +1095,20 @@ ifeq ($(HOST_OS_ARCH),WINNT)
 #  on it, then merge with the rest of the path.
 root-path = $(shell echo $(1) | sed -e "s|\(/[^/]*\)/\?\(.*\)|\1|")
 non-root-path = $(shell echo $(1) | sed -e "s|\(/[^/]*\)/\?\(.*\)|\2|")
 normalizepath = $(foreach p,$(1),$(if $(filter /%,$(1)),$(patsubst %/,%,$(shell cd $(call root-path,$(1)) && pwd -W))/$(call non-root-path,$(1)),$(1)))
 else
 normalizepath = $(1)
 endif
 
+ifneq (,$(value JAVAFILES)$(value RESFILES))
+  include $(topsrcdir)/config/makefiles/java-build.mk
+endif
+
 _srcdir = $(call normalizepath,$(srcdir))
 ifdef JAVA_SOURCEPATH
 SP = $(subst $(SPACE),$(SEP),$(call normalizepath,$(strip $(JAVA_SOURCEPATH))))
 _JAVA_SOURCEPATH = ".$(SEP)$(_srcdir)$(SEP)$(SP)"
 else
 _JAVA_SOURCEPATH = ".$(SEP)$(_srcdir)"
 endif
 
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -135,17 +135,8 @@ LifoAlloc::transferUnusedFrom(LifoAlloc 
      */
 
     if (other->latest->next()) {
         append(other->latest->next(), other->last);
         other->latest->setNext(NULL);
         other->last = other->latest;
     }
 }
-
-bool
-LifoAlloc::ensureUnusedApproximateSlow(size_t n)
-{
-    // This relies on the behavior that releasing a chunk does not immediately free it.
-    LifoAllocScope scope(this);
-    return !!getOrCreateChunk(n);
-}
-
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -177,18 +177,16 @@ class LifoAlloc
         JS_ASSERT(start && end);
         if (last)
             last->setNext(start);
         else
             first = latest = start;
         last = end;
     }
 
-    bool ensureUnusedApproximateSlow(size_t n);
-
   public:
     explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); }
 
     /* Steal allocated chunks from |other|. */
     void steal(LifoAlloc *other) {
         JS_ASSERT(!other->markCount);
         PodCopy((char *) this, (char *) other, sizeof(*this));
         other->reset(defaultChunkSize_);
@@ -241,17 +239,22 @@ class LifoAlloc
         size_t total = 0;
         BumpChunk *chunk = latest;
         while (chunk) {
             total += chunk->unused();
             if (total >= n)
                 return true;
             chunk = chunk->next();
         }
-        return ensureUnusedApproximateSlow(n);
+        BumpChunk *latestBefore = latest;
+        if (!getOrCreateChunk(n))
+            return false;
+        if (latestBefore)
+            latest = latestBefore;
+        return true;
     }
 
     template <typename T>
     T *newArray(size_t count) {
         void *mem = alloc(sizeof(T) * count);
         if (!mem)
             return NULL;
         JS_STATIC_ASSERT(tl::IsPodType<T>::result);
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -133,17 +133,17 @@ frontend::CompileScript(JSContext *cx, H
         globalsc.strict = true;
 
     if (options.compileAndGo) {
         if (source) {
             /*
              * Save eval program source in script->atoms[0] for the
              * eval cache (see EvalCacheLookup in jsobj.cpp).
              */
-            JSAtom *atom = AtomizeString(cx, source);
+            JSAtom *atom = AtomizeString<CanGC>(cx, source);
             jsatomid _;
             if (!atom || !bce.makeAtomIndex(atom, &_))
                 return UnrootedScript(NULL);
         }
 
         if (callerFrame && callerFrame.isFunctionFrame()) {
             /*
              * An eval script in a caller frame needs to have its enclosing
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -82,20 +82,20 @@ FoldType(JSContext *cx, ParseNode *pn, P
                 pn->pn_dval = d;
                 pn->setKind(PNK_NUMBER);
                 pn->setOp(JSOP_DOUBLE);
             }
             break;
 
           case PNK_STRING:
             if (pn->isKind(PNK_NUMBER)) {
-                JSString *str = js_NumberToString(cx, pn->pn_dval);
+                JSString *str = js_NumberToString<CanGC>(cx, pn->pn_dval);
                 if (!str)
                     return false;
-                pn->pn_atom = AtomizeString(cx, str);
+                pn->pn_atom = AtomizeString<CanGC>(cx, str);
                 if (!pn->pn_atom)
                     return false;
                 pn->setKind(PNK_STRING);
                 pn->setOp(JSOP_STRING);
             }
             break;
 
           default:;
@@ -283,17 +283,17 @@ FoldXMLConstants(JSContext *cx, ParseNod
             } else if (accum && pn1 != pn2) {
                 while (pn1->pn_next != pn2) {
                     pn1 = parser->freeTree(pn1);
                     --pn->pn_count;
                 }
                 pn1->setKind(PNK_XMLTEXT);
                 pn1->setOp(JSOP_STRING);
                 pn1->setArity(PN_NULLARY);
-                pn1->pn_atom = AtomizeString(cx, accum);
+                pn1->pn_atom = AtomizeString<CanGC>(cx, accum);
                 if (!pn1->pn_atom)
                     return false;
                 JS_ASSERT(listp != &pn1->pn_next);
                 *listp = pn1;
             }
             listp = &pn2->pn_next;
             pn1 = *listp;
             accum = NULL;
@@ -335,17 +335,17 @@ FoldXMLConstants(JSContext *cx, ParseNod
         JS_ASSERT(*listp == pn1);
         while (pn1->pn_next) {
             pn1 = parser->freeTree(pn1);
             --pn->pn_count;
         }
         pn1->setKind(PNK_XMLTEXT);
         pn1->setOp(JSOP_STRING);
         pn1->setArity(PN_NULLARY);
-        pn1->pn_atom = AtomizeString(cx, accum);
+        pn1->pn_atom = AtomizeString<CanGC>(cx, accum);
         if (!pn1->pn_atom)
             return false;
         JS_ASSERT(listp != &pn1->pn_next);
         *listp = pn1;
     }
 
     if (pn1 && pn->pn_count == 1) {
         /*
@@ -705,33 +705,33 @@ frontend::FoldConstants(JSContext *cx, P
                 length += pn2->pn_atom->length();
             }
 
             /* Allocate a new buffer and string descriptor for the result. */
             jschar *chars = cx->pod_malloc<jschar>(length + 1);
             if (!chars)
                 return false;
             chars[length] = 0;
-            JSString *str = js_NewString(cx, chars, length);
+            JSString *str = js_NewString<CanGC>(cx, chars, length);
             if (!str) {
                 js_free(chars);
                 return false;
             }
 
             /* Fill the buffer, advancing chars and recycling kids as we go. */
             for (pn2 = pn1; pn2; pn2 = parser->freeTree(pn2)) {
                 JSAtom *atom = pn2->pn_atom;
                 size_t length2 = atom->length();
                 js_strncpy(chars, atom->chars(), length2);
                 chars += length2;
             }
             JS_ASSERT(*chars == 0);
 
             /* Atomize the result string and mutate pn to refer to it. */
-            pn->pn_atom = AtomizeString(cx, str);
+            pn->pn_atom = AtomizeString<CanGC>(cx, str);
             if (!pn->pn_atom)
                 return false;
             pn->setKind(PNK_STRING);
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             break;
         }
 
@@ -742,17 +742,17 @@ frontend::FoldConstants(JSContext *cx, P
                 return false;
             if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
                 return true;
             RootedString left(cx, pn1->pn_atom);
             RootedString right(cx, pn2->pn_atom);
             RootedString str(cx, js_ConcatStrings(cx, left, right));
             if (!str)
                 return false;
-            pn->pn_atom = AtomizeString(cx, str);
+            pn->pn_atom = AtomizeString<CanGC>(cx, str);
             if (!pn->pn_atom)
                 return false;
             pn->setKind(PNK_STRING);
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             parser->freeTree(pn1);
             parser->freeTree(pn2);
             break;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5631,17 +5631,17 @@ Parser::memberExpr(bool allowCallSyntax)
                         propExpr->setOp(JSOP_DOUBLE);
                         propExpr->pn_dval = index;
                     } else {
                         name = atom->asPropertyName();
                     }
                 } else if (propExpr->isKind(PNK_NUMBER)) {
                     double number = propExpr->pn_dval;
                     if (number != ToUint32(number)) {
-                        JSAtom *atom = ToAtom(context, DoubleValue(number));
+                        JSAtom *atom = ToAtom<CanGC>(context, DoubleValue(number));
                         if (!atom)
                             return NULL;
                         name = atom->asPropertyName();
                     }
                 }
             }
 
             if (name)
@@ -6646,17 +6646,17 @@ Parser::primaryExpr(TokenKind tt, bool a
             TokenKind ltok = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
             TokenPtr begin = tokenStream.currentToken().pos.begin;
             switch (ltok) {
               case TOK_NUMBER:
                 pn3 = NullaryNode::create(PNK_NUMBER, this);
                 if (!pn3)
                     return NULL;
                 pn3->pn_dval = tokenStream.currentToken().number();
-                atom = ToAtom(context, DoubleValue(pn3->pn_dval));
+                atom = ToAtom<CanGC>(context, DoubleValue(pn3->pn_dval));
                 if (!atom)
                     return NULL;
                 break;
               case TOK_NAME:
                 {
                     atom = tokenStream.currentToken().name();
                     if (atom == context->names().get) {
                         op = JSOP_GETTER;
@@ -6680,30 +6680,30 @@ Parser::primaryExpr(TokenKind tt, bool a
                         atom = tokenStream.currentToken().atom();
 
                         uint32_t index;
                         if (atom->isIndex(&index)) {
                             pn3 = NullaryNode::create(PNK_NUMBER, this);
                             if (!pn3)
                                 return NULL;
                             pn3->pn_dval = index;
-                            atom = ToAtom(context, DoubleValue(pn3->pn_dval));
+                            atom = ToAtom<CanGC>(context, DoubleValue(pn3->pn_dval));
                             if (!atom)
                                 return NULL;
                         } else {
                             pn3 = NameNode::create(PNK_STRING, atom, this, this->pc);
                             if (!pn3)
                                 return NULL;
                         }
                     } else if (tt == TOK_NUMBER) {
                         pn3 = NullaryNode::create(PNK_NUMBER, this);
                         if (!pn3)
                             return NULL;
                         pn3->pn_dval = tokenStream.currentToken().number();
-                        atom = ToAtom(context, DoubleValue(pn3->pn_dval));
+                        atom = ToAtom<CanGC>(context, DoubleValue(pn3->pn_dval));
                         if (!atom)
                             return NULL;
                     } else {
                         tokenStream.ungetToken();
                         pn3 = NullaryNode::create(PNK_NAME, this);
                         if (!pn3)
                             return NULL;
                         pn3->pn_atom = atom;
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1051,18 +1051,18 @@ TokenStream::getXMLMarkup(TokenKind *ttp
         }
         if (targetLength == 0)
             goto bad_xml_markup;
 
         JSAtom *data;
         if (contentIndex < 0) {
             data = cx->names().empty;
         } else {
-            data = AtomizeChars(cx, tokenbuf.begin() + contentIndex,
-                                tokenbuf.length() - contentIndex);
+            data = AtomizeChars<CanGC>(cx, tokenbuf.begin() + contentIndex,
+                                       tokenbuf.length() - contentIndex);
             if (!data)
                 goto error;
         }
         tokenbuf.shrinkBy(tokenbuf.length() - targetLength);
         consumeKnownChar('>');
         JSAtom *target = atomize(cx, tokenbuf);
         if (!target)
             goto error;
@@ -1279,17 +1279,17 @@ TokenStream::newToken(ptrdiff_t adjust)
     tp->pos.begin.index = tp->ptr - linebase;
     tp->pos.begin.lineno = tp->pos.end.lineno = lineno;
     return tp;
 }
 
 JS_ALWAYS_INLINE JSAtom *
 TokenStream::atomize(JSContext *cx, CharBuffer &cb)
 {
-    return AtomizeChars(cx, cb.begin(), cb.length());
+    return AtomizeChars<CanGC>(cx, cb.begin(), cb.length());
 }
 
 #ifdef DEBUG
 bool
 IsTokenSane(Token *tp)
 {
     /*
      * Nb: TOK_EOL should never be used in an actual Token;  it should only be
@@ -1575,17 +1575,17 @@ TokenStream::getTokenInternal()
 
         /*
          * Identifiers containing no Unicode escapes can be atomized directly
          * from userbuf.  The rest must use the escapes converted via
          * tokenbuf before atomizing.
          */
         JSAtom *atom;
         if (!hadUnicodeEscape)
-            atom = AtomizeChars(cx, identStart, userbuf.addressOfNextRawChar() - identStart);
+            atom = AtomizeChars<CanGC>(cx, identStart, userbuf.addressOfNextRawChar() - identStart);
         else
             atom = atomize(cx, tokenbuf);
         if (!atom)
             goto error;
         tp->setName(JSOP_NAME, atom->asPropertyName());
         tt = TOK_NAME;
         goto out;
     }
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -856,16 +856,20 @@ class FakeRooted : public RootedBase<T>
 template <typename T>
 class FakeMutableHandle : public js::MutableHandleBase<T>
 {
   public:
     FakeMutableHandle(T *t) {
         ptr = t;
     }
 
+    FakeMutableHandle(FakeRooted<T> *root) {
+        ptr = root->address();
+    }
+
     void set(T v) {
         JS_ASSERT(!js::RootMethods<T>::poisoned(v));
         *ptr = v;
     }
 
     T *address() const { return ptr; }
     T get() const { return *ptr; }
 
@@ -882,45 +886,45 @@ class FakeMutableHandle : public js::Mut
 };
 
 /*
  * Types for a variable that either should or shouldn't be rooted, depending on
  * the template parameter Rooted. Used for implementing functions that can
  * operate on either rooted or unrooted data.
  *
  * The toHandle() and toMutableHandle() functions are for calling functions
- * which require handle types and are only called in the ALLOW_GC case. These
+ * which require handle types and are only called in the CanGC case. These
  * allow the calling code to type check.
  */
 enum AllowGC {
-    DONT_ALLOW_GC = 0,
-    ALLOW_GC = 1
+    NoGC = 0,
+    CanGC = 1
 };
 template <typename T, AllowGC allowGC>
 class MaybeRooted
 {
 };
 
-template <typename T> class MaybeRooted<T, ALLOW_GC>
+template <typename T> class MaybeRooted<T, CanGC>
 {
   public:
     typedef Handle<T> HandleType;
     typedef Rooted<T> RootType;
     typedef MutableHandle<T> MutableHandleType;
 
     static inline Handle<T> toHandle(HandleType v) {
         return v;
     }
 
     static inline MutableHandle<T> toMutableHandle(MutableHandleType v) {
         return v;
     }
 };
 
-template <typename T> class MaybeRooted<T, DONT_ALLOW_GC>
+template <typename T> class MaybeRooted<T, NoGC>
 {
   public:
     typedef T HandleType;
     typedef FakeRooted<T> RootType;
     typedef FakeMutableHandle<T> MutableHandleType;
 
     static inline Handle<T> toHandle(HandleType v) {
         JS_NOT_REACHED("Bad conversion");
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -504,17 +504,17 @@ gc::StartVerifyPreBarriers(JSRuntime *rt
     }
 
     rt->gcVerifyPreData = trc;
     rt->gcIncrementalState = MARK;
     rt->gcMarker.start();
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         PurgeJITCaches(c);
         c->setNeedsBarrier(true, JSCompartment::UpdateIon);
-        c->arenas.purge();
+        c->allocator.arenas.purge();
     }
 
     return;
 
 oom:
     rt->gcIncrementalState = NO_INCREMENTAL;
     trc->~VerifyPreTracer();
     js_free(trc);
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -76,17 +76,17 @@ IonBailoutIterator::dump() const
 
 static UnrootedScript
 GetBailedJSScript(JSContext *cx)
 {
     AutoAssertNoGC nogc;
 
     // Just after the frame conversion, we can safely interpret the ionTop as JS
     // frame because it targets the bailed JS frame converted to an exit frame.
-    IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->runtime->ionTop);
+    IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->mainThread().ionTop);
     switch (GetCalleeTokenTag(frame->calleeToken())) {
       case CalleeToken_Function: {
         JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
         return fun->nonLazyScript();
       }
       case CalleeToken_Script:
         return CalleeTokenToScript(frame->calleeToken());
       default:
@@ -360,17 +360,17 @@ EnsureExitFrame(IonCommonFrameLayout *fr
 }
 
 uint32_t
 ion::Bailout(BailoutStack *sp)
 {
     AutoAssertNoGC nogc;
     JSContext *cx = GetIonContext()->cx;
     // We don't have an exit frame.
-    cx->runtime->ionTop = NULL;
+    cx->mainThread().ionTop = NULL;
     IonActivationIterator ionActivations(cx);
     IonBailoutIterator iter(ionActivations, sp);
     IonActivation *activation = ionActivations.activation();
 
     // IonCompartment *ioncompartment = cx->compartment->ionCompartment();
     // IonActivation *activation = cx->runtime->ionActivation;
     // FrameRecovery in = FrameRecoveryFromBailout(ioncompartment, sp);
 
@@ -386,17 +386,17 @@ uint32_t
 ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut)
 {
     AutoAssertNoGC nogc;
     sp->checkInvariants();
 
     JSContext *cx = GetIonContext()->cx;
 
     // We don't have an exit frame.
-    cx->runtime->ionTop = NULL;
+    cx->mainThread().ionTop = NULL;
     IonActivationIterator ionActivations(cx);
     IonBailoutIterator iter(ionActivations, sp);
     IonActivation *activation = ionActivations.activation();
 
     IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     // Note: the frame size must be computed before we return from this function.
     *frameSizeOut = iter.topFrameSize();
@@ -458,17 +458,17 @@ ReflowArgTypes(JSContext *cx)
     for (unsigned i = 0; i < nargs; ++i)
         types::TypeScript::SetArgument(cx, script, i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING));
 }
 
 uint32_t
 ion::ReflowTypeInfo(uint32_t bailoutResult)
 {
     JSContext *cx = GetIonContext()->cx;
-    IonActivation *activation = cx->runtime->ionActivation;
+    IonActivation *activation = cx->mainThread().ionActivation;
 
     IonSpew(IonSpew_Bailouts, "reflowing type info");
 
     if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) {
         IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry");
         ReflowArgTypes(cx);
         return true;
     }
@@ -584,17 +584,17 @@ ion::CachedShapeGuardFailure()
 
     return Invalidate(cx, script);
 }
 
 uint32_t
 ion::ThunkToInterpreter(Value *vp)
 {
     JSContext *cx = GetIonContext()->cx;
-    IonActivation *activation = cx->runtime->ionActivation;
+    IonActivation *activation = cx->mainThread().ionActivation;
     BailoutClosure *br = activation->takeBailout();
     InterpMode resumeMode = JSINTERP_BAILOUT;
 
     if (!EnsureHasScopeObjects(cx, cx->fp()))
         resumeMode = JSINTERP_RETHROW;
 
     // By default we set the forbidOsr flag on the ion script, but if a GC
     // happens just after we re-enter the interpreter, the ion script get
--- a/js/src/ion/BaselineCompiler.cpp
+++ b/js/src/ion/BaselineCompiler.cpp
@@ -267,17 +267,17 @@ BaselineCompiler::initScopeChain()
 
     return true;
 }
 
 bool
 BaselineCompiler::emitStackCheck()
 {
     Label skipIC;
-    uintptr_t *limitAddr = &cx->runtime->ionStackLimit;
+    uintptr_t *limitAddr = &cx->runtime->mainThread.ionStackLimit;
     masm.loadPtr(AbsoluteAddress(limitAddr), R0.scratchReg());
     masm.branchPtr(Assembler::AboveOrEqual, BaselineStackReg, R0.scratchReg(), &skipIC);
 
     ICStackCheck_Fallback::Compiler stubCompiler(cx);
     if (!emitIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     masm.bind(&skipIC);
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -700,42 +700,46 @@ DoCompareFallback(JSContext *cx, ICCompa
                   MutableHandleValue ret)
 {
     RootedScript script(cx, GetTopIonJSScript(cx));
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
 
     FallbackICSpew(cx, stub, "Compare(%s)", js_CodeName[op]);
 
+    // Don't pass lhs/rhs directly, we need the original values when
+    // generating stubs.
+    RootedValue lhsCopy(cx, lhs);
+    RootedValue rhsCopy(cx, rhs);
+
     // Perform the compare operation.
     JSBool out;
-
     switch(op) {
       case JSOP_LT:
-        if (!LessThan(cx, lhs, rhs, &out))
+        if (!LessThan(cx, &lhsCopy, &rhsCopy, &out))
             return false;
         break;
       case JSOP_LE:
-        if (!LessThanOrEqual(cx, lhs, rhs, &out))
+        if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
             return false;
         break;
       case JSOP_GT:
-        if (!GreaterThan(cx, lhs, rhs, &out))
+        if (!GreaterThan(cx, &lhsCopy, &rhsCopy, &out))
             return false;
         break;
       case JSOP_GE:
-        if (!GreaterThanOrEqual(cx, lhs, rhs, &out))
+        if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, &out))
             return false;
         break;
       case JSOP_EQ:
-        if (!LooselyEqual<true>(cx, lhs, rhs, &out))
+        if (!LooselyEqual<true>(cx, &lhsCopy, &rhsCopy, &out))
             return false;
         break;
       case JSOP_NE:
-        if (!LooselyEqual<false>(cx, lhs, rhs, &out))
+        if (!LooselyEqual<false>(cx, &lhsCopy, &rhsCopy, &out))
             return false;
         break;
       default:
         JS_ASSERT(!"Unhandled baseline compare op");
         return false;
     }
 
     ret.setBoolean(out);
@@ -1205,17 +1209,19 @@ ICUnaryArith_Double::Compiler::generateS
 //
 
 static bool
 DoGetElemFallback(JSContext *cx, ICGetElem_Fallback *stub, HandleValue lhs, HandleValue rhs, MutableHandleValue res)
 {
     RootedScript script(cx, GetTopIonJSScript(cx));
     FallbackICSpew(cx, stub, "GetElem");
 
-    if (!GetElementMonitored(cx, lhs, rhs, res))
+    // Don't pass lhs directly, we need it when generating stubs.
+    RootedValue lhsCopy(cx, lhs);
+    if (!GetElementMonitored(cx, &lhsCopy, rhs, res))
         return false;
 
     if (stub->numOptimizedStubs() >= ICGetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -392,17 +392,17 @@ CodeGenerator::visitPolyInlineDispatch(L
             }
         }
     }
     return true;
 }
 
 typedef JSFlatString *(*IntToStringFn)(JSContext *, int);
 static const VMFunction IntToStringInfo =
-    FunctionInfo<IntToStringFn>(Int32ToString);
+    FunctionInfo<IntToStringFn>(Int32ToString<CanGC>);
 
 bool
 CodeGenerator::visitIntToString(LIntToString *lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
 
     OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input),
@@ -1483,17 +1483,17 @@ CodeGenerator::visitCheckOverRecursed(LC
     // Ion may legally place frames very close to the limit. Calling additional
     // C functions may then violate the limit without any checking.
 
     JSRuntime *rt = gen->compartment->rt;
     Register limitReg = ToRegister(lir->limitTemp());
 
     // Since Ion frames exist on the C stack, the stack limit may be
     // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
-    uintptr_t *limitAddr = &rt->ionStackLimit;
+    uintptr_t *limitAddr = &rt->mainThread.ionStackLimit;
     masm.loadPtr(AbsoluteAddress(limitAddr), limitReg);
 
     CheckOverRecursedFailure *ool = new CheckOverRecursedFailure(lir);
     if (!addOutOfLineCode(ool))
         return false;
 
     // Conditional forward (unlikely) branch to failure.
     masm.branchPtr(Assembler::BelowOrEqual, StackPointer, limitReg, ool->entry());
@@ -2398,17 +2398,17 @@ CodeGenerator::visitCompareS(LCompareS *
     masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp);
     masm.branchPtr(Assembler::Equal, output, temp, ool->entry());
     masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
-typedef bool (*CompareFn)(JSContext *, HandleValue, HandleValue, JSBool *);
+typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, JSBool *);
 static const VMFunction EqInfo = FunctionInfo<CompareFn>(ion::LooselyEqual<true>);
 static const VMFunction NeInfo = FunctionInfo<CompareFn>(ion::LooselyEqual<false>);
 static const VMFunction StrictEqInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<true>);
 static const VMFunction StrictNeInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<false>);
 static const VMFunction LtInfo = FunctionInfo<CompareFn>(ion::LessThan);
 static const VMFunction LeInfo = FunctionInfo<CompareFn>(ion::LessThanOrEqual);
 static const VMFunction GtInfo = FunctionInfo<CompareFn>(ion::GreaterThan);
 static const VMFunction GeInfo = FunctionInfo<CompareFn>(ion::GreaterThanOrEqual);
@@ -3725,17 +3725,17 @@ static const VMFunction GetPropertyInfo 
 bool
 CodeGenerator::visitCallGetProperty(LCallGetProperty *lir)
 {
     pushArg(ImmGCPtr(lir->mir()->name()));
     pushArg(ToValue(lir, LCallGetProperty::Value));
     return callVM(GetPropertyInfo, lir);
 }
 
-typedef bool (*GetOrCallElementFn)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
+typedef bool (*GetOrCallElementFn)(JSContext *, MutableHandleValue, HandleValue, MutableHandleValue);
 static const VMFunction GetElementInfo = FunctionInfo<GetOrCallElementFn>(js::GetElement);
 static const VMFunction CallElementInfo = FunctionInfo<GetOrCallElementFn>(js::CallElement);
 
 bool
 CodeGenerator::visitCallGetElement(LCallGetElement *lir)
 {
     pushArg(ToValue(lir, LCallGetElement::RhsInput));
     pushArg(ToValue(lir, LCallGetElement::LhsInput));
@@ -4500,17 +4500,17 @@ CodeGenerator::visitLoadTypedArrayElemen
 
     if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
         return false;
 
     masm.bind(ool->rejoin());
     return true;
 }
 
-typedef bool (*GetElementMonitoredFn)(JSContext *, HandleValue, HandleValue, MutableHandleValue);
+typedef bool (*GetElementMonitoredFn)(JSContext *, MutableHandleValue, HandleValue, MutableHandleValue);
 static const VMFunction GetElementMonitoredInfo =
     FunctionInfo<GetElementMonitoredFn>(js::GetElementMonitored);
 
 bool
 CodeGenerator::visitOutOfLineLoadTypedArray(OutOfLineLoadTypedArray *ool)
 {
     LLoadTypedArrayElementHole *ins = ool->ins();
     saveLive(ins);
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -15,19 +15,21 @@
 #include "LIR.h"
 #include "AliasAnalysis.h"
 #include "LICM.h"
 #include "ValueNumbering.h"
 #include "EdgeCaseAnalysis.h"
 #include "RangeAnalysis.h"
 #include "LinearScan.h"
 #include "jscompartment.h"
-#include "jsworkers.h"
+#include "vm/ThreadPool.h"
+#include "vm/ForkJoin.h"
 #include "IonCompartment.h"
 #include "CodeGenerator.h"
+#include "jsworkers.h"
 #include "BacktrackingAllocator.h"
 #include "StupidAllocator.h"
 #include "UnreachableCodeElimination.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/Lowering-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/Lowering-x64.h"
@@ -116,16 +118,20 @@ IonContext::~IonContext()
 bool
 ion::InitializeIon()
 {
 #ifdef JS_THREADSAFE
     if (!IonTLSInitialized) {
         PRStatus status = PR_NewThreadPrivateIndex(&IonTLSIndex, NULL);
         if (status != PR_SUCCESS)
             return false;
+
+        if (!ForkJoinSlice::Initialize())
+            return false;
+
         IonTLSInitialized = true;
     }
 #endif
     CheckLogging();
     return true;
 }
 
 IonRuntime::IonRuntime()
@@ -228,16 +234,18 @@ IonCompartment::initialize(JSContext *cx
     if (!stubCodes_ || !stubCodes_->init())
         return false;
     return true;
 }
 
 void
 ion::FinishOffThreadBuilder(IonBuilder *builder)
 {
+    JS_ASSERT(builder->info().executionMode() == SequentialExecution);
+
     // Clean up if compilation did not succeed.
     if (builder->script()->isIonCompilingOffThread()) {
         types::TypeCompartment &types = builder->script()->compartment()->types;
         builder->recompileInfo.compilerOutput(types)->invalidate();
         builder->script()->ion = NULL;
     }
 
     // The builder is allocated into its LifoAlloc, so destroying that will
@@ -301,48 +309,48 @@ IonCompartment::getVMWrapper(const VMFun
     JS_ASSERT(p);
 
     return p->value;
 }
 
 IonActivation::IonActivation(JSContext *cx, StackFrame *fp)
   : cx_(cx),
     compartment_(cx->compartment),
-    prev_(cx->runtime->ionActivation),
+    prev_(cx->mainThread().ionActivation),
     entryfp_(fp),
     bailout_(NULL),
-    prevIonTop_(cx->runtime->ionTop),
-    prevIonJSContext_(cx->runtime->ionJSContext),
+    prevIonTop_(cx->mainThread().ionTop),
+    prevIonJSContext_(cx->mainThread().ionJSContext),
     prevpc_(NULL)
 {
     if (fp)
         fp->setRunningInIon();
-    cx->runtime->ionJSContext = cx;
-    cx->runtime->ionActivation = this;
-    cx->runtime->ionStackLimit = cx->runtime->nativeStackLimit;
+    cx->mainThread().ionJSContext = cx;
+    cx->mainThread().ionActivation = this;
+    cx->mainThread().ionStackLimit = cx->mainThread().nativeStackLimit;
 }
 
 IonActivation::~IonActivation()
 {
-    JS_ASSERT(cx_->runtime->ionActivation == this);
+    JS_ASSERT(cx_->mainThread().ionActivation == this);
     JS_ASSERT(!bailout_);
 
     if (entryfp_)
         entryfp_->clearRunningInIon();
-    cx_->runtime->ionActivation = prev();
-    cx_->runtime->ionTop = prevIonTop_;
-    cx_->runtime->ionJSContext = prevIonJSContext_;
+    cx_->mainThread().ionActivation = prev();
+    cx_->mainThread().ionTop = prevIonTop_;
+    cx_->mainThread().ionJSContext = prevIonJSContext_;
 }
 
 IonCode *
 IonCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
 {
     AssertCanGC();
 
-    IonCode *codeObj = gc::NewGCThing<IonCode, ALLOW_GC>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode));
+    IonCode *codeObj = gc::NewGCThing<IonCode, CanGC>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode));
     if (!codeObj) {
         pool->release();
         return NULL;
     }
 
     new (codeObj) IonCode(code, bufferSize, pool);
     return codeObj;
 }
@@ -1255,42 +1263,42 @@ TestIonCompile(JSContext *cx, HandleScri
         return Method_CantCompile;
     }
 
     JS_ASSERT(reason == AbortReason_NoAbort);
     return Method_Compiled;
 }
 
 static bool
-CheckFrame(StackFrame *fp)
+CheckFrame(AbstractFramePtr fp)
 {
-    if (fp->isEvalFrame()) {
+    if (fp.isEvalFrame()) {
         // Eval frames are not yet supported. Supporting this will require new
         // logic in pushBailoutFrame to deal with linking prev.
         // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
         IonSpew(IonSpew_Abort, "eval frame");
         return false;
     }
 
-    if (fp->isGeneratorFrame()) {
+    if (fp.isGeneratorFrame()) {
         // Err... no.
         IonSpew(IonSpew_Abort, "generator frame");
         return false;
     }
 
-    if (fp->isDebuggerFrame()) {
+    if (fp.isDebuggerFrame()) {
         IonSpew(IonSpew_Abort, "debugger frame");
         return false;
     }
 
     // This check is to not overrun the stack. Eventually, we will want to
     // handle this when we support JSOP_ARGUMENTS or function calls.
-    if (fp->isFunctionFrame() &&
-        (fp->numActualArgs() >= SNAPSHOT_MAX_NARGS ||
-         fp->numActualArgs() > js_IonOptions.maxStackArgs))
+    if (fp.isFunctionFrame() &&
+        (fp.numActualArgs() >= SNAPSHOT_MAX_NARGS ||
+         fp.numActualArgs() > js_IonOptions.maxStackArgs))
     {
         IonSpew(IonSpew_Abort, "too many actual args");
         return false;
     }
 
     return true;
 }
 
@@ -1409,17 +1417,18 @@ Compile(JSContext *cx, HandleScript scri
 }
 
 } // namespace ion
 } // namespace js
 
 // Decide if a transition from interpreter execution to Ion code should occur.
 // May compile or recompile the target JSScript.
 MethodStatus
-ion::CanEnterAtBranch(JSContext *cx, HandleScript script, StackFrame *fp, jsbytecode *pc)
+ion::CanEnterAtBranch(JSContext *cx, HandleScript script, AbstractFramePtr fp,
+                      jsbytecode *pc, bool isConstructing)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
 
     // Skip if the script has been disabled.
     if (script->ion == ION_DISABLED_SCRIPT)
         return Method_Skipped;
 
@@ -1437,32 +1446,33 @@ ion::CanEnterAtBranch(JSContext *cx, Han
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    RootedFunction fun(cx, fp->isFunctionFrame() ? fp->fun() : NULL);
-    MethodStatus status = Compile(cx, script, fun, pc, fp->isConstructing());
+    RootedFunction fun(cx, fp.isFunctionFrame() ? fp.fun() : NULL);
+    MethodStatus status = Compile(cx, script, fun, pc, isConstructing);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     if (script->ion->osrPc() != pc)
         return Method_Skipped;
 
     return Method_Compiled;
 }
 
 MethodStatus
-ion::CanEnter(JSContext *cx, HandleScript script, StackFrame *fp, bool newType)
+ion::CanEnter(JSContext *cx, HandleScript script, AbstractFramePtr fp,
+              bool isConstructing, bool newType)
 {
     JS_ASSERT(ion::IsEnabled(cx));
 
     // Skip if the script has been disabled.
     if (script->ion == ION_DISABLED_SCRIPT)
         return Method_Skipped;
 
     // Skip if the script is being compiled off thread.
@@ -1471,33 +1481,33 @@ ion::CanEnter(JSContext *cx, HandleScrip
 
     // Skip if the code is expected to result in a bailout.
     if (script->ion && script->ion->bailoutExpected())
         return Method_Skipped;
 
     // If constructing, allocate a new |this| object before building Ion.
     // Creating |this| is done before building Ion because it may change the
     // type information and invalidate compilation results.
-    if (fp->isConstructing() && fp->functionThis().isPrimitive()) {
-        RootedObject callee(cx, &fp->callee());
+    if (isConstructing && fp.thisValue().isPrimitive()) {
+        RootedObject callee(cx, fp.callee());
         RootedObject obj(cx, js_CreateThisForFunction(cx, callee, newType));
         if (!obj)
             return Method_Skipped;
-        fp->functionThis().setObject(*obj);
+        fp.thisValue().setObject(*obj);
     }
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    RootedFunction fun(cx, fp->isFunctionFrame() ? fp->fun() : NULL);
-    MethodStatus status = Compile(cx, script, fun, NULL, fp->isConstructing());
+    RootedFunction fun(cx, fp.isFunctionFrame() ? fp.fun() : NULL);
+    MethodStatus status = Compile(cx, script, fun, NULL, isConstructing);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -248,18 +248,19 @@ bool InitializeIon();
 // Get and set the current Ion context.
 IonContext *GetIonContext();
 
 bool SetIonContext(IonContext *ctx);
 
 bool CanIonCompileScript(JSContext *cx, UnrootedScript script);
 
 MethodStatus CanEnterAtBranch(JSContext *cx, HandleScript script,
-                              StackFrame *fp, jsbytecode *pc);
-MethodStatus CanEnter(JSContext *cx, HandleScript script, StackFrame *fp, bool newType);
+                              AbstractFramePtr fp, jsbytecode *pc, bool isConstructing);
+MethodStatus CanEnter(JSContext *cx, HandleScript script, AbstractFramePtr fp,
+                      bool isConstructing, bool newType);
 MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs);
 
 enum IonExecStatus
 {
     // The method call had to be aborted due to a stack limit check. This
     // error indicates that Ion never attempted to clean up frames.
     IonExec_Aborted,
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -1731,17 +1731,17 @@ js::ion::GetElementCache(JSContext *cx, 
     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
     IonCacheGetElement &cache = ion->getCache(cacheIndex).toGetElement();
     RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
     RootedValue lval(cx, ObjectValue(*obj));
 
     if (cache.isDisabled()) {
-        if (!GetElementOperation(cx, JSOp(*pc), lval, idval, res))
+        if (!GetElementOperation(cx, JSOp(*pc), &lval, idval, res))
             return false;
         types::TypeScript::Monitor(cx, script, pc, res);
         return true;
     }
 
     // Override the return value if we are invalidated (bug 728188).
     AutoFlushCache afc ("GetElementCache");
     AutoDetectInvalidation adi(cx, res.address(), ion);
@@ -1766,17 +1766,17 @@ js::ion::GetElementCache(JSContext *cx, 
             cache.incrementStubCount();
 
             if (!cache.attachDenseElement(cx, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
     }
 
-    if (!GetElementOperation(cx, JSOp(*pc), lval, idval, res))
+    if (!GetElementOperation(cx, JSOp(*pc), &lval, idval, res))
         return false;
 
     // If no new attach was done, and we've reached maximum number of stubs, then
     // disable the cache.
     if (!attachedStub && cache.stubCount() >= MAX_STUBS)
         cache.disable();
 
     types::TypeScript::Monitor(cx, script, pc, res);
--- a/js/src/ion/IonFrames-inl.h
+++ b/js/src/ion/IonFrames-inl.h
@@ -83,17 +83,17 @@ IonFrameIterator::frameSize() const
     return frameSize_;
 }
 
 // Returns the JSScript associated with the topmost Ion frame.
 inline UnrootedScript
 GetTopIonJSScript(JSContext *cx, const SafepointIndex **safepointIndexOut, void **returnAddrOut)
 {
     AutoAssertNoGC nogc;
-    IonFrameIterator iter(cx->runtime->ionTop);
+    IonFrameIterator iter(cx->mainThread().ionTop);
     JS_ASSERT(iter.type() == IonFrame_Exit);
     ++iter;
 
     // If needed, grab the safepoint index.
     if (safepointIndexOut)
         *safepointIndexOut = iter.safepoint();
 
     JS_ASSERT(iter.returnAddressToFp() != NULL);
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -407,26 +407,26 @@ ion::HandleException(ResumeFromException
     JSContext *cx = GetIonContext()->cx;
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
 
     IonSpew(IonSpew_Invalidate, "handling exception");
 
     // Immediately remove any bailout frame guard that might be left over from
     // an error in between ConvertFrames and ThunkToInterpreter.
-    js_delete(cx->runtime->ionActivation->maybeTakeBailout());
+    js_delete(cx->mainThread().ionActivation->maybeTakeBailout());
 
     // Clear any Ion return override that's been set.
     // This may happen if a callVM function causes an invalidation (setting the
     // override), and then fails, bypassing the bailout handlers that would
     // otherwise clear the return override.
     if (cx->runtime->hasIonReturnOverride())
         cx->runtime->takeIonReturnOverride();
 
-    IonFrameIterator iter(cx->runtime->ionTop);
+    IonFrameIterator iter(cx->mainThread().ionTop);
     while (!iter.isEntry()) {
         if (iter.isOptimizedJS()) {
             // Search each inlined frame for live iterator objects, and close
             // them.
             InlineFrameIterator frames(cx, &iter);
             for (;;) {
                 CloseLiveIterators(cx, frames);
 
@@ -477,25 +477,25 @@ IonActivationIterator::settle()
 {
     while (activation_ && activation_->empty()) {
         top_ = activation_->prevIonTop();
         activation_ = activation_->prev();
     }
 }
 
 IonActivationIterator::IonActivationIterator(JSContext *cx)
-  : top_(cx->runtime->ionTop),
-    activation_(cx->runtime->ionActivation)
+  : top_(cx->mainThread().ionTop),
+    activation_(cx->mainThread().ionActivation)
 {
     settle();
 }
 
 IonActivationIterator::IonActivationIterator(JSRuntime *rt)
-  : top_(rt->ionTop),
-    activation_(rt->ionActivation)
+  : top_(rt->mainThread.ionTop),
+    activation_(rt->mainThread.ionActivation)
 {
     settle();
 }
 
 IonActivationIterator &
 IonActivationIterator::operator++()
 {
     JS_ASSERT(activation_);
@@ -819,17 +819,17 @@ void
 ion::GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
 {
     JS_ASSERT(cx->fp()->beginsIonActivation());
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
 
     JSRuntime *rt = cx->runtime;
 
     // Recover the return address.
-    IonFrameIterator it(rt->ionTop);
+    IonFrameIterator it(rt->mainThread.ionTop);
     uint8_t *retAddr = it.returnAddress();
     uint32_t hash = PcScriptCache::Hash(retAddr);
     JS_ASSERT(retAddr != NULL);
 
     // Lazily initialize the cache. The allocation may safely fail and will not GC.
     if (JS_UNLIKELY(rt->ionPcScriptCache == NULL)) {
         rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache));
         if (rt->ionPcScriptCache)
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsinfer.h"
 #include "jsinferinlines.h"
 #include "BaselineJIT.h"
 #include "IonMacroAssembler.h"
 #include "gc/Root.h"
 #include "Bailouts.h"
+#include "vm/ForkJoin.h"
 
 using namespace js;
 using namespace js::ion;
 
 template <typename T> void
 MacroAssembler::guardTypeSet(const T &address, const types::TypeSet *types,
                              Register scratch, Label *mismatched)
 {
@@ -317,17 +318,17 @@ MacroAssembler::newGCThing(const Registe
     branch32(Assembler::NotEqual, result, Imm32(0), fail);
 #endif
 
     // Inline FreeSpan::allocate.
     // There is always exactly one FreeSpan per allocKind per JSCompartment.
     // If a FreeSpan is replaced, its members are updated in the freeLists table,
     // which the code below always re-reads.
     gc::FreeSpan *list = const_cast<gc::FreeSpan *>
-                         (compartment->arenas.getFreeList(allocKind));
+                         (compartment->allocator.arenas.getFreeList(allocKind));
     loadPtr(AbsoluteAddress(&list->first), result);
     branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result, fail);
 
     addPtr(Imm32(thingSize), result);
     storePtr(result, AbsoluteAddress(&list->first));
     subPtr(Imm32(thingSize), result);
 }
 
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -157,21 +157,21 @@ class MacroAssembler : public MacroAssem
 
     void loadStringLength(Register str, Register dest) {
         loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
         rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
     }
 
     void loadJSContext(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->compartment->rt), dest);
-        loadPtr(Address(dest, offsetof(JSRuntime, ionJSContext)), dest);
+        loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionJSContext)), dest);
     }
     void loadIonActivation(const Register &dest) {
         movePtr(ImmWord(GetIonContext()->compartment->rt), dest);
-        loadPtr(Address(dest, offsetof(JSRuntime, ionActivation)), dest);
+        loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionActivation)), dest);
     }
 
     template<typename T>
     void loadTypedOrValue(const T &src, TypedOrValueRegister dest) {
         if (dest.hasValue())
             loadValue(src, dest.valueReg());
         else
             loadUnboxedValue(src, dest.type(), dest.typedReg());
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -114,17 +114,17 @@ InvokeFunction(JSContext *cx, HandleFunc
         types::TypeScript::Monitor(cx, *rval);
 
     return ok;
 }
 
 JSObject *
 NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize)
 {
-    return gc::NewGCThing<JSObject, ALLOW_GC>(cx, allocKind, thingSize);
+    return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize);
 }
 
 bool
 CheckOverRecursed(JSContext *cx)
 {
     // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
     // want to trigger an operation callback, we set the ionStackLimit to NULL,
     // which causes the stack limit check to fail.
@@ -165,74 +165,74 @@ InitProp(JSContext *cx, HandleObject obj
 
     if (name == cx->names().proto)
         return baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, false);
     return DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0);
 }
 
 template<bool Equal>
 bool
-LooselyEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
+LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     bool equal;
     if (!js::LooselyEqual(cx, lhs, rhs, &equal))
         return false;
     *res = (equal == Equal);
     return true;
 }
 
-template bool LooselyEqual<true>(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
-template bool LooselyEqual<false>(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
+template bool LooselyEqual<true>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
+template bool LooselyEqual<false>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
 
 template<bool Equal>
 bool
-StrictlyEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
+StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     bool equal;
     if (!js::StrictlyEqual(cx, lhs, rhs, &equal))
         return false;
     *res = (equal == Equal);
     return true;
 }
 
-template bool StrictlyEqual<true>(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
-template bool StrictlyEqual<false>(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
+template bool StrictlyEqual<true>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
+template bool StrictlyEqual<false>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
 
 bool
-LessThan(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
+LessThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     bool cond;
     if (!LessThanOperation(cx, lhs, rhs, &cond))
         return false;
     *res = cond;
     return true;
 }
 
 bool
-LessThanOrEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
+LessThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     bool cond;
     if (!LessThanOrEqualOperation(cx, lhs, rhs, &cond))
         return false;
     *res = cond;
     return true;
 }
 
 bool
-GreaterThan(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
+GreaterThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     bool cond;
     if (!GreaterThanOperation(cx, lhs, rhs, &cond))
         return false;
     *res = cond;
     return true;
 }
 
 bool
-GreaterThanOrEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
+GreaterThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     bool cond;
     if (!GreaterThanOrEqualOperation(cx, lhs, rhs, &cond))
         return false;
     *res = cond;
     return true;
 }
 
@@ -400,18 +400,17 @@ CharCodeAt(JSContext *cx, HandleString s
 JSFlatString *
 StringFromCharCode(JSContext *cx, int32_t code)
 {
     jschar c = jschar(code);
 
     if (StaticStrings::hasUnit(c))
         return cx->runtime->staticStrings.getUnit(c);
 
-    return js_NewStringCopyN(cx, &c, 1);
-
+    return js_NewStringCopyN<CanGC>(cx, &c, 1);
 }
 
 bool
 SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
             bool strict, bool isSetName)
 {
     RootedValue v(cx, value);
     RootedId id(cx, NameToId(name));
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -418,25 +418,25 @@ bool InvokeFunction(JSContext *cx, Handl
 JSObject *NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize);
 
 bool CheckOverRecursed(JSContext *cx);
 
 bool DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
 bool InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value);
 
 template<bool Equal>
-bool LooselyEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
+bool LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
 
 template<bool Equal>
-bool StrictlyEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
+bool StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
 
-bool LessThan(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
-bool LessThanOrEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
-bool GreaterThan(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
-bool GreaterThanOrEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res);
+bool LessThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
+bool LessThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
+bool GreaterThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
+bool GreaterThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
 
 template<bool Equal>
 bool StringsEqual(JSContext *cx, HandleString left, HandleString right, JSBool *res);
 
 JSBool ObjectEmulatesUndefined(RawObject obj);
 
 bool IteratorMore(JSContext *cx, HandleObject obj, JSBool *res);
 
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -2669,17 +2669,17 @@ MacroAssemblerARMCompat::storeTypeTag(Im
     ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
     ma_mov(tag, ScratchRegister);
     ma_str(ScratchRegister, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
     ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
 }
 
 void
 MacroAssemblerARMCompat::linkExitFrame() {
-    uint8_t *dest = ((uint8_t*)GetIonContext()->compartment->rt) + offsetof(JSRuntime, ionTop);
+    uint8_t *dest = ((uint8_t*)GetIonContext()->compartment->rt) + offsetof(JSRuntime, mainThread.ionTop);
     movePtr(ImmWord(dest), ScratchRegister);
     ma_str(StackPointer, Operand(ScratchRegister, 0));
 }
 
 // ARM says that all reads of pc will return 8 higher than the
 // address of the currently executing instruction.  This means we are
 // correctly storing the address of the instruction after the call
 // in the register.
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -934,17 +934,17 @@ class MacroAssemblerX64 : public MacroAs
         shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orq(Imm32(type), frameSizeReg);
     }
 
     // Save an exit frame (which must be aligned to the stack pointer) to
     // ThreadData::ionTop.
     void linkExitFrame() {
         mov(ImmWord(GetIonContext()->compartment->rt), ScratchReg);
-        mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, ionTop)));
+        mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, mainThread.ionTop)));
     }
 
     void callWithExitFrame(IonCode *target, Register dynStack) {
         addPtr(Imm32(framePushed()), dynStack);
         makeFrameDescriptor(dynStack, IonFrame_OptimizedJS);
         Push(dynStack);
         call(target);
     }
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -800,17 +800,17 @@ class MacroAssemblerX86 : public MacroAs
         shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orl(Imm32(type), frameSizeReg);
     }
 
     // Save an exit frame (which must be aligned to the stack pointer) to
     // ThreadData::ionTop.
     void linkExitFrame() {
         JSCompartment *compartment = GetIonContext()->compartment;
-        movl(StackPointer, Operand(&compartment->rt->ionTop));
+        movl(StackPointer, Operand(&compartment->rt->mainThread.ionTop));
     }
 
     void callWithExitFrame(IonCode *target, Register dynStack) {
         addPtr(Imm32(framePushed()), dynStack);
         makeFrameDescriptor(dynStack, IonFrame_OptimizedJS);
         Push(dynStack);
         call(target);
     }
--- a/js/src/jsapi-tests/testDebugger.cpp
+++ b/js/src/jsapi-tests/testDebugger.cpp
@@ -8,22 +8,23 @@
 
 #include "tests.h"
 #include "jsdbgapi.h"
 #include "jscntxt.h"
 
 static int callCount[2] = {0, 0};
 
 static void *
-callCountHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
+callCountHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, JSBool before,
+              JSBool *ok, void *closure)
 {
     callCount[before]++;
 
     jsval thisv;
-    JS_GetFrameThis(cx, fp, &thisv);  // assert if fp is incomplete
+    frame.getThisValue(cx, &thisv); // assert if fp is incomplete
 
     return cx;  // any non-null value causes the hook to be called again after
 }
 
 BEGIN_TEST(testDebugger_bug519719)
 {
     CHECK(JS_SetDebugMode(cx, JS_TRUE));
     JS_SetCallHook(rt, callCountHook, NULL);
@@ -33,22 +34,23 @@ BEGIN_TEST(testDebugger_bug519719)
          "f(Math.cos);\n");  // side exit in f -> call
     CHECK_EQUAL(callCount[0], 20);
     CHECK_EQUAL(callCount[1], 20);
     return true;
 }
 END_TEST(testDebugger_bug519719)
 
 static void *
-nonStrictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
+nonStrictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, JSBool before,
+                  JSBool *ok, void *closure)
 {
     if (before) {
         bool *allWrapped = (bool *) closure;
         js::RootedValue thisv(cx);
-        JS_GetFrameThis(cx, fp, thisv.address());
+        frame.getThisValue(cx, thisv.address());
         *allWrapped = *allWrapped && !JSVAL_IS_PRIMITIVE(thisv);
     }
     return NULL;
 }
 
 BEGIN_TEST(testDebugger_getThisNonStrict)
 {
     bool allWrapped = true;
@@ -71,22 +73,23 @@ BEGIN_TEST(testDebugger_getThisNonStrict
          "nonstrict.call({});\n"
          "({}).nonstrict();\n");
     CHECK(allWrapped);
     return true;
 }
 END_TEST(testDebugger_getThisNonStrict)
 
 static void *
-strictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
+strictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, JSBool before,
+               JSBool *ok, void *closure)
 {
     if (before) {
         bool *anyWrapped = (bool *) closure;
         js::RootedValue thisv(cx);
-        JS_GetFrameThis(cx, fp, thisv.address());
+        frame.getThisValue(cx, thisv.address());
         *anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv);
     }
     return NULL;
 }
 
 BEGIN_TEST(testDebugger_getThisStrict)
 {
     bool anyWrapped = false;
--- a/js/src/jsapi-tests/testFindSCCs.cpp
+++ b/js/src/jsapi-tests/testFindSCCs.cpp
@@ -150,17 +150,17 @@ void setup(unsigned count)
 
 void edge(unsigned src_index, unsigned dest_index)
 {
     Vertex[src_index].hasEdge[dest_index] = true;
 }
 
 void run()
 {
-    finder = new ComponentFinder<TestNode>(rt->nativeStackLimit);
+    finder = new ComponentFinder<TestNode>(rt->mainThread.nativeStackLimit);
     for (unsigned i = 0; i < vertex_count; ++i)
         finder->addNode(&Vertex[i]);
     resultsList = finder->getResultsList();
 }
 
 bool group(int vertex, ...)
 {
     TestNode *v = resultsList;
@@ -241,17 +241,17 @@ BEGIN_TEST(testFindSCCsStackLimit)
      */
     const unsigned max = 1000000;
     const unsigned initial = 10;
 
     TestNode2 *vertices = new TestNode2[max]();
     for (unsigned i = initial; i < (max - 10); ++i)
         vertices[i].edge = &vertices[i + 1];
 
-    ComponentFinder<TestNode2> finder(rt->nativeStackLimit);
+    ComponentFinder<TestNode2> finder(rt->mainThread.nativeStackLimit);
     for (unsigned i = 0; i < max; ++i)
         finder.addNode(&vertices[i]);
 
     TestNode2 *r = finder.getResultsList();
     CHECK(r);
     TestNode2 *v = r;
 
     unsigned count = 0;
--- a/js/src/jsapi-tests/testIndexToString.cpp
+++ b/js/src/jsapi-tests/testIndexToString.cpp
@@ -15,17 +15,17 @@
 
 #include "vm/String-inl.h"
 
 using mozilla::ArrayLength;
 
 template<size_t N> JSFlatString *
 NewString(JSContext *cx, const jschar (&chars)[N])
 {
-    return js_NewStringCopyN(cx, chars, N);
+    return js_NewStringCopyN<js::CanGC>(cx, chars, N);
 }
 
 static const struct TestPair {
     uint32_t num;
     const char *expected;
 } tests[] = {
     { 0, "0" },
     { 1, "1" },
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -35,17 +35,17 @@ class AutoInflatedString {
 
     const jschar *chars() const { return chars_; }
     size_t length() const { return length_; }
 };
 
 template<size_t N> JSFlatString *
 NewString(JSContext *cx, const jschar (&chars)[N])
 {
-    return js_NewStringCopyN(cx, chars, N);
+    return js_NewStringCopyN<CanGC>(cx, chars, N);
 }
 
 BEGIN_TEST(testParseJSON_success)
 {
     // Primitives
     CHECK(TryParse(cx, "true", JSVAL_TRUE));
     CHECK(TryParse(cx, "false", JSVAL_FALSE));
     CHECK(TryParse(cx, "null", JSVAL_NULL));
@@ -55,17 +55,17 @@ BEGIN_TEST(testParseJSON_success)
     CHECK(TryParse(cx, "1", DOUBLE_TO_JSVAL(1)));
     CHECK(TryParse(cx, "1.75", DOUBLE_TO_JSVAL(1.75)));
     CHECK(TryParse(cx, "9e9", DOUBLE_TO_JSVAL(9e9)));
     CHECK(TryParse(cx, "9e99999", DOUBLE_TO_JSVAL(std::numeric_limits<double>::infinity())));
 
     js::Rooted<JSFlatString*> str(cx);
 
     const jschar emptystr[] = { '\0' };
-    str = js_NewStringCopyN(cx, emptystr, 0);
+    str = js_NewStringCopyN<CanGC>(cx, emptystr, 0);
     CHECK(str);
     CHECK(TryParse(cx, "\"\"", STRING_TO_JSVAL(str)));
 
     const jschar nullstr[] = { '\0' };
     str = NewString(cx, nullstr);
     CHECK(str);
     CHECK(TryParse(cx, "\"\\u0000\"", STRING_TO_JSVAL(str)));
 
--- a/js/src/jsapi-tests/testStringBuffer.cpp
+++ b/js/src/jsapi-tests/testStringBuffer.cpp
@@ -14,17 +14,17 @@
 
 #include "jsobjinlines.h"
 
 BEGIN_TEST(testStringBuffer_finishString)
 {
     JSString *str = JS_NewStringCopyZ(cx, "foopy");
     CHECK(str);
 
-    js::Rooted<JSAtom*> atom(cx, js::AtomizeString(cx, str));
+    js::Rooted<JSAtom*> atom(cx, js::AtomizeString<js::CanGC>(cx, str));
     CHECK(atom);
 
     js::StringBuffer buffer(cx);
     CHECK(buffer.append("foopy"));
 
     JSAtom *finishedAtom = buffer.finishAtom();
     CHECK(finishedAtom);
     CHECK_EQUAL(atom, finishedAtom);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -715,22 +715,27 @@ JS_FRIEND_API(void) JS::EnterAssertNoGCS
 JS_FRIEND_API(void) JS::LeaveAssertNoGCScope() {}
 JS_FRIEND_API(bool) JS::InNoGCScope() { return false; }
 JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
 JS_FRIEND_API(bool) JS::NeedRelaxedRootChecks() { return false; }
 #endif
 
 static const JSSecurityCallbacks NullSecurityCallbacks = { };
 
-PerThreadData::PerThreadData(JSRuntime *runtime)
-  : runtime_(runtime),
+js::PerThreadData::PerThreadData(JSRuntime *runtime)
+  : PerThreadDataFriendFields(),
+    runtime_(runtime),
 #ifdef DEBUG
     gcRelaxRootChecks(false),
     gcAssertNoGCDepth(0),
 #endif
+    ionTop(NULL),
+    ionJSContext(NULL),
+    ionStackLimit(0),
+    ionActivation(NULL),
     suppressGC(0)
 {}
 
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
   : mainThread(this),
     atomsCompartment(NULL),
 #ifdef JS_THREADSAFE
     ownerThread_(NULL),
@@ -873,20 +878,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     wrapObjectCallback(TransparentObjectWrapper),
     sameCompartmentWrapObjectCallback(NULL),
     preWrapObjectCallback(NULL),
     preserveWrapperCallback(NULL),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     jitHardening(false),
-    ionTop(NULL),
-    ionJSContext(NULL),
-    ionStackLimit(0),
-    ionActivation(NULL),
     ionPcScriptCache(NULL),
     threadPool(this),
     ctypesActivityCallback(NULL),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     useHelperThreads_(useHelperThreads),
     requestedHelperThreadCount(-1),
     rngNonce(0)
 {
@@ -928,17 +929,17 @@ JSRuntime::init(uint32_t maxbytes)
         !atomsCompartment->init(NULL) ||
         !compartments.append(atomsCompartment))
     {
         js_delete(atomsCompartment);
         return false;
     }
 
     atomsCompartment->isSystemCompartment = true;
-    atomsCompartment->setGCLastBytes(8192, 8192, GC_NORMAL);
+    atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
 
     if (!InitAtoms(this))
         return false;
 
     if (!InitRuntimeNumberState(this))
         return false;
 
     dtoaState = js_NewDtoaState();
@@ -1050,19 +1051,19 @@ JSRuntime::clearOwnerThread()
 {
     assertValidThread();
     JS_ASSERT(requestDepth == 0);
     JS_ASSERT(js_NewRuntimeWasCalled);
     ownerThread_ = (void *)0xc1ea12;  /* "clear" */
     js::TlsPerThreadData.set(NULL);
     nativeStackBase = 0;
 #if JS_STACK_GROWTH_DIRECTION > 0
-    nativeStackLimit = UINTPTR_MAX;
+    mainThread.nativeStackLimit = UINTPTR_MAX;
 #else
-    nativeStackLimit = 0;
+    mainThread.nativeStackLimit = 0;
 #endif
 }
 
 JS_FRIEND_API(void)
 JSRuntime::abortIfWrongThread() const
 {
     if (ownerThread_ != PR_GetCurrentThread())
         MOZ_CRASH();
@@ -2258,28 +2259,16 @@ JS_ComputeThis(JSContext *cx, jsval *vp)
     AssertHeapIsIdle(cx);
     assertSameCompartment(cx, JSValueArray(vp, 2));
     CallReceiver call = CallReceiverFromVp(vp);
     if (!BoxNonStrictThis(cx, call))
         return JSVAL_NULL;
     return call.thisv();
 }
 
-JS_PUBLIC_API(void)
-JS_MallocInCompartment(JSCompartment *comp, size_t nbytes)
-{
-    comp->mallocInCompartment(nbytes);
-}
-
-JS_PUBLIC_API(void)
-JS_FreeInCompartment(JSCompartment *comp, size_t nbytes)
-{
-    comp->freeInCompartment(nbytes);
-}
-
 JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return cx->malloc_(nbytes);
 }
 
@@ -2307,17 +2296,17 @@ JS_PUBLIC_API(JSFreeOp *)
 JS_GetDefaultFreeOp(JSRuntime *rt)
 {
     return rt->defaultFreeOp();
 }
 
 JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes)
 {
-    return cx->runtime->updateMallocCounter(cx, nbytes);
+    return cx->runtime->updateMallocCounter(cx->compartment, nbytes);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s)
 {
     AssertHeapIsIdle(cx);
     size_t n = strlen(s) + 1;
     void *p = cx->malloc_(n);
@@ -3095,27 +3084,27 @@ JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSRuntime *rt, size_t stackSize)
 {
     rt->nativeStackQuota = stackSize;
     if (!rt->nativeStackBase)
         return;
 
 #if JS_STACK_GROWTH_DIRECTION > 0
     if (stackSize == 0) {
-        rt->nativeStackLimit = UINTPTR_MAX;
+        rt->mainThread.nativeStackLimit = UINTPTR_MAX;
     } else {
         JS_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
-        rt->nativeStackLimit = rt->nativeStackBase + stackSize - 1;
+        rt->mainThread.nativeStackLimit = rt->nativeStackBase + stackSize - 1;
     }
 #else
     if (stackSize == 0) {
-        rt->nativeStackLimit = 0;
+        rt->mainThread.nativeStackLimit = 0;
     } else {
         JS_ASSERT(rt->nativeStackBase >= stackSize);
-        rt->nativeStackLimit = rt->nativeStackBase - (stackSize - 1);
+        rt->mainThread.nativeStackLimit = rt->nativeStackBase - (stackSize - 1);
     }
 #endif
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(int)
 JS_IdArrayLength(JSContext *cx, JSIdArray *ida)
@@ -3618,17 +3607,17 @@ JS_LookupProperty(JSContext *cx, JSObjec
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_LookupPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_LookupPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *objArg, jsid id_, unsigned flags,
                                JSObject **objpArg, jsval *vp)
 {
     RootedObject obj(cx, objArg);
@@ -3691,17 +3680,17 @@ JS_HasProperty(JSContext *cx, JSObject *
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_HasPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_HasUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_HasPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnPropertyById(JSContext *cx, JSObject *objArg, jsid id_, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, id_);
@@ -3748,17 +3737,17 @@ JS_AlreadyHasOwnProperty(JSContext *cx, 
     return atom && JS_AlreadyHasOwnPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                            JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_AlreadyHasOwnPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 /* Wrapper functions to create wrappers with no corresponding JSJitInfo from API
  * function arguments.
  */
 static JSPropertyOpWrapper
 GetterWrapper(JSPropertyOp getter)
@@ -3922,17 +3911,17 @@ JS_DefinePropertyWithTinyId(JSContext *c
 
 static JSBool
 DefineUCProperty(JSContext *cx, JSHandleObject obj, const jschar *name, size_t namelen,
                  const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                  unsigned flags, int tinyid)
 {
     RootedValue value(cx, value_);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
     return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter),
                               SetterWrapper(setter), attrs, flags, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
@@ -4125,17 +4114,17 @@ JS_GetPropertyAttributes(JSContext *cx, 
                                                           attrsp, foundp, NULL, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCPropertyAttributes(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                            unsigned *attrsp, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, NULL, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *objArg, const char *name,
                                    unsigned *attrsp, JSBool *foundp,
                                    JSPropertyOp *getterp, JSStrictPropertyOp *setterp)
@@ -4148,17 +4137,17 @@ JS_GetPropertyAttrsGetterAndSetter(JSCon
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *objArg,
                                      const jschar *name, size_t namelen,
                                      unsigned *attrsp, JSBool *foundp,
                                      JSPropertyOp *getterp, JSStrictPropertyOp *setterp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, getterp, setterp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
 {
     RootedObject obj(cx, objArg);
@@ -4203,17 +4192,17 @@ JS_SetPropertyAttributes(JSContext *cx, 
     return atom && SetPropertyAttributesById(cx, obj, id, attrs, foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetUCPropertyAttributes(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                            unsigned attrs, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     RootedId id(cx, AtomToId(atom));
     return atom && SetPropertyAttributesById(cx, obj, id, attrs, foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
 {
     return JS_ForwardGetPropertyTo(cx, objArg, idArg, objArg, vp);
@@ -4315,17 +4304,17 @@ JS_GetPropertyDefault(JSContext *cx, JSO
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyByIdDefault(cx, obj, AtomToId(atom), def, vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetMethodById(JSContext *cx, JSObject *objArg, jsid idArg, JSObject **objp, jsval *vp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
@@ -4394,17 +4383,17 @@ JS_SetProperty(JSContext *cx, JSObject *
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_SetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_SetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeletePropertyById2(JSContext *cx, JSObject *objArg, jsid id, jsval *rval)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
@@ -4467,17 +4456,17 @@ JS_DeleteProperty2(JSContext *cx, JSObje
 JS_PUBLIC_API(JSBool)
 JS_DeleteUCProperty2(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *rval)
 {
     RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, 0);
 
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
 
     RootedValue value(cx);
     if (!JSObject::deleteByValue(cx, obj, StringValue(atom), &value, false))
         return false;
 
     *rval = value;
@@ -5115,17 +5104,17 @@ JS_DefineUCFunction(JSContext *cx, JSObj
                     const jschar *name, size_t namelen, JSNative call,
                     unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return NULL;
     Rooted<jsid> id(cx, AtomToId(atom));
     return js_DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_DefineFunctionById(JSContext *cx, JSObject *objArg, jsid id_, JSNative call,
@@ -5538,17 +5527,17 @@ JS_DecompileScript(JSContext *cx, JSScri
     CHECK_REQUEST(cx);
     RootedScript script(cx, scriptArg);
     RootedFunction fun(cx, script->function());
     if (fun)
         return JS_DecompileFunction(cx, fun, indent);
     bool haveSource = script->scriptSource()->hasSourceData();
     if (!haveSource && !JSScript::loadSource(cx, script, &haveSource))
         return NULL;
-    return haveSource ? script->sourceData(cx) : js_NewStringCopyZ(cx, "[no source]");
+    return haveSource ? script->sourceData(cx) : js_NewStringCopyZ<CanGC>(cx, "[no source]");
 }
 
 JS_PUBLIC_API(JSString *)
 JS_DecompileFunction(JSContext *cx, JSFunction *funArg, unsigned indent)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -5930,17 +5919,17 @@ JS_GetFunctionCallback(JSContext *cx)
 #endif
 
 /************************************************************************/
 JS_PUBLIC_API(JSString *)
 JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_NewStringCopyN(cx, s, n);
+    return js_NewStringCopyN<CanGC>(cx, s, n);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewStringCopyZ(JSContext *cx, const char *s)
 {
     size_t n;
     jschar *js;
     JSString *str;
@@ -5948,17 +5937,17 @@ JS_NewStringCopyZ(JSContext *cx, const c
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (!s || !*s)
         return cx->runtime->emptyString;
     n = strlen(s);
     js = InflateString(cx, s, &n);
     if (!js)
         return NULL;
-    str = js_NewString(cx, js, n);
+    str = js_NewString<CanGC>(cx, js, n);
     if (!str)
         js_free(js);
     return str;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_StringHasBeenInterned(JSContext *cx, JSString *str)
 {
@@ -5980,17 +5969,17 @@ INTERNED_STRING_TO_JSID(JSContext *cx, J
     return AtomToId(&str->asAtom());
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternJSString(JSContext *cx, JSString *str)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom *atom = AtomizeString(cx, str, InternAtom);
+    JSAtom *atom = AtomizeString<CanGC>(cx, str, InternAtom);
     JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternString(JSContext *cx, const char *s)
 {
     return JS_InternStringN(cx, s, strlen(s));
@@ -6006,43 +5995,43 @@ JS_InternStringN(JSContext *cx, const ch
     return atom;
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewUCString(JSContext *cx, jschar *chars, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_NewString(cx, chars, length);
+    return js_NewString<CanGC>(cx, chars, length);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_NewStringCopyN(cx, s, n);
+    return js_NewStringCopyN<CanGC>(cx, s, n);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewUCStringCopyZ(JSContext *cx, const jschar *s)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (!s)
         return cx->runtime->emptyString;
-    return js_NewStringCopyZ(cx, s);
+    return js_NewStringCopyZ<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom *atom = AtomizeChars(cx, s, length, InternAtom);
+    JSAtom *atom = AtomizeChars<CanGC>(cx, s, length, InternAtom);
     JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternUCString(JSContext *cx, const jschar *s)
 {
     return JS_InternUCStringN(cx, s, js_strlen(s));
@@ -6188,17 +6177,17 @@ JS_FileEscapedString(FILE *fp, JSString 
     return linearStr && FileEscapedString(fp, linearStr, quote);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    return js_NewString(cx, chars, length);
+    return js_NewString<CanGC>(cx, chars, length);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewDependentString(JSContext *cx, JSString *str, size_t start, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return js_NewDependentString(cx, str, start, length);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3536,22 +3536,16 @@ JS_THIS(JSContext *cx, jsval *vp)
  * or vice versa.  Either use the provided this value with this macro, or
  * compute the boxed this value using those.
  *
  * N.B. constructors must not use JS_THIS_VALUE, as no 'this' object has been
  * created.
  */
 #define JS_THIS_VALUE(cx,vp)    ((vp)[1])
 
-extern JS_PUBLIC_API(void)
-JS_MallocInCompartment(JSCompartment *comp, size_t nbytes);
-
-extern JS_PUBLIC_API(void)
-JS_FreeInCompartment(JSCompartment *comp, size_t nbytes);
-
 extern JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(void *)
 JS_realloc(JSContext *cx, void *p, size_t nbytes);
 
 /*
  * A wrapper for js_free(p) that may delay js_free(p) invocation as a
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1410,16 +1410,18 @@ js::array_sort(JSContext *cx, unsigned a
 
                 /* Order vec[n:2n-1] using strElements.index */
                 for (size_t i = 0; i < n; i ++)
                     vec[n + i] = vec[strElements[i].elementIndex];
 
                 result = vec.begin() + n;
             }
         } else {
+            /* array.sort() cannot currently be used from parallel code */
+            JS_ASSERT(!ForkJoinSlice::InParallelSection());
             FastInvokeGuard fig(cx, fval);
             if (!MergeSort(vec.begin(), n, vec.begin() + n,
                            SortComparatorFunction(cx, fval, fig))) {
                 return false;
             }
         }
 
         if (!InitArrayElements(cx, obj, 0, uint32_t(n), result, DontUpdateTypes))
@@ -2198,16 +2200,17 @@ array_map(JSContext *cx, unsigned argc, 
         return false;
     arr->setType(newtype);
 
     /* Step 7. */
     uint32_t k = 0;
 
     /* Step 8. */
     RootedValue kValue(cx);
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
     FastInvokeGuard fig(cx, ObjectValue(*callable));
     InvokeArgsGuard &ag = fig.args();
     while (k < len) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
 
         /* Step a, b, and c.i. */
         JSBool kNotPresent;
@@ -2278,16 +2281,17 @@ array_filter(JSContext *cx, unsigned arg
 
     /* Step 7. */
     uint32_t k = 0;
 
     /* Step 8. */
     uint32_t to = 0;
 
     /* Step 9. */
+    JS_ASSERT(!ForkJoinSlice::InParallelSection());
     FastInvokeGuard fig(cx, ObjectValue(*callable));
     InvokeArgsGuard &ag = fig.args();
     RootedValue kValue(cx);
     while (k < len) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;