Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Sat, 19 Jan 2013 08:59:10 -0800
changeset 130019 a97eb4f09112957e54797b332d852195524799f7
parent 130018 d15625b7c9c7821974c653d08a00ce0b5c0b7331 (current diff)
parent 130016 02e12a80aef9133f473e9fc4a5023a40b720898b (diff)
child 130036 587ac3f2bd30880393aa13546ae8d6c59ff8200f
push id317
push userbbajaj@mozilla.com
push dateTue, 07 May 2013 01:20:33 +0000
treeherdermozilla-release@159a10910249 [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 m-c to s-c.
browser/base/content/test/browser_clickToPlayPluginScriptAccessPopup.js
config/expandlibs_config.py.in
content/svg/content/src/nsSVGClipPathElement.cpp
content/svg/content/src/nsSVGClipPathElement.h
docshell/test/browser/browser_bug134911.js
extensions/cookie/test/frame_clear_browser_data.html
extensions/cookie/test/test_app_cleardata_permissions.html
js/src/config/expandlibs_config.py.in
js/src/jit-test/jit_test.py
layout/reftests/backgrounds/continuous-inline-1-ref.html
layout/reftests/css-calc/background-image-gradient-1-ref.html
layout/reftests/css-calc/background-image-gradient-1.html
layout/reftests/css-calc/reftest.list
layout/reftests/text/auto-hyphenation-10.html
layout/reftests/text/auto-hyphenation-8.html
layout/reftests/text/auto-hyphenation-9.html
mobile/android/base/resources/drawable-hdpi/menu_popup_arrow.png
mobile/android/base/resources/drawable-hdpi/tab_indicator_divider.9.png
mobile/android/base/resources/drawable-hdpi/tab_indicator_selected.9.png
mobile/android/base/resources/drawable-hdpi/tab_indicator_selected_focused.9.png
mobile/android/base/resources/drawable-xhdpi/menu_popup_arrow.png
mobile/android/base/resources/drawable-xhdpi/tab_indicator_divider.9.png
mobile/android/base/resources/drawable-xhdpi/tab_indicator_selected.9.png
mobile/android/base/resources/drawable-xhdpi/tab_indicator_selected_focused.9.png
mobile/android/base/resources/drawable/menu_popup_arrow.png
mobile/android/base/resources/drawable/tab_indicator_divider.9.png
mobile/android/base/resources/drawable/tab_indicator_selected.9.png
mobile/android/base/resources/drawable/tab_indicator_selected_focused.9.png
mobile/android/base/resources/drawable/tabs_panel_indicator.xml
mobile/android/base/resources/layout-xlarge-v11/tabs_panel_toolbar_menu.xml
mobile/android/base/resources/layout/tabs_panel_indicator.xml
mobile/android/base/resources/layout/tabs_panel_toolbar_menu.xml
services/sync/SyncComponents.manifest
--- a/accessible/src/msaa/HyperTextAccessibleWrap.cpp
+++ b/accessible/src/msaa/HyperTextAccessibleWrap.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=2:tabstop=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 "HyperTextAccessibleWrap.h"
+#include "Accessible-inl.h"
 
 #include "nsEventShell.h"
 
 #include "mozilla/StaticPtr.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
--- a/accessible/tests/mochitest/editabletext/editabletext.js
+++ b/accessible/tests/mochitest/editabletext/editabletext.js
@@ -307,16 +307,20 @@ function editableTextTest(aID)
 
     if (aInsertTriple) {
       var checker = new textChangeChecker(aID, aInsertTriple[0],
                                           aInsertTriple[1], aInsertTriple[2],
                                           true);
       invoker.eventSeq.push(checker);
     }
 
+    // Claim that we don't want to fail when no events are expected.
+    if (!aRemoveTriple && !aInsertTriple)
+      invoker.noEventsOnAction = true;
+
     this.mEventQueue.mInvokers[this.mEventQueue.mIndex + 1] = invoker;
   }
 
   /**
    * Run the tests.
    */
   this.startTest = function startTest(aTestRun)
   {
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -181,23 +181,28 @@ const DO_NOT_FINISH_TEST = 1;
  *     invoke: function(){},
  *
  *     // [optional] Invoker's check of handled event for correctness.
  *     check: function(aEvent){},
  *
  *     // [optional] Invoker's check before the next invoker is proceeded.
  *     finalCheck: function(aEvent){},
  *
- *     // [optional] Is called when event of registered type is handled.
+ *     // [optional] Is called when event of any registered type is handled.
  *     debugCheck: function(aEvent){},
  *
  *     // [ignored if 'eventSeq' is defined] DOM node event is generated for
  *     // (used in the case when invoker expects single event).
  *     DOMNode getter: function() {},
  *
+ *     // [optional] if true then event sequences are ignored (no failure if
+ *     // sequences are empty). Use you need to invoke an action, do some check
+ *     // after timeout and proceed a next invoker.
+ *     noEventsOnAction getter: function() {},
+ *
  *     // Array of checker objects defining expected events on invoker's action.
  *     //
  *     // Checker object interface:
  *     //
  *     // var checker = {
  *     //   * DOM or a11y event type. *
  *     //   type getter: function() {},
  *     //
@@ -230,16 +235,21 @@ const DO_NOT_FINISH_TEST = 1;
  *     // Array of checker objects defining unexpected events on invoker's
  *     // action.
  *     unexpectedEventSeq getter() {},
  *
  *     // The ID of invoker.
  *     getID: function(){} // returns invoker ID
  *   };
  *
+ *   // Used to add a possible scenario of expected/unexpected events on
+ *   // invoker's action.
+ *  defineScenario(aInvokerObj, aEventSeq, aUnexpectedEventSeq)
+ *
+ *
  * @param  aEventType  [in, optional] the default event type (isn't used if
  *                      invoker defines eventSeq property).
  */
 function eventQueue(aEventType)
 {
   // public
 
   /**
@@ -272,56 +282,100 @@ function eventQueue(aEventType)
 
   // private
 
   /**
    * Process next invoker.
    */
   this.processNextInvoker = function eventQueue_processNextInvoker()
   {
-    // Finish processing of the current invoker.
+    // Some scenario was matched, we wait on next invoker processing.
+    if (this.mNextInvokerStatus == kInvokerCanceled) {
+      this.mNextInvokerStatus = kInvokerNotScheduled;
+      return;
+    }
+
+    this.mNextInvokerStatus = kInvokerNotScheduled;
+
+    // Finish processing of the current invoker if any.
     var testFailed = false;
 
     var invoker = this.getInvoker();
     if (invoker) {
       if ("finalCheck" in invoker)
         invoker.finalCheck();
 
-      if (invoker.wasCaught) {
-        for (var idx = 0; idx < invoker.wasCaught.length; idx++) {
-          var id = this.getEventID(idx);
-          var type = this.getEventType(idx);
-          var unexpected = this.mEventSeq[idx].unexpected;
-
-          var typeStr = this.getEventTypeAsString(idx);
+      if (this.mScenarios && this.mScenarios.length) {
+        var matchIdx = -1;
+        for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+          var eventSeq = this.mScenarios[scnIdx];
+          if (!this.areExpectedEventsLeft(eventSeq)) {
+            for (var idx = 0; idx < eventSeq.length; idx++) {
+              var checker = eventSeq[idx];
+              if (checker.unexpected && checker.wasCaught ||
+                  !checker.unexpected && checker.wasCaught != 1) {
+                break;
+              }
+            }
 
-          var msg = "test with ID = '" + id + "' failed. ";
-          if (unexpected) {
-            var wasCaught = invoker.wasCaught[idx];
-            if (!testFailed)
-              testFailed = wasCaught;
+            // Ok, we have matched scenario. Report it was completed ok. In
+            // case of empty scenario guess it was matched but if later we
+            // find out that non empty scenario was matched then it will be
+            // a final match.
+            if (idx == eventSeq.length) {
+              if (matchIdx != -1 && eventSeq.length > 0 &&
+                  this.mScenarios[matchIdx].length > 0) {
+                ok(false,
+                   "We have a matched scenario at index " + matchIdx + " already.");
+              }
+
+              if (matchIdx == -1 || eventSeq.length > 0)
+                matchIdx = scnIdx;
 
-            ok(!wasCaught,
-               msg + "There is unexpected " + typeStr + " event.");
+              // Report everythign is ok.
+              for (var idx = 0; idx < eventSeq.length; idx++) {
+                var checker = eventSeq[idx];
 
-          } else {
-            var wasCaught = invoker.wasCaught[idx];
-            if (!testFailed)
-              testFailed = !wasCaught;
+                var typeStr = eventQueue.getEventTypeAsString(checker);
+                var msg = "Test with ID = '" + this.getEventID(checker) +
+                  "' succeed. ";
 
-            ok(wasCaught,
-               msg + "No " + typeStr + " event.");
+                if (checker.unexpected)
+                  ok(true, msg + "There's no unexpected " + typeStr + " event.");
+                else
+                  ok(true, msg + "Event " + typeStr + " was handled.");
+              }
+            }
           }
         }
-      } else {
-        testFailed = true;
-        for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-          var id = this.getEventID(idx);
-          ok(false,
-             "test with ID = '" + id + "' failed. No events were registered.");
+
+        // We don't have completely matched scenario. Report each failure/success
+        // for every scenario.
+        if (matchIdx == -1) {
+          testFailed = true;
+          for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+            var eventSeq = this.mScenarios[scnIdx];
+            for (var idx = 0; idx < eventSeq.length; idx++) {
+              var checker = eventSeq[idx];
+
+              var typeStr = eventQueue.getEventTypeAsString(checker);
+              var msg = "Scenario #" + scnIdx + " of test with ID = '" +
+                this.getEventID(checker) + "' failed. ";
+
+              if (checker.wasCaught > 1)
+                ok(false, msg + "Dupe " + typeStr + " event.");
+
+              if (checker.unexpected) {
+                if (checker.wasCaught)
+                  ok(false, msg + "There's unexpected " + typeStr + " event.");
+              } else if (!checker.wasCaught) {
+                ok(false, msg + typeStr + " event was missed.");
+              }
+            }
+          }
         }
       }
     }
 
     this.clearEventHandler();
 
     // Check if need to stop the test.
     if (testFailed || this.mIndex == this.mInvokers.length - 1) {
@@ -332,43 +386,58 @@ function eventQueue(aEventType)
         SimpleTest.finish();
 
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
-    this.setEventHandler(invoker);
+    // Set up event listeners. Process a next invoker if no events were added.
+    if (!this.setEventHandler(invoker)) {
+      this.processNextInvoker();
+      return;
+    }
 
     if (gLogger.isEnabled()) {
       gLogger.logToConsole("Event queue: \n  invoke: " + invoker.getID());
       gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true);
     }
 
     var infoText = "Invoke the '" + invoker.getID() + "' test { ";
-    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-      infoText += this.isEventUnexpected(idx) ? "un" : "";
-      infoText += "expected '" + this.getEventTypeAsString(idx) + "' event; ";
+    var scnCount = this.mScenarios ? this.mScenarios.length : 0;
+    for (var scnIdx = 0; scnIdx < scnCount; scnIdx++) {
+      infoText += "scenario #" + scnIdx + ": ";
+      var eventSeq = this.mScenarios[scnIdx];
+      for (var idx = 0; idx < eventSeq.length; idx++) {
+        infoText += eventSeq[idx].unexpected ? "un" : "" +
+          "expected '" + eventQueue.getEventTypeAsString(eventSeq[idx]) +
+          "' event; ";
+      }
     }
     infoText += " }";
     info(infoText);
 
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
-    if (this.areAllEventsUnexpected())
+    if (this.hasUnexpectedEventsScenario())
       this.processNextInvokerInTimeout(true);
   }
 
-  this.processNextInvokerInTimeout = function eventQueue_processNextInvokerInTimeout(aUncondProcess)
+  this.processNextInvokerInTimeout =
+    function eventQueue_processNextInvokerInTimeout(aUncondProcess)
   {
+    this.mNextInvokerStatus = kInvokerPending;
+
+    // No need to wait extra timeout when a) we know we don't need to do that
+    // and b) there's no any single unexpected event.
     if (!aUncondProcess && this.areAllEventsExpected()) {
       // We need delay to avoid events coalesce from different invokers.
       var queue = this;
       SimpleTest.executeSoon(function() { queue.processNextInvoker(); });
       return;
     }
 
     // Check in timeout invoker didn't fire registered events.
@@ -380,425 +449,461 @@ function eventQueue(aEventType)
    * Handle events for the current invoker.
    */
   this.handleEvent = function eventQueue_handleEvent(aEvent)
   {
     var invoker = this.getInvoker();
     if (!invoker) // skip events before test was started
       return;
 
-    if (!this.mEventSeq) {
+    if (!this.mScenarios) {
       // Bad invoker object, error will be reported before processing of next
       // invoker in the queue.
       this.processNextInvoker();
       return;
     }
 
     if ("debugCheck" in invoker)
       invoker.debugCheck(aEvent);
 
-    // Search through handled expected events to report error if one of them is
-    // handled for a second time.
-    var idx = 0;
-    for (; idx < this.mEventSeq.length; idx++) {
-      if (this.isEventExpected(idx) && (invoker.wasCaught[idx] == true) &&
-          this.isSameEvent(idx, aEvent)) {
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
+      for (var idx = 0; idx < eventSeq.length; idx++) {
+        var checker = eventSeq[idx];
+
+        // Search through handled expected events to report error if one of them
+        // is handled for a second time.
+        if (!checker.unexpected && (checker.wasCaught > 0) &&
+            eventQueue.isSameEvent(checker, aEvent)) {
+          checker.wasCaught++;
+          continue;
+        }
+
+        // Search through unexpected events, any match results in error report
+        // after this invoker processing (in case of matched scenario only).
+        if (checker.unexpected && eventQueue.compareEvents(checker, aEvent)) {
+          checker.wasCaught++;
+          continue;
+        }
+
+        // Report an error if we hanlded not expected event of unique type
+        // (i.e. event types are matched, targets differs).
+        if (!checker.unexpected && checker.unique &&
+            eventQueue.compareEventTypes(checker, aEvent)) {
+          var isExppected = false;
+          for (var jdx = 0; jdx < eventSeq.length; jdx++) {
+            isExpected = eventQueue.compareEvents(eventSeq[jdx], aEvent);
+            if (isExpected)
+              break;
+          }
 
-        var msg = "Doubled event { event type: " +
-          this.getEventTypeAsString(idx) + ", target: " +
-          this.getEventTargetDescr(idx) + "} in test with ID = '" +
-          this.getEventID(idx) + "'.";
-        ok(false, msg);
+          if (!isExpected) {
+            ok(false,
+               "Unique type " +
+               eventQueue.getEventTypeAsString(checker) + " event was handled.");
+          }
+        }
+      }
+    }
+
+    var matchedChecker = null;
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
+
+      // Check if handled event matches expected sync event.
+      var nextChecker = this.getNextExpectedEvent(eventSeq);
+      if (nextChecker) {
+        if (eventQueue.compareEvents(nextChecker, aEvent)) {
+          matchedChecker = nextChecker;
+          matchedChecker.wasCaught++;
+          break;
+        }
+      }
+
+      // Check if handled event matches any expected async events.
+      for (idx = 0; idx < eventSeq.length; idx++) {
+        if (!eventSeq[idx].unexpected && eventSeq[idx].async) {
+          if (eventQueue.compareEvents(eventSeq[idx], aEvent)) {
+            matchedChecker = eventSeq[idx];
+            matchedChecker.wasCaught++;
+            break;
+          }
+        }
       }
     }
 
-    // Search through unexpected events, any matches result in error report
-    // after this invoker processing.
-    for (idx = 0; idx < this.mEventSeq.length; idx++) {
-      if (this.isEventUnexpected(idx) && this.compareEvents(idx, aEvent))
-        invoker.wasCaught[idx] = true;
+    // Call 'check' functions on invoker's side.
+    if (matchedChecker) {
+      if ("check" in matchedChecker)
+        matchedChecker.check(aEvent);
+
+      var invoker = this.getInvoker();
+      if ("check" in invoker)
+        invoker.check(aEvent);
     }
 
-    // Nothing left, proceed next invoker in timeout. Otherwise check if
-    // handled event is matched.
-    var idxObj = {};
-    if (!this.prepareForExpectedEvent(invoker, idxObj))
-      return;
+    // Dump handled event.
+    eventQueue.logEvent(aEvent, matchedChecker, this.areExpectedEventsLeft(),
+                        this.mNextInvokerStatus);
 
-    // Check if handled event matches expected sync event.
-    var matched = false;
-    idx = idxObj.value;
-    if (idx < this.mEventSeq.length) {
-      matched = this.compareEvents(idx, aEvent);
-      if (matched)
-        this.mEventSeqIdx = idx;
+    // If we don't have more events to wait then schedule next invoker.
+    if (!this.areExpectedEventsLeft() &&
+        (this.mNextInvokerStatus == kInvokerNotScheduled)) {
+      this.processNextInvokerInTimeout();
+      return;
     }
 
-    // Check if handled event matches any expected async events.
-    if (!matched) {
-      for (idx = 0; idx < this.mEventSeq.length; idx++) {
-        if (this.mEventSeq[idx].async) {
-          matched = this.compareEvents(idx, aEvent);
-          if (matched)
-            break;
-        }
-      }
-    }
-    this.dumpEventToDOM(aEvent, idx, matched);
-
-    if (matched) {
-      this.checkEvent(idx, aEvent);
-      invoker.wasCaught[idx] = true;
-
-      this.prepareForExpectedEvent(invoker);
-    }
+    // If we have scheduled a next invoker then cancel in case of match.
+    if ((this.mNextInvokerStatus == kInvokerPending) && matchedChecker)
+      this.mNextInvokerStatus = kInvokerCanceled;
   }
 
   // Helpers
-  this.prepareForExpectedEvent =
-    function eventQueue_prepareForExpectedEvent(aInvoker, aIdxObj)
+  this.getNextExpectedEvent =
+    function eventQueue_getNextExpectedEvent(aEventSeq)
   {
-    // Nothing left, wait for next invoker.
-    if (this.mEventSeqFinished)
-      return false;
+    if (!("idx" in aEventSeq))
+      aEventSeq.idx = 0;
+
+    while (aEventSeq.idx < aEventSeq.length &&
+           (aEventSeq[aEventSeq.idx].unexpected ||
+            aEventSeq[aEventSeq.idx].async ||
+            aEventSeq[aEventSeq.idx].wasCaught > 0)) {
+      aEventSeq.idx++;
+    }
 
-    // Compute next expected sync event index.
-    for (var idx = this.mEventSeqIdx + 1;
-         idx < this.mEventSeq.length &&
-         (this.mEventSeq[idx].unexpected || this.mEventSeq[idx].async);
-         idx++);
+    return aEventSeq.idx != aEventSeq.length ? aEventSeq[aEventSeq.idx] : null;
+  }
 
-    // If no expected events were left, proceed to next invoker in timeout
-    // to make sure unexpected events for current invoker aren't be handled.
-    if (idx == this.mEventSeq.length) {
-      var allHandled = true;
-      for (var jdx = 0; jdx < this.mEventSeq.length; jdx++) {
-        if (this.isEventExpected(jdx) && !aInvoker.wasCaught[jdx])
-          allHandled = false;
+  this.areExpectedEventsLeft =
+    function eventQueue_areExpectedEventsLeft(aScenario)
+  {
+    function scenarioHasUnhandledExpectedEvent(aEventSeq)
+    {
+      // Check if we have unhandled async (can be anywhere in the sequance) or
+      // sync expcected events yet.
+      for (var idx = 0; idx < aEventSeq.length; idx++) {
+        if (!aEventSeq[idx].unexpected && !aEventSeq[idx].wasCaught)
+          return true;
       }
 
-      if (allHandled) {
-        this.mEventSeqIdx = this.mEventSeq.length;
-        this.mEventFinished = true;
-        this.processNextInvokerInTimeout();
-        return false;
+      return false;
+    }
+
+    if (aScenario)
+      return scenarioHasUnhandledExpectedEvent(aScenario);
+
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
+      if (scenarioHasUnhandledExpectedEvent(eventSeq))
+        return true;
+    }
+    return false;
+  }
+
+  this.areAllEventsExpected =
+    function eventQueue_areAllEventsExpected()
+  {
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
+      for (var idx = 0; idx < eventSeq.length; idx++) {
+        if (eventSeq[idx].unexpected)
+          return false;
       }
     }
 
-    if (aIdxObj)
-      aIdxObj.value = idx;
+    return true;
+  }
+
+  this.hasUnexpectedEventsScenario =
+    function eventQueue_hasUnexpectedEventsScenario()
+  {
+    if (this.getInvoker().noEventsOnAction)
+      return true;
 
-    return true;
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
+      for (var idx = 0; idx < eventSeq.length; idx++) {
+        if (!eventSeq[idx].unexpected)
+          break;
+      }
+      if (idx == eventSeq.length)
+        return true;
+    }
+
+    return false;
   }
 
   this.getInvoker = function eventQueue_getInvoker()
   {
     return this.mInvokers[this.mIndex];
   }
 
   this.getNextInvoker = function eventQueue_getNextInvoker()
   {
     return this.mInvokers[++this.mIndex];
   }
 
   this.setEventHandler = function eventQueue_setEventHandler(aInvoker)
   {
-    // Create unified event sequence concatenating expected and unexpected
-    // events.
-    this.mEventSeq = ("eventSeq" in aInvoker) ? aInvoker.eventSeq : [ ];
-    if (!this.mEventSeq.length && this.mDefEventType) {
-      this.mEventSeq.push(new invokerChecker(this.mDefEventType,
-                                             aInvoker.DOMNode));
+    if (!("scenarios" in aInvoker) || aInvoker.scenarios.length == 0) {
+      var eventSeq = aInvoker.eventSeq;
+      var unexpectedEventSeq = aInvoker.unexpectedEventSeq;
+      if (!eventSeq && !unexpectedEventSeq && this.mDefEventType)
+        eventSeq = [ new invokerChecker(this.mDefEventType, aInvoker.DOMNode) ];
+
+      if (eventSeq || unexpectedEventSeq)
+        defineScenario(aInvoker, eventSeq, unexpectedEventSeq);
+    }
+
+    if (aInvoker.noEventsOnAction)
+      return true;
+
+    this.mScenarios = aInvoker.scenarios;
+    if (!this.mScenarios || !this.mScenarios.length) {
+      ok(false, "Broken invoker '" + aInvoker.getID() + "'");
+      return false;
     }
 
-    var len = this.mEventSeq.length;
-    for (var idx = 0; idx < len; idx++) {
-      var seqItem = this.mEventSeq[idx];
-      // Allow unexpected events in primary event sequence.
-      if (!("unexpected" in this.mEventSeq[idx]))
-        seqItem.unexpected = false;
-
-      if (!("async" in this.mEventSeq[idx]))
-        seqItem.async = false;
+    // Register event listeners.
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
 
-      // If the event is of unique type (regardless whether it's expected or
-      // not) then register additional unexpected event that matches to any
-      // event of the same type with any target different from registered
-      // expected events.
-      if (("unique" in seqItem) && seqItem.unique) {
-        var uniquenessChecker = {
-          type: seqItem.type,
-          unexpected: true,
-          match: function uniquenessChecker_match(aEvent)
-          {
-            // The handled event is matched if its target doesn't match to any
-            // registered expected event.
-            var matched = true;
-            for (var idx = 0; idx < this.queue.mEventSeq.length; idx++) {
-              if (this.queue.isEventExpected(idx) &&
-                  this.queue.compareEvents(idx, aEvent)) {
-                matched = false;
-                break;
-              }
-            }
-            return matched;
-          },
-          targetDescr: "any target different from expected events",
-          queue: this
-        };
-        this.mEventSeq.push(uniquenessChecker);
-      }
-    }
-
-    var unexpectedSeq = aInvoker.unexpectedEventSeq;
-    if (unexpectedSeq) {
-      for (var idx = 0; idx < unexpectedSeq.length; idx++) {
-        unexpectedSeq[idx].unexpected = true;
-        unexpectedSeq[idx].async = false;
+      if (gLogger.isEnabled()) {
+        var msg = "scenario #" + scnIdx +
+          ", registered events number: " + eventSeq.length;
+        gLogger.logToConsole(msg);
+        gLogger.logToDOM(msg, true);
       }
 
-      this.mEventSeq = this.mEventSeq.concat(unexpectedSeq);
-    }
-
-    this.mEventSeqIdx = -1;
-    this.mEventSeqFinished = false;
+      // Do not warn about empty event sequances when more than one scenario
+      // was registered.
+      if (this.mScenarios.length == 1 && eventSeq.length == 0) {
+        ok(false,
+           "Broken scenario #" + scnIdx + " of invoker '" + aInvoker.getID() +
+           "'. No registered events");
+        return false;
+      }
 
-    // Register event listeners
-    if (this.mEventSeq) {
-      aInvoker.wasCaught = new Array(this.mEventSeq.length);
+      for (var idx = 0; idx < eventSeq.length; idx++)
+        eventSeq[idx].wasCaught = 0;
 
-      for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-        var eventType = this.getEventType(idx);
-
+      for (var idx = 0; idx < eventSeq.length; idx++) {
         if (gLogger.isEnabled()) {
           var msg = "registered";
-          if (this.isEventUnexpected(idx))
+          if (eventSeq[idx].unexpected)
             msg += " unexpected";
-          if (this.mEventSeq[idx].async)
+          if (eventSeq[idx].async)
             msg += " async";
 
-          msg += ": event type: " + this.getEventTypeAsString(idx) +
-            ", target: " + this.getEventTargetDescr(idx, true);
+          msg += ": event type: " +
+            eventQueue.getEventTypeAsString(eventSeq[idx]) +
+            ", target: " + eventQueue.getEventTargetDescr(eventSeq[idx], true);
 
           gLogger.logToConsole(msg);
           gLogger.logToDOM(msg, true);
         }
 
+        var eventType = eventSeq[idx].type;
         if (typeof eventType == "string") {
           // DOM event
-          var target = this.getEventTarget(idx);
+          var target = eventSeq[idx].target;
           if (!target) {
             ok(false, "no target for DOM event!");
-            return;
+            return false;
           }
-          var phase = this.getEventPhase(idx);
+          var phase = eventQueue.getEventPhase(eventSeq[idx]);
           target.ownerDocument.addEventListener(eventType, this, phase);
 
         } else {
           // A11y event
           addA11yEventListener(eventType, this);
         }
       }
     }
+
+    return true;
   }
 
   this.clearEventHandler = function eventQueue_clearEventHandler()
   {
-    if (this.mEventSeq) {
-      for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-        var eventType = this.getEventType(idx);
+    if (!this.mScenarios)
+      return;
+
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      var eventSeq = this.mScenarios[scnIdx];
+      for (var idx = 0; idx < eventSeq.length; idx++) {
+        var eventType = eventSeq[idx].type;
         if (typeof eventType == "string") {
           // DOM event
-          var target = this.getEventTarget(idx);
-          var phase = this.getEventPhase(idx);
+          var target = eventSeq[idx].target;
+          var phase = eventQueue.getEventPhase(eventSeq[idx]);
           target.ownerDocument.removeEventListener(eventType, this, phase);
 
         } else {
           // A11y event
           removeA11yEventListener(eventType, this);
         }
       }
-
-      this.mEventSeq = null;
     }
-  }
-
-  this.getEventType = function eventQueue_getEventType(aIdx)
-  {
-    return this.mEventSeq[aIdx].type;
-  }
-
-  this.getEventTypeAsString = function eventQueue_getEventTypeAsString(aIdx)
-  {
-    var type = this.mEventSeq[aIdx].type;
-    return (typeof type == "string") ? type : eventTypeToString(type);
-  }
-
-  this.getEventTarget = function eventQueue_getEventTarget(aIdx)
-  {
-    return this.mEventSeq[aIdx].target;
-  }
-
-  this.getEventTargetDescr =
-    function eventQueue_getEventTargetDescr(aIdx, aDontForceTarget)
-  {
-    var descr = this.mEventSeq[aIdx].targetDescr;
-    if (descr)
-      return descr;
-
-    if (aDontForceTarget)
-      return "no target description";
-
-    var target = ("target" in this.mEventSeq[aIdx]) ?
-      this.mEventSeq[aIdx].target : null;
-    return prettyName(target);
-  }
-
-  this.getEventPhase = function eventQueue_getEventPhase(aIdx)
-  {
-     var eventItem = this.mEventSeq[aIdx];
-    if ("phase" in eventItem)
-      return eventItem.phase;
-
-    return true;
-  }
-
-  this.getEventID = function eventQueue_getEventID(aIdx)
-  {
-    var eventItem = this.mEventSeq[aIdx];
-    if ("getID" in eventItem)
-      return eventItem.getID();
-    
-    var invoker = this.getInvoker();
-    return invoker.getID();
-  }
-
-  this.isEventUnexpected = function eventQueue_isEventUnexpected(aIdx)
-  {
-    return this.mEventSeq[aIdx].unexpected;
-  }
-  this.isEventExpected = function eventQueue_isEventExpected(aIdx)
-  {
-    return !this.mEventSeq[aIdx].unexpected;
-  }
-
-  this.compareEventTypes = function eventQueue_compareEventTypes(aIdx, aEvent)
-  {
-    var eventType1 = this.getEventType(aIdx);
-    var eventType2 = (aEvent instanceof nsIDOMEvent) ?
-      aEvent.type : aEvent.eventType;
-
-    return eventType1 == eventType2;
+    this.mScenarios = null;
   }
 
-  this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
+  this.getEventID = function eventQueue_getEventID(aChecker)
   {
-    if (!this.compareEventTypes(aIdx, aEvent))
-      return false;
-
-    // If checker provides "match" function then allow the checker to decide
-    // whether event is matched.
-    if ("match" in this.mEventSeq[aIdx])
-      return this.mEventSeq[aIdx].match(aEvent);
-
-    var target1 = this.getEventTarget(aIdx);
-    if (target1 instanceof nsIAccessible) {
-      var target2 = (aEvent instanceof nsIDOMEvent) ?
-        getAccessible(aEvent.target) : aEvent.accessible;
-
-      return target1 == target2;
-    }
-
-    // If original target isn't suitable then extend interface to support target
-    // (original target is used in test_elm_media.html).
-    var target2 = (aEvent instanceof nsIDOMEvent) ?
-      aEvent.originalTarget : aEvent.DOMNode;
-    return target1 == target2;
-  }
-
-  this.isSameEvent = function eventQueue_isSameEvent(aIdx, aEvent)
-  {
-    // We don't have stored info about handled event other than its type and
-    // target, thus we should filter text change and state change events since
-    // they may occur on the same element because of complex changes.
-    return this.compareEvents(aIdx, aEvent) &&
-      !(aEvent instanceof nsIAccessibleTextChangeEvent) &&
-      !(aEvent instanceof nsIAccessibleStateChangeEvent);
-  }
-
-  this.checkEvent = function eventQueue_checkEvent(aIdx, aEvent)
-  {
-    var eventItem = this.mEventSeq[aIdx];
-    if ("check" in eventItem)
-      eventItem.check(aEvent);
+    if ("getID" in aChecker)
+      return aChecker.getID();
 
     var invoker = this.getInvoker();
-    if ("check" in invoker)
-      invoker.check(aEvent);
-  }
-
-  this.areAllEventsExpected = function eventQueue_areAllEventsExpected()
-  {
-    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-      if (this.mEventSeq[idx].unexpected)
-        return false;
-    }
-
-    return true;
-  }
-
-  this.areAllEventsUnexpected = function eventQueue_areAllEventsUnxpected()
-  {
-    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
-      if (!this.mEventSeq[idx].unexpected)
-        return false;
-    }
-
-    return true;
-  }
-
-  this.dumpEventToDOM = function eventQueue_dumpEventToDOM(aOrigEvent,
-                                                           aExpectedEventIdx,
-                                                           aMatch)
-  {
-    if (!gLogger.isEnabled()) // debug stuff
-      return;
-
-    // Dump DOM event information. Skip a11y event since it is dumped by
-    // gA11yEventObserver.
-    if (aOrigEvent instanceof nsIDOMEvent) {
-      var info = "Event type: " + aOrigEvent.type;
-      info += ". Target: " + prettyName(aOrigEvent.originalTarget);
-      gLogger.logToDOM(info);
-    }
-
-    if (!aMatch)
-      return;
-
-    var msg = "EQ: ";
-    var emphText = "matched ";
-
-    var currType = this.getEventTypeAsString(aExpectedEventIdx);
-    var currTargetDescr = this.getEventTargetDescr(aExpectedEventIdx);
-    var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
-    gLogger.logToConsole(consoleMsg);
-
-    msg += " event, type: " + currType + ", target: " + currTargetDescr;
-
-    gLogger.logToDOM(msg, true, emphText);
+    return invoker.getID();
   }
 
   this.mDefEventType = aEventType;
 
   this.mInvokers = new Array();
   this.mIndex = -1;
+  this.mScenarios = null;
 
-  this.mEventSeq = null;
-  this.mEventSeqIdx = -1;
-  this.mEventSeqFinished = false;
+  this.mNextInvokerStatus = kInvokerNotScheduled;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// eventQueue static members and constants
+
+const kInvokerNotScheduled = 0;
+const kInvokerPending = 1;
+const kInvokerCanceled = 2;
+
+eventQueue.getEventTypeAsString =
+  function eventQueue_getEventTypeAsString(aEventOrChecker)
+{
+  if (aEventOrChecker instanceof nsIDOMEvent)
+    return aEventOrChecker.type;
+
+  if (aEventOrChecker instanceof nsIAccessibleEvent)
+    return eventTypeToString(aEventOrChecker.eventType);
+
+  return (typeof aEventOrChecker.type == "string") ?
+    aEventOrChecker.type : eventTypeToString(aEventOrChecker.type);
+}
+
+eventQueue.getEventTargetDescr =
+  function eventQueue_getEventTargetDescr(aEventOrChecker, aDontForceTarget)
+{
+  if (aEventOrChecker instanceof nsIDOMEvent)
+    return prettyName(aEventOrChecker.originalTarget);
+
+  if (aEventOrChecker instanceof nsIDOMEvent)
+    return prettyName(aEventOrChecker.accessible);
+
+  var descr = aEventOrChecker.targetDescr;
+  if (descr)
+    return descr;
+
+  if (aDontForceTarget)
+    return "no target description";
+
+  var target = ("target" in aEventOrChecker) ? aEventOrChecker.target : null;
+  return prettyName(target);
+}
+
+eventQueue.getEventPhase = function eventQueue_getEventPhase(aChecker)
+{
+  return ("phase" in aChecker) ? aChecker.phase : true;
+}
+
+eventQueue.compareEventTypes =
+  function eventQueue_compareEventTypes(aChecker, aEvent)
+{
+  var eventType = (aEvent instanceof nsIDOMEvent) ?
+    aEvent.type : aEvent.eventType;
+  return aChecker.type == eventType;
+}
+
+eventQueue.compareEvents = function eventQueue_compareEvents(aChecker, aEvent)
+{
+  if (!eventQueue.compareEventTypes(aChecker, aEvent))
+    return false;
+
+  // If checker provides "match" function then allow the checker to decide
+  // whether event is matched.
+  if ("match" in aChecker)
+    return aChecker.match(aEvent);
+
+  var target1 = aChecker.target;
+  if (target1 instanceof nsIAccessible) {
+    var target2 = (aEvent instanceof nsIDOMEvent) ?
+      getAccessible(aEvent.target) : aEvent.accessible;
+
+    return target1 == target2;
+  }
+
+  // If original target isn't suitable then extend interface to support target
+  // (original target is used in test_elm_media.html).
+  var target2 = (aEvent instanceof nsIDOMEvent) ?
+    aEvent.originalTarget : aEvent.DOMNode;
+  return target1 == target2;
+}
+
+eventQueue.isSameEvent = function eventQueue_isSameEvent(aChecker, aEvent)
+{
+  // We don't have stored info about handled event other than its type and
+  // target, thus we should filter text change and state change events since
+  // they may occur on the same element because of complex changes.
+  return this.compareEvents(aChecker, aEvent) &&
+    !(aEvent instanceof nsIAccessibleTextChangeEvent) &&
+    !(aEvent instanceof nsIAccessibleStateChangeEvent);
+}
+
+eventQueue.logEvent = function eventQueue_logEvent(aOrigEvent, aMatchedChecker,
+                                                   aAreExpectedEventsLeft,
+                                                   aInvokerStatus)
+{
+  if (!gLogger.isEnabled()) // debug stuff
+    return;
+
+  // Dump DOM event information. Skip a11y event since it is dumped by
+  // gA11yEventObserver.
+  if (aOrigEvent instanceof nsIDOMEvent) {
+    var info = "Event type: " + eventQueue.getEventTypeAsString(aOrigEvent);
+    info += ". Target: " + eventQueue.getEventTargetDescr(aOrigEvent);
+    gLogger.logToDOM(info);
+  }
+
+  var msg = "unhandled expected events: " + aAreExpectedEventsLeft +
+    ", invoker status: ";
+  switch (aInvokerStatus) {
+    case kInvokerNotScheduled:
+      msg += "not scheduled";
+      break;
+    case kInvokerPending:
+      msg += "pending";
+      break;
+    case kInvokerCanceled:
+      msg += "canceled";
+      break;
+  }
+
+  gLogger.logToConsole(msg);
+  gLogger.logToDOM(msg);
+
+  if (!aMatchedChecker)
+    return;
+
+  var msg = "EQ: ";
+  var emphText = "matched ";
+
+  var currType = eventQueue.getEventTypeAsString(aMatchedChecker);
+  var currTargetDescr = eventQueue.getEventTargetDescr(aMatchedChecker);
+  var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
+  gLogger.logToConsole(consoleMsg);
+
+  msg += " event, type: " + currType + ", target: " + currTargetDescr;
+
+  gLogger.logToDOM(msg, true, emphText);
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Action sequence
 
 /**
  * Deal with action sequence. Used when you need to execute couple of actions
@@ -846,16 +951,48 @@ function sequence()
   this.idx = -1;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
+ * Defines a scenario of expected/unexpected events. Each invoker can have
+ * one or more scenarios of events. Only one scenario must be completed.
+ */
+function defineScenario(aInvoker, aEventSeq, aUnexpectedEventSeq)
+{
+  if (!("scenarios" in aInvoker))
+    aInvoker.scenarios = new Array();
+
+  // Create unified event sequence concatenating expected and unexpected
+  // events.
+  if (!aEventSeq)
+    aEventSeq = [];
+
+  for (var idx = 0; idx < aEventSeq.length; idx++) {
+    aEventSeq[idx].unexpected |= false;
+    aEventSeq[idx].async |= false;
+  }
+
+  if (aUnexpectedEventSeq) {
+    for (var idx = 0; idx < aUnexpectedEventSeq.length; idx++) {
+      aUnexpectedEventSeq[idx].unexpected = true;
+      aUnexpectedEventSeq[idx].async = false;
+    }
+
+    aEventSeq = aEventSeq.concat(aUnexpectedEventSeq);
+  }
+
+  aInvoker.scenarios.push(aEventSeq);
+}
+
+
+/**
  * Invokers defined below take a checker object (or array of checker objects).
  * An invoker listens for default event type registered in event queue object
  * until its checker is provided.
  *
  * Note, checker object or array of checker objects is optional.
  */
 
 /**
--- a/accessible/tests/mochitest/states/test_doc_busy.html
+++ b/accessible/tests/mochitest/states/test_doc_busy.html
@@ -19,23 +19,27 @@
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     //gA11yEventDumpToConsole = true; // debugging stuff
 
     function loadFile()
     {
-      // XXX: state change busy false event might be delievered when document
-      // has state busy true already (these events should be coalesced actually
-      // in this case as nothing happened).
-      this.eventSeq = [
+      var eventSeq = [
         new stateChangeChecker(STATE_BUSY, false, true, document, null, false, true),
         new stateChangeChecker(STATE_BUSY, false, false, document)
       ];
+      defineScenario(this, eventSeq); // both events were fired
+
+      var unexpectedEventSeq = [
+        new stateChangeChecker(STATE_BUSY, false, true, document),
+        new stateChangeChecker(STATE_BUSY, false, false, document)
+      ];
+      defineScenario(this, [], unexpectedEventSeq); // events were coalesced
 
       this.invoke = function loadFile_invoke()
       {
         synthesizeMouse(getNode("link"), 1, 1, {});
       }
 
       this.getID = function loadFile_getID()
       {
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -30,17 +30,16 @@ Makefile
 build/Makefile
 build/pgo/Makefile
 build/pgo/blueprint/Makefile
 build/pgo/js-input/Makefile
 config/Makefile
 config/autoconf.mk
 config/nspr/Makefile
 config/doxygen.cfg
-config/expandlibs_config.py
 mfbt/Makefile
 probes/Makefile
 python/Makefile
 extensions/Makefile
 "
 
 if [ "$MOZ_WEBAPP_RUNTIME" ]; then
   add_makefiles "
--- a/b2g/chrome/content/dbg-webapps-actors.js
+++ b/b2g/chrome/content/dbg-webapps-actors.js
@@ -208,16 +208,34 @@ WebappsActor.prototype = {
 
     let appId = aRequest.appId;
     if (!appId) {
       return { error: "missingParameter",
                message: "missing parameter appId" }
     }
 
     let appType = aRequest.appType || Ci.nsIPrincipal.APP_STATUS_INSTALLED;
+
+    // Check that we are not overriding a preinstalled application.
+    let reg = DOMApplicationRegistry;
+    if (appId in reg.webapps && reg.webapps[appId].removable === false) {
+      return { error: "badParameterType",
+               message: "The application " + appId + " can't be overriden."
+             }
+    }
+
+    // In production builds, don't allow installation of certified apps.
+#ifdef MOZ_OFFICIAL
+    if (appType == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
+      return { error: "badParameterType",
+               message: "Installing certified apps is not allowed."
+             }
+    }
+#endif
+
     let appDir = FileUtils.getDir("TmpD", ["b2g", appId], false, false);
 
     if (!appDir || !appDir.exists()) {
       return { error: "badParameterType",
                message: "missing directory " + appDir.path
              }
     }
 
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -91,16 +91,17 @@ for each (let [setting, maxValue, stream
     });
   })(setting, maxValue, streamTypes);
 }
 
 // =================== Console ======================
 
 SettingsListener.observe('debug.console.enabled', true, function(value) {
   Services.prefs.setBoolPref('consoleservice.enabled', value);
+  Services.prefs.setBoolPref('layout.css.report_errors', value);
 });
 
 // =================== Languages ====================
 SettingsListener.observe('language.current', 'en-US', function(value) {
   Services.prefs.setCharPref('general.useragent.locale', value);
 
   let prefName = 'intl.accept_languages';
   if (Services.prefs.prefHasUserValue(prefName)) {
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -112,16 +112,22 @@ ContentPermissionPrompt.prototype = {
       return false;
     }
 
     request.cancel();
     return true;
   },
 
   prompt: function(request) {
+
+    if (secMan.isSystemPrincipal(request.principal)) {
+      request.allow();
+      return true;
+    }
+
     if (this.handledByApp(request))
         return;
 
     // returns true if the request was handled
     if (this.handleExistingPermission(request))
        return;
 
     // If the request was initiated from a hidden iframe
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -152,16 +152,17 @@
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_activities.xpt
 @BINPATH@/components/dom_apps.xpt
+@BINPATH@/components/dom_audiochannel.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_system.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
 @BINPATH@/components/dom_cellbroadcast.xpt
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1357926611000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1358464191000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
@@ -215,16 +215,20 @@
                     </versionRange>
                   </emItem>
       <emItem  blockID="i22" id="ShopperReports@ShopperReports.com">
                         <versionRange  minVersion="3.1.22.0" maxVersion="3.1.22.0">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i44" id="sigma@labs.mozilla">
                         </emItem>
+      <emItem  blockID="i246" id="support@vide1flash2.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i48" id="admin@youtubespeedup.com">
                         </emItem>
       <emItem  blockID="i109" id="{392e123b-b691-4a5e-b52f-c4c1027e749c}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i79" id="GifBlock@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*">
@@ -583,31 +587,31 @@
       <pluginItem  blockID="p178">
                   <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.0" maxVersion="11.4.402.286.999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p180">
-                  <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 0" maxVersion="Java 7 Update 10" severity="0" vulnerabilitystatus="2">
+                  <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 0" maxVersion="Java 7 Update 11" severity="0" vulnerabilitystatus="2">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p182">
-      <match name="name" exp="Java\(TM\) Platform SE 7 U([0-9]|10)(\s[^\d\._U]|$)" />            <match name="filename" exp="npjp2\.dll" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
+      <match name="name" exp="Java\(TM\) Platform SE 7 U([0-9]|(1[0-1]))(\s[^\d\._U]|$)" />            <match name="filename" exp="npjp2\.dll" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p184">
-      <match name="name" exp="Java\(TM\) Plug-in 1\.7\.0(_0?([0-9]|10)?)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
+      <match name="name" exp="Java\(TM\) Plug-in 1\.7\.0(_0?([0-9]|(1[0-1]))?)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p186">
       <match name="name" exp="Java\(TM\) Platform SE 6 U3[1-8](\s[^\d\._U]|$)" />            <match name="filename" exp="npjp2\.dll" />                                    <versionRange  severity="0" vulnerabilitystatus="2">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -655,16 +659,32 @@
                   </pluginItem>
       <pluginItem  os="Darwin" blockID="p242">
             <match name="description" exp="Flip4Mac" />                                          <versionRange  minVersion="0" maxVersion="2.4.3.999" severity="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="18.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p248">
+                  <match name="filename" exp="Scorch\.plugin" />                      <versionRange  minVersion="0" maxVersion="6.2.0" severity="1"></versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p250">
+                  <match name="filename" exp="npFoxitReaderPlugin\.dll" />                      <versionRange  minVersion="0" maxVersion="2.2.1.530" severity="0" vulnerabilitystatus="2"></versionRange>
+                  </pluginItem>
+      <pluginItem  os="Darwin" blockID="p252">
+                  <match name="filename" exp="AdobePDFViewerNPAPI\.plugin" />                      <versionRange  minVersion="11.0.0" maxVersion="11.0.01" severity="1"></versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p254">
+                  <match name="filename" exp="PDF Browser Plugin\.plugin" />                                    <versionRange  minVersion="0" maxVersion="2.4.2" severity="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="18.0a1" maxVersion="*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
     </pluginItems>
 
   <gfxItems>
     <gfxBlacklistEntry  blockID="g35">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
                       <device>0x0a6c</device>
                   </devices>
             <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.17.12.5896</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g36">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -87,26 +87,16 @@ static bool IsArg(const char* arg, const
 #if defined(XP_WIN) || defined(XP_OS2)
   if (*arg == '/')
     return !strcasecmp(++arg, s);
 #endif
 
   return false;
 }
 
-/**
- * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
- */
-class ScopedLogging
-{
-public:
-  ScopedLogging() { NS_LogInit(); }
-  ~ScopedLogging() { NS_LogTerm(); }
-};
-
 XRE_GetFileFromPathType XRE_GetFileFromPath;
 XRE_CreateAppDataType XRE_CreateAppData;
 XRE_FreeAppDataType XRE_FreeAppData;
 #ifdef XRE_HAS_DLL_BLOCKLIST
 XRE_SetupDllBlocklistType XRE_SetupDllBlocklist;
 #endif
 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
@@ -310,24 +300,26 @@ InitXPCOMGlue(const char *argv0, nsIFile
   }
 
   rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XRE functions.\n");
     return rv;
   }
 
+  NS_LogInit();
+
   // chop XPCOM_DLL off exePath
   *lastSlash = '\0';
 #ifdef XP_WIN
-  NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), false,
-                  xreDirectory);
+  rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), false,
+                       xreDirectory);
 #else
-  NS_NewNativeLocalFile(nsDependentCString(exePath), false,
-                        xreDirectory);
+  rv = NS_NewNativeLocalFile(nsDependentCString(exePath), false,
+                             xreDirectory);
 #endif
 
   return rv;
 }
 
 int main(int argc, char* argv[])
 {
   PRTime start = _PR_Now();
@@ -377,21 +369,19 @@ int main(int argc, char* argv[])
     struct rusage newRUsage;
     if (!getrusage(RUSAGE_SELF, &newRUsage)) {
       XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS,
                               int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
     }
 #endif
   }
 
-  int result;
-  {
-    ScopedLogging log;
-    result = do_main(argc, argv, xreDirectory);
-  }
+  int result = do_main(argc, argv, xreDirectory);
+
+  NS_LogTerm();
 
 #ifdef XP_MACOSX
   // Allow writes again. While we would like to catch writes from static
   // destructors to allow early exits to use _exit, we know that there is
   // at least one such write that we don't control (see bug 826029). For
   // now we enable writes again and early exits will have to use exit instead
   // of _exit.
   XRE_DisableWritePoisoning();
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -157,16 +157,25 @@
       <menuitem id="context-sendvideo"
                 label="&emailVideoCmd.label;"
                 accesskey="&emailVideoCmd.accesskey;"
                 oncommand="gContextMenu.sendMedia();"/>
       <menuitem id="context-sendaudio"
                 label="&emailAudioCmd.label;"
                 accesskey="&emailAudioCmd.accesskey;"
                 oncommand="gContextMenu.sendMedia();"/>
+      <menuitem id="context-ctp-play"
+                label="&playPluginCmd.label;"
+                accesskey="&playPluginCmd.accesskey;"
+                oncommand="gContextMenu.playPlugin();"/>
+      <menuitem id="context-ctp-hide"
+                label="&hidePluginCmd.label;"
+                accesskey="&hidePluginCmd.accesskey;"
+                oncommand="gContextMenu.hidePlugin();"/>
+      <menuseparator id="context-sep-ctp"/>
       <menuitem id="context-back"
                 label="&backCmd.label;"
                 accesskey="&backCmd.accesskey;"
                 command="Browser:BackOrBackDuplicate"
                 onclick="checkForMiddleClick(this, event);"/>
       <menuitem id="context-forward"
                 label="&forwardCmd.label;"
                 accesskey="&forwardCmd.accesskey;"
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -339,16 +339,22 @@ var gPluginHandler = {
     if (notification) {
       notification.remove();
     }
     if (pluginNeedsActivation) {
       gPluginHandler._showClickToPlayNotification(browser);
     }
   },
 
+  hideClickToPlayOverlay: function(aPlugin) {
+    let overlay = aPlugin.ownerDocument.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
+    if (overlay)
+      overlay.style.visibility = "hidden";
+  },
+
   stopPlayPreview: function PH_stopPlayPreview(aPlugin, aPlayPlugin) {
     let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
     if (objLoadingContent.activated)
       return;
 
     if (aPlayPlugin)
       objLoadingContent.playPlugin();
     else
@@ -441,22 +447,29 @@ var gPluginHandler = {
       objLoadingContent.playPlugin();
       return;
     }
 
     if (overlay) {
       overlay.addEventListener("click", function(aEvent) {
         // Have to check that the target is not the link to update the plugin
         if (!(aEvent.originalTarget instanceof HTMLAnchorElement) &&
+            !(aEvent.originalTarget instanceof HTMLButtonElement) &&
             aEvent.button == 0 && aEvent.isTrusted) {
           gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin);
           aEvent.stopPropagation();
           aEvent.preventDefault();
         }
       }, true);
+
+      let closeIcon = doc.getAnonymousElementByAttribute(aPlugin, "anonid", "closeIcon");
+      closeIcon.addEventListener("click", function(aEvent) {
+        if (aEvent.button == 0 && aEvent.isTrusted)
+          gPluginHandler.hideClickToPlayOverlay(aPlugin);
+      }, true);
     }
 
     gPluginHandler._showClickToPlayNotification(browser);
   },
 
   _handlePlayPreviewEvent: function PH_handlePlayPreviewEvent(aPlugin) {
     let doc = aPlugin.ownerDocument;
     let previewContent = doc.getAnonymousElementByAttribute(aPlugin, "class", "previewPluginContent");
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -52,16 +52,17 @@ nsContextMenu.prototype = {
     this.initNavigationItems();
     this.initViewItems();
     this.initMiscItems();
     this.initSpellingItems();
     this.initSaveItems();
     this.initClipboardItems();
     this.initMediaPlayerItems();
     this.initLeaveDOMFullScreenItems();
+    this.initClickToPlayItems();
   },
 
   initPageMenuSeparator: function CM_initPageMenuSeparator() {
     this.showItem("page-menu-separator", this.hasPageMenu);
   },
 
   initOpenItems: function CM_initOpenItems() {
     var isMailtoInternal = false;
@@ -412,16 +413,22 @@ nsContextMenu.prototype = {
         this.setItemAttr("context-video-fullscreen", "disabled", hasError);
         this.setItemAttr("context-video-showstats", "disabled", hasError);
         this.setItemAttr("context-video-hidestats", "disabled", hasError);
       }
     }
     this.showItem("context-media-sep-commands",  onMedia);
   },
 
+  initClickToPlayItems: function() {
+    this.showItem("context-ctp-play", this.onCTPPlugin);
+    this.showItem("context-ctp-hide", this.onCTPPlugin);
+    this.showItem("context-sep-ctp", this.onCTPPlugin);
+  },
+
   inspectNode: function CM_inspectNode() {
     let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
     let imported = {};
     Cu.import("resource:///modules/devtools/Target.jsm", imported);
     let tt = imported.TargetFactory.forTab(gBrowser.selectedTab);
     return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
       let inspector = toolbox.getCurrentPanel();
       inspector.selection.setNode(this.target, "browser-context-menu");
@@ -457,16 +464,17 @@ nsContextMenu.prototype = {
     this.linkProtocol      = "";
     this.onMathML          = false;
     this.inFrame           = false;
     this.inSyntheticDoc    = false;
     this.hasBGImage        = false;
     this.bgImageURL        = "";
     this.onEditableArea    = false;
     this.isDesignMode      = false;
+    this.onCTPPlugin       = false;
 
     // Remember the node that was clicked.
     this.target = aNode;
 
     // Check if we are in a synthetic document (stand alone image, video, etc.).
     this.inSyntheticDoc =  this.target.ownerDocument.mozSyntheticDocument;
     // First, do checks for nodes that never have children.
     if (this.target.nodeType == Node.ELEMENT_NODE) {
@@ -534,16 +542,22 @@ nsContextMenu.prototype = {
           }
           if (computedURL) {
             this.hasBGImage = true;
             this.bgImageURL = makeURLAbsolute(bodyElt.baseURI,
                                               computedURL);
           }
         }
       }
+      else if ((this.target instanceof HTMLEmbedElement ||
+                this.target instanceof HTMLObjectElement ||
+                this.target instanceof HTMLAppletElement) &&
+               this.target.mozMatchesSelector(":-moz-handler-clicktoplay")) {
+        this.onCTPPlugin = true;
+      }
     }
 
     // Second, bubble out, looking for items of interest that can have childen.
     // Always pick the innermost link, background image, etc.
     const XMLNS = "http://www.w3.org/XML/1998/namespace";
     var elem = this.target;
     while (elem) {
       if (elem.nodeType == Node.ELEMENT_NODE) {
@@ -1104,16 +1118,24 @@ nsContextMenu.prototype = {
     if (this.onCanvas || this.onImage)
         this.sendMedia();
   },
 
   sendMedia: function() {
     MailIntegration.sendMessage(this.mediaURL, "");
   },
 
+  playPlugin: function() {
+    gPluginHandler.activateSinglePlugin(this.target.ownerDocument.defaultView.top, this.target);
+  },
+
+  hidePlugin: function() {
+    gPluginHandler.hideClickToPlayOverlay(this.target);
+  },
+
   // Generate email address and put it on clipboard.
   copyEmail: function() {
     // Copy the comma-separated list of email addresses only.
     // There are other ways of embedding email addresses in a mailto:
     // link, but such complex parsing is beyond us.
     var url = this.linkURL;
     var qmark = url.indexOf("?");
     var addresses;
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -182,17 +182,17 @@ endif
                  browser_overflowScroll.js \
                  browser_locationBarCommand.js \
                  browser_locationBarExternalLoad.js \
                  browser_page_style_menu.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
                  browser_pluginnotification.js \
                  browser_plugins_added_dynamically.js \
-                 browser_clickToPlayPluginScriptAccessPopup.js \
+                 browser_CTPScriptPlugin.js \
                  browser_pluginplaypreview.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
                  browser_sanitize-sitepermissions.js \
                  browser_sanitize-timespans.js \
                  browser_clearplugindata.js \
                  browser_clearplugindata.html \
                  browser_clearplugindata_noage.html \
rename from browser/base/content/test/browser_clickToPlayPluginScriptAccessPopup.js
rename to browser/base/content/test/browser_CTPScriptPlugin.js
--- a/browser/base/content/test/subtst_contextmenu.html
+++ b/browser/base/content/test/subtst_contextmenu.html
@@ -60,10 +60,11 @@ Browser context menu subtest.
     <menuitem></menuitem>
   </menu>
 </div>
 <div id="test-select-text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</div>
 <div id="test-select-text-link">http://mozilla.com</div>
 <a id="test-image-link" href="#"><img src="ctxmenu-image.png"></a>
 <input id="test-select-input-text" type="text" value="input">
 <input id="test-select-input-text-type-password" type="password" value="password">
+<embed id="test-plugin" style="width: 200px; height: 200px;" type="application/x-test"></embed>
 </body>
 </html>
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -858,33 +858,57 @@ function runTest(testNum) {
                          ].concat(inspectItems));
         closeContextMenu();
         selectInputText(select_inputtext_password); // Select text prior to opening context menu.
         openContextMenuFor(select_inputtext_password); // Invoke context menu for next test.
         return;
 
     case 28:
         // Context menu for selected text in input[type="password"]
-          checkContextMenu(["context-undo",        false,
-                            "---",                 null,
-                            "context-cut",         true,
-                            "context-copy",        true,
-                            "context-paste",       null, // ignore clipboard state
-                            "context-delete",      true,
-                            "---",                 null,
-                            "context-selectall",   true,
-                            "---",                 null,
-                            "spell-check-enabled", true,
-                            //spell checker is shown on input[type="password"] on this testcase
-                            "spell-dictionaries",  true,
-                                ["spell-check-dictionary-en-US", true,
-                                 "---",                          null,
-                                 "spell-add-dictionaries",       true], null
-                           ].concat(inspectItems));
+        checkContextMenu(["context-undo",        false,
+                          "---",                 null,
+                          "context-cut",         true,
+                          "context-copy",        true,
+                          "context-paste",       null, // ignore clipboard state
+                          "context-delete",      true,
+                          "---",                 null,
+                          "context-selectall",   true,
+                          "---",                 null,
+                          "spell-check-enabled", true,
+                          //spell checker is shown on input[type="password"] on this testcase
+                          "spell-dictionaries",  true,
+                              ["spell-check-dictionary-en-US", true,
+                               "---",                          null,
+                               "spell-add-dictionaries",       true], null
+                         ].concat(inspectItems));
         closeContextMenu();
+        subwindow.getSelection().removeAllRanges();
+        openContextMenuFor(plugin);
+        return;
+
+    case 29:
+        // Context menu for click-to-play blocked plugin
+        checkContextMenu(["context-ctp-play",     true,
+                          "context-ctp-hide",     true,
+                          "---",                  null,
+                          "context-back",         false,
+                          "context-forward",      false,
+                          "context-reload",       true,
+                          "---",                  null,
+                          "context-bookmarkpage", true,
+                          "context-savepage",     true,
+                          "---",                  null,
+                          "context-viewbgimage",  false,
+                          "context-selectall",    true,
+                          "---",                  null,
+                          "context-viewsource",   true,
+                          "context-viewinfo",     true
+                         ].concat(inspectItems));
+        closeContextMenu();
+        SpecialPowers.clearUserPref("plugins.click_to_play");
 
         // finish test
         subwindow.close();
         SimpleTest.finish();
         return;
 
     /*
      * Other things that would be nice to test:
@@ -903,17 +927,18 @@ function runTest(testNum) {
 }
 
 
 var testNum = 1;
 var subwindow, chromeWin, contextMenu, lastElement;
 var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2,
     iframe, video_in_iframe, image_in_iframe, textarea, contenteditable,
     inputspell, pagemenu, dom_full_screen, plainTextItems, audio_in_video,
-    selecttext, selecttextlink, imagelink, select_inputtext, select_inputtext_password;
+    selecttext, selecttextlink, imagelink, select_inputtext, select_inputtext_password,
+    plugin;
 
 function startTest() {
     chromeWin = SpecialPowers.wrap(subwindow)
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
                     .QueryInterface(Ci.nsIDocShellTreeItem)
                     .rootTreeItem
                     .QueryInterface(Ci.nsIInterfaceRequestor)
@@ -951,16 +976,17 @@ function startTest() {
     contenteditable.focus(); // content editable needs to be focused to enable spellcheck
     inputspell = subwindow.document.getElementById("test-input-spellcheck");
     pagemenu = subwindow.document.getElementById("test-pagemenu");
     dom_full_screen = subwindow.document.getElementById("test-dom-full-screen");
     selecttext = subwindow.document.getElementById("test-select-text");
     selecttextlink = subwindow.document.getElementById("test-select-text-link");
     select_inputtext = subwindow.document.getElementById("test-select-input-text");
     select_inputtext_password = subwindow.document.getElementById("test-select-input-text-type-password");
+    plugin = subwindow.document.getElementById("test-plugin");
 
     contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }, false);
     runTest(1);
 }
 
 // We open this in a separate window, because the Mochitests run inside a frame.
 // The frame causes an extra menu item, and prevents running the test
 // standalone (ie, clicking the test name in the Mochitest window) to see
@@ -975,16 +1001,18 @@ function waitForEvents(event)
     loaded = true;
   if (painted && loaded) {
     subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
     subwindow.onload = null;
     startTest();
   }
 }
 
+SpecialPowers.setBoolPref("plugins.click_to_play", true);
+
 var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
 subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
 subwindow.onload = waitForEvents;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -743,16 +743,18 @@ DownloadElementShell.prototype = {
    * At the first time an item is selected, we don't yet have
    * the target file information.  Thus the call to goUpdateDownloadCommands
    * in DPV_onSelect would result in best-guess enabled/disabled result.
    * That way we let the user perform command immediately. However, once
    * we have the target file information, we can update the commands
    * appropriately (_fetchTargetFileInfo() calls goUpdateDownloadCommands).
    */
   onSelect: function DES_onSelect() {
+    if (!this.active)
+      return;
     if (!this._targetFileInfoFetched)
       this._fetchTargetFileInfo();
   }
 };
 
 /**
  * A Downloads Places View is a places view designed to show a places query
  * for history donwloads alongside the current "session"-downloads.
@@ -879,17 +881,17 @@ DownloadsPlacesView.prototype = {
    *        a document fragment may be passed to which the new elements would
    *        be appended. It's the caller's job to ensure the fragment is merged
    *        to the richlistbox at the end.
    */
   _addDownloadData:
   function DPV_addDownloadData(aDataItem, aPlacesNode, aNewest = false,
                                aDocumentFragment = null) {
     let downloadURI = aPlacesNode ? aPlacesNode.uri : aDataItem.uri;
-    let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI, null);
+    let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI);
     if (!shellsForURI) {
       shellsForURI = new Set();
       this._downloadElementsShellsForURI.set(downloadURI, shellsForURI);
     }
 
     let newOrUpdatedShell = null;
 
     // Trivial: if there are no shells for this download URI, we always
@@ -991,17 +993,17 @@ DownloadsPlacesView.prototype = {
     this._richlistbox.removeChild(aElement);
     this._ensureVisibleElementsAreActive();
     goUpdateCommand("downloadsCmd_clearDownloads");
   },
 
   _removeHistoryDownloadFromView:
   function DPV__removeHistoryDownloadFromView(aPlacesNode) {
     let downloadURI = aPlacesNode.uri;
-    let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI, null);
+    let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI);
     if (shellsForURI) {
       for (let shell of shellsForURI) {
         if (shell.dataItem) {
           shell.placesNode = null;
         }
         else {
           this._removeElement(shell.element);
           shellsForURI.delete(shell);
@@ -1009,17 +1011,17 @@ DownloadsPlacesView.prototype = {
             this._downloadElementsShellsForURI.delete(downloadURI);
         }
       }
     }
   },
 
   _removeSessionDownloadFromView:
   function DPV__removeSessionDownloadFromView(aDataItem) {
-    let shells = this._downloadElementsShellsForURI.get(aDataItem.uri, null);
+    let shells = this._downloadElementsShellsForURI.get(aDataItem.uri);
     if (shells.size == 0)
       throw new Error("Should have had at leaat one shell for this uri");
 
     let shell = this.getViewItem(aDataItem);
     if (!shells.has(shell))
       throw new Error("Missing download element shell in shells list for url");
 
     // If there's more than one item for this download uri, we can let the
@@ -1164,19 +1166,24 @@ DownloadsPlacesView.prototype = {
 
   invalidateContainer:
   function DPV_invalidateContainer(aContainer) {
     if (aContainer != this._resultNode)
       throw new Error("Unexpected container node");
     if (!aContainer.containerOpen)
       throw new Error("Root container for the downloads query cannot be closed");
 
+    let suppressOnSelect = this._richlistbox.suppressOnSelect;
+    this._richlistbox.suppressOnSelect = true;
+
     // Remove the invalidated history downloads from the list and unset the
     // places node for data downloads.
-    for (let element of this._richlistbox.childNodes) {
+    // Loop backwards since _removeHistoryDownloadFromView may removeChild().
+    for (let i = this._richlistbox.childNodes.length - 1; i >= 0; --i) {
+      let element = this._richlistbox.childNodes[i];
       if (element._shell.placesNode)
         this._removeHistoryDownloadFromView(element._shell.placesNode);
     }
 
     let elementsToAppendFragment = document.createDocumentFragment();
     for (let i = 0; i < aContainer.childCount; i++) {
       try {
         this._addDownloadData(null, aContainer.getChild(i), false,
@@ -1184,16 +1191,18 @@ DownloadsPlacesView.prototype = {
       }
       catch(ex) {
         Cu.reportError(ex);
       }
     }
 
     this._appendDownloadsFragment(elementsToAppendFragment);
     this._ensureVisibleElementsAreActive();
+
+    this._richlistbox.suppressOnSelect = suppressOnSelect;
     goUpdateDownloadCommands();
   },
 
   _appendDownloadsFragment: function DPV__appendDownloadsFragment(aDOMFragment) {
     // Workaround multiple reflows hang by removing the richlistbox
     // and adding it back when we're done.
     let parentNode = this._richlistbox.parentNode;
     let nextSibling = this._richlistbox.nextSibling;
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -423,16 +423,20 @@ These should match what Safari and other
 <!ENTITY saveAudioCmd.label           "Save Audio As…">
 <!ENTITY saveAudioCmd.accesskey       "v">
 <!ENTITY emailImageCmd.label          "Email Image…">
 <!ENTITY emailImageCmd.accesskey      "g">
 <!ENTITY emailVideoCmd.label          "Email Video…">
 <!ENTITY emailVideoCmd.accesskey      "a">
 <!ENTITY emailAudioCmd.label          "Email Audio…">
 <!ENTITY emailAudioCmd.accesskey      "a">
+<!ENTITY playPluginCmd.label          "Activate this plugin">
+<!ENTITY playPluginCmd.accesskey      "c">
+<!ENTITY hidePluginCmd.label          "Hide this plugin">
+<!ENTITY hidePluginCmd.accesskey      "H">
 <!ENTITY copyLinkCmd.label            "Copy Link Location">
 <!ENTITY copyLinkCmd.accesskey        "a">
 <!ENTITY copyImageCmd.label           "Copy Image Location">
 <!ENTITY copyImageCmd.accesskey       "o">
 <!ENTITY copyImageContentsCmd.label   "Copy Image">
 <!ENTITY copyImageContentsCmd.accesskey  "y"> 
 <!ENTITY copyVideoURLCmd.label        "Copy Video Location">
 <!ENTITY copyVideoURLCmd.accesskey    "o">
index 6abb7d10ade04526dd6a5c1898acbf84622f2792..8b83fdc84177048c31088130e112ff5433750ad3
GIT binary patch
literal 10575
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
index 1d66333ae00e23d52a13206ee11696d8a4038f1c..521c6c3887b4ccd4ad96abb77d69a94551d76da1
GIT binary patch
literal 6679
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
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -4176,16 +4176,23 @@ panel[type="arrow"][popupid="click-to-pl
 %ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 #main-window[privatebrowsingmode=temporary] {
   background-image: url("chrome://browser/skin/privatebrowsing-mask.png");
   background-position: top right;
   background-repeat: no-repeat;
   background-color: -moz-mac-chrome-active;
 }
 
+@media (min-resolution: 2dppx) {
+  #main-window[privatebrowsingmode=temporary] {
+    background-image: url("chrome://browser/skin/privatebrowsing-mask@2x.png");
+    background-size: 38px;
+  }
+}
+
 @media (-moz-mac-lion-theme) {
   #main-window[privatebrowsingmode=temporary] {
     background-position: top right 40px;
   }
 
   #main-window[privatebrowsingmode=temporary]:-moz-locale-dir(rtl) {
     background-position: top left 70px;
   }
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -44,16 +44,17 @@ browser.jar:
   skin/classic/browser/panel-expander-open@2x.png
   skin/classic/browser/panel-plus-sign.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/page-livemarks@2x.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/privatebrowsing-mask.png
+  skin/classic/browser/privatebrowsing-mask@2x.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/searchbar-dropmarker.png
   skin/classic/browser/searchbar-dropmarker@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/Search.png
   skin/classic/browser/Search@2x.png
   skin/classic/browser/Secure-Glyph.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ec1cf7452ef6e0251dad9113e8119793bfed4b6f
GIT binary patch
literal 2639
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
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -481,16 +481,17 @@ user_pref("font.size.inflation.minTwips"
 // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
 user_pref("extensions.enabledScopes", 5);
 // Disable metadata caching for installed add-ons by default
 user_pref("extensions.getAddons.cache.enabled", false);
 // Disable intalling any distribution add-ons
 user_pref("extensions.installDistroAddons", false);
 
 user_pref("extensions.testpilot.runStudies", false);
+user_pref("extensions.testpilot.alreadyCustomizedToolbar", true);
 
 user_pref("geo.wifi.uri", "http://%(server)s/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs");
 user_pref("geo.wifi.testing", true);
 user_pref("geo.ignore.location_filter", true);
 
 user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
 
 // Make url-classifier updates so rare that they won't affect tests
--- a/build/buildconfig.py
+++ b/build/buildconfig.py
@@ -17,11 +17,11 @@ path = os.path.join(path, 'config.status
 config = imp.load_module('_buildconfig', open(path), path, ('', 'r', imp.PY_SOURCE))
 
 for var in os.environ:
     if var in config.substs:
         config.substs[var] = os.environ[var]
 
 for var in config.__all__:
     value = getattr(config, var)
-    if isinstance(value, list) and isinstance(value[0], tuple):
+    if isinstance(value, list) and value and isinstance(value[0], tuple):
         value = dict(value)
     setattr(sys.modules[__name__], var, value)
--- a/build/pymake/pymake/data.py
+++ b/build/pymake/pymake/data.py
@@ -35,17 +35,18 @@ def mtimeislater(deptime, targettime):
     """
     Is the mtime of the dependency later than the target?
     """
 
     if deptime is None:
         return True
     if targettime is None:
         return False
-    return deptime > targettime
+    # int(1000*x) because of http://bugs.python.org/issue10148
+    return int(1000 * deptime) > int(1000 * targettime)
 
 def getmtime(path):
     try:
         s = os.stat(path)
         return s.st_mtime
     except OSError:
         return None
 
--- a/config/config.mk
+++ b/config/config.mk
@@ -753,18 +753,18 @@ endif
 
 OPTIMIZE_JARS_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/optimizejars.py)
 
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
 # MDDEPDIR is the subdirectory where dependency files are stored
 MDDEPDIR := .deps
 
-EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py $(if $@,--depend $(MDDEPDIR)/$(@F).pp --target $@)
-EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py $(if $@,--depend $(MDDEPDIR)/$(@F).pp)
+EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/expandlibs_exec.py $(if $@,--depend $(MDDEPDIR)/$(@F).pp --target $@)
+EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/expandlibs_gen.py $(if $@,--depend $(MDDEPDIR)/$(@F).pp)
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
 EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
 EXPAND_MKSHLIB_ARGS = --uselist
 ifdef SYMBOL_ORDER
 EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
 endif
rename from config/expandlibs_config.py.in
rename to config/expandlibs_config.py
--- a/config/expandlibs_config.py.in
+++ b/config/expandlibs_config.py
@@ -1,25 +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/.
 
+from buildconfig import substs
+
 def normalize_suffix(suffix):
     '''Returns a normalized suffix, i.e. ensures it starts with a dot and
     doesn't starts or ends with whitespace characters'''
     value = suffix.strip()
     if len(value) and not value.startswith('.'):
         value = '.' + value
     return value
 
 # Variables from the build system
-AR = "@AR@"
-AR_EXTRACT = "@AR_EXTRACT@".replace('$(AR)', AR)
-DLL_PREFIX = "@DLL_PREFIX@"
-LIB_PREFIX = "@LIB_PREFIX@"
-OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@")
-LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@")
-DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@")
-IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@")
-LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@")
-EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@"
-EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@"
-LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
+AR = substs['AR']
+AR_EXTRACT = substs['AR_EXTRACT'].replace('$(AR)', AR)
+DLL_PREFIX = substs['DLL_PREFIX']
+LIB_PREFIX = substs['LIB_PREFIX']
+OBJ_SUFFIX = normalize_suffix(substs['OBJ_SUFFIX'])
+LIB_SUFFIX = normalize_suffix(substs['LIB_SUFFIX'])
+DLL_SUFFIX = normalize_suffix(substs['DLL_SUFFIX'])
+IMPORT_LIB_SUFFIX = normalize_suffix(substs['IMPORT_LIB_SUFFIX'])
+LIBS_DESC_SUFFIX = normalize_suffix(substs['LIBS_DESC_SUFFIX'])
+EXPAND_LIBS_LIST_STYLE = substs['EXPAND_LIBS_LIST_STYLE']
+EXPAND_LIBS_ORDER_STYLE = substs['EXPAND_LIBS_ORDER_STYLE']
+LD_PRINT_ICF_SECTIONS = substs['LD_PRINT_ICF_SECTIONS']
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -95,18 +95,18 @@ class HTMLBodyElement;
 class Link;
 class ProcessingInstruction;
 class UndoManager;
 template<typename> class Sequence;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0xff03d72f, 0x87cd, 0x4d11, \
- { 0x81, 0x8d, 0xa8, 0xb4, 0xf5, 0x98, 0x1a, 0x10 } }
+{ 0x2df7f766, 0xf70b, 0x4de4, \
+ { 0xb0, 0xba, 0x78, 0x25, 0x07, 0x41, 0xd6, 0xce } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
@@ -563,16 +563,22 @@ public:
   /**
    * Return the root element for this document.
    */
   Element* GetRootElement() const;
 
   virtual nsViewportInfo GetViewportInfo(uint32_t aDisplayWidth,
                                          uint32_t aDisplayHeight) = 0;
 
+  /**
+   * True iff this doc will ignore manual character encoding overrides.
+   */
+  virtual bool WillIgnoreCharsetOverride() {
+    return true;
+  }
 
 protected:
   virtual Element *GetRootElementInternal() const = 0;
 
 public:
   // Get the root <html> element, or return null if there isn't one (e.g.
   // if the root isn't <html>)
   Element* GetHtmlElement();
--- a/content/base/src/nsDOMTokenList.cpp
+++ b/content/base/src/nsDOMTokenList.cpp
@@ -26,19 +26,20 @@ nsDOMTokenList::nsDOMTokenList(Element* 
   // we'll be told to drop our reference
   SetIsDOMBinding();
 }
 
 nsDOMTokenList::~nsDOMTokenList() { }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMTokenList)
 
-NS_INTERFACE_TABLE_HEAD(nsDOMTokenList)
+NS_INTERFACE_MAP_BEGIN(nsDOMTokenList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDOMTokenList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMTokenList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList)
 
 void
 nsDOMTokenList::DropReference()
 {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1276,17 +1276,16 @@ nsDOMStyleSheetSetList::GetSets(nsTArray
 {
   if (!mDocument) {
     return NS_OK; // Spec says "no exceptions", and we have no style sets if we
                   // have no document, for sure
   }
 
   int32_t count = mDocument->GetNumberOfStyleSheets();
   nsAutoString title;
-  nsAutoString temp;
   for (int32_t index = 0; index < count; index++) {
     nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
     NS_ASSERTION(sheet, "Null sheet in sheet list!");
     sheet->GetTitle(title);
     if (!title.IsEmpty() && !aStyleSets.Contains(title) &&
         !aStyleSets.AppendElement(title)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
@@ -3182,41 +3181,36 @@ nsDocument::SetHeaderData(nsIAtom* aHead
       aHeaderField == nsGkAtoms::viewport_initial_scale ||
       aHeaderField == nsGkAtoms::viewport_height ||
       aHeaderField == nsGkAtoms::viewport_width ||
       aHeaderField ==  nsGkAtoms::viewport_user_scalable) {
     mViewportType = Unknown;
   }
 }
 
-bool
+void
 nsDocument::TryChannelCharset(nsIChannel *aChannel,
                               int32_t& aCharsetSource,
                               nsACString& aCharset,
                               nsHtml5TreeOpExecutor* aExecutor)
 {
-  if(kCharsetFromChannel <= aCharsetSource) {
-    return true;
-  }
-
   if (aChannel) {
     nsAutoCString charsetVal;
     nsresult rv = aChannel->GetContentCharset(charsetVal);
     if (NS_SUCCEEDED(rv)) {
       nsAutoCString preferred;
       if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) {
         aCharset = preferred;
         aCharsetSource = kCharsetFromChannel;
-        return true;
+        return;
       } else if (aExecutor && !charsetVal.IsEmpty()) {
         aExecutor->ComplainAboutBogusProtocolCharset(this);
       }
     }
   }
-  return false;
 }
 
 nsresult
 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
                         nsStyleSet* aStyleSet,
                         nsIPresShell** aInstancePtrResult)
 {
   // Don't add anything here.  Add it to |doCreateShell| instead.
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1045,17 +1045,17 @@ protected:
   }
 
   void ReportEmptyGetElementByIdArg();
 
   void DispatchContentLoadedEvents();
 
   void RetrieveRelevantHeaders(nsIChannel *aChannel);
 
-  bool TryChannelCharset(nsIChannel *aChannel,
+  void TryChannelCharset(nsIChannel *aChannel,
                          int32_t& aCharsetSource,
                          nsACString& aCharset,
                          nsHtml5TreeOpExecutor* aExecutor);
 
   // Call this before the document does something that will unbind all content.
   // That will stop us from doing a lot of work as each element is removed.
   void DestroyElementMaps();
 
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -104,29 +104,16 @@ nsImageLoadingContent::DestroyImageLoadi
 nsImageLoadingContent::~nsImageLoadingContent()
 {
   NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
                "DestroyImageLoadingContent not called");
   NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
                "Observers still registered?");
 }
 
-// Macro to call some func on each observer.  This handles observers
-// removing themselves.
-#define LOOP_OVER_OBSERVERS(func_)                                       \
-  PR_BEGIN_MACRO                                                         \
-    for (ImageObserver* observer = &mObserverList, *next; observer;      \
-         observer = next) {                                              \
-      next = observer->mNext;                                            \
-      if (observer->mObserver) {                                         \
-        observer->mObserver->func_;                                      \
-      }                                                                  \
-    }                                                                    \
-  PR_END_MACRO
-
 /*
  * imgINotificationObserver impl
  */
 NS_IMETHODIMP
 nsImageLoadingContent::Notify(imgIRequest* aRequest,
                               int32_t aType,
                               const nsIntRect* aData)
 {
@@ -137,17 +124,27 @@ nsImageLoadingContent::Notify(imgIReques
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     // We should definitely have a request here
     NS_ABORT_IF_FALSE(aRequest, "no request?");
 
     NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
                     "Unknown request");
   }
 
-  LOOP_OVER_OBSERVERS(Notify(aRequest, aType, aData));
+  {
+    nsAutoScriptBlocker scriptBlocker;
+
+    for (ImageObserver* observer = &mObserverList, *next; observer;
+         observer = next) {
+      next = observer->mNext;
+      if (observer->mObserver) {
+        observer->mObserver->Notify(aRequest, aType, aData);
+      }
+    }
+  }
 
   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     // Have to check for state changes here, since we might have been in
     // the LOADING state before.
     UpdateImageState(true);
   }
 
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 sw=2 et tw=80: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Util.h"
 
 #include "nsXMLHttpRequest.h"
 #include "nsISimpleEnumerator.h"
@@ -633,30 +633,58 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+  NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
 
 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
 
 NS_IMPL_EVENT_HANDLER(nsXMLHttpRequest, readystatechange)
 
 void
 nsXMLHttpRequest::DisconnectFromOwner()
 {
   nsXHREventTarget::DisconnectFromOwner();
   Abort();
 }
 
+size_t
+nsXMLHttpRequest::SizeOfEventTargetIncludingThis(
+  nsMallocSizeOfFun aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+  n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+  // Why is this safe?  Because no-one else will report this string.  The
+  // other possible sharers of this string are as follows.
+  //
+  // - The JS engine could hold copies if the JS code holds references, e.g.
+  //   |var text = XHR.responseText|.  However, those references will be via JS
+  //   external strings, for which the JS memory reporter does *not* report the
+  //   chars.
+  //
+  // - Binary extensions, but they're *extremely* unlikely to do any memory
+  //   reporting.
+  //
+  n += mResponseText.SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
+
+  return n;
+
+  // Measurement of the following members may be added later if DMD finds it is
+  // worthwhile:
+  // - lots
+}
+
 /* readonly attribute nsIChannel channel; */
 NS_IMETHODIMP
 nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
 {
   NS_ENSURE_ARG_POINTER(aChannel);
   NS_IF_ADDREF(*aChannel = mChannel);
 
   return NS_OK;
@@ -3853,17 +3881,19 @@ JS::Value
 nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv)
 {
   const nsID* iid = aIID->GetID();
   nsCOMPtr<nsISupports> result;
   JS::Value v = JSVAL_NULL;
   aRv = GetInterface(*iid, getter_AddRefs(result));
   NS_ENSURE_FALSE(aRv.Failed(), JSVAL_NULL);
 
-  JSObject* global = JS_GetGlobalForObject(aCx, GetWrapper());
+  JSObject* wrapper = GetWrapper();
+  JSAutoCompartment ac(aCx, wrapper);
+  JSObject* global = JS_GetGlobalForObject(aCx, wrapper);
   aRv = nsContentUtils::WrapNative(aCx, global, result, iid, &v);
   return aRv.Failed() ? JSVAL_NULL : v;
 }
 
 nsXMLHttpRequestUpload*
 nsXMLHttpRequest::Upload()
 {
   if (!mUpload) {
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsXMLHttpRequest_h__
 #define nsXMLHttpRequest_h__
 
 #include "nsIXMLHttpRequest.h"
@@ -30,16 +31,17 @@
 #include "nsITimer.h"
 #include "nsIDOMProgressEvent.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsContentUtils.h"
 #include "nsDOMFile.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
+#include "nsISizeOfEventTarget.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/dom/EventHandlerBinding.h"
 
@@ -119,17 +121,18 @@ class nsXMLHttpRequest : public nsXHREve
                          public nsIXMLHttpRequest,
                          public nsIJSXMLHttpRequest,
                          public nsIStreamListener,
                          public nsIChannelEventSink,
                          public nsIProgressEventSink,
                          public nsIInterfaceRequestor,
                          public nsSupportsWeakReference,
                          public nsIJSNativeInitializer,
-                         public nsITimerCallback
+                         public nsITimerCallback,
+                         public nsISizeOfEventTarget
 {
   friend class nsXHRParseEndListener;
   friend class nsXMLHttpRequestXPCOMifier;
 
 public:
   nsXMLHttpRequest();
   virtual ~nsXMLHttpRequest();
 
@@ -224,16 +227,20 @@ public:
 
   // nsITimerCallback
   NS_DECL_NSITIMERCALLBACK
 
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
                        uint32_t argc, jsval* argv);
 
+  // nsISizeOfEventTarget
+  virtual size_t
+    SizeOfEventTargetIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
   NS_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget::)
 
 #ifdef DEBUG
   void StaticAssertions();
 #endif
 
   // event handler
   IMPL_EVENT_HANDLER(readystatechange)
@@ -486,16 +493,17 @@ public:
   void SetRequestObserver(nsIRequestObserver* aObserver);
 
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(nsXMLHttpRequest,
                                                                    nsXHREventTarget)
   bool AllowUploadProgress();
   void RootJSResultObjects();
 
   virtual void DisconnectFromOwner();
+
 protected:
   friend class nsMultipartProxyListener;
 
   nsresult DetectCharset();
   nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen);
   static NS_METHOD StreamReaderFunc(nsIInputStream* in,
                 void* closure,
                 const char* fromRawSegment,
@@ -568,17 +576,17 @@ protected:
   nsCString mResponseBody;
 
   // The text version of our response body. This is incrementally decoded into
   // as we receive network data. However for the DEFAULT responseType we
   // lazily decode into this from mResponseBody only when .responseText is
   // accessed.
   // Only used for DEFAULT and TEXT responseTypes.
   nsString mResponseText;
-  
+
   // For DEFAULT responseType we use this to keep track of how far we've
   // lazily decoded from mResponseBody to mResponseText
   uint32_t mResponseBodyDecodedPos;
 
   // Decoder used for decoding into mResponseText
   // Only used for DEFAULT, TEXT and JSON responseTypes.
   // In cases where we've only received half a surrogate, the decoder itself
   // carries the state to remember this. Next time we receive more data we
--- a/content/base/test/test_bug513194.html
+++ b/content/base/test/test_bug513194.html
@@ -12,16 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank"
    href="https://bugzilla.mozilla.org/show_bug.cgi?id=631615"
    >Mozilla Bug 513194</a>
 <script>
 // The use of document.write is deliberate.  We are testing for the
 // HTML parser to call the CSS parser once and only once when it
 // encounters a new <style> element.
+
 SimpleTest.runTestExpectingConsoleMessages(
   function () { document.write("<style>qux { foo : bar; }<\/style>") },
   [{ errorMessage: /Unknown property/ }]
 );
 </script>
 </body>
 </html>
 
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -3639,16 +3639,23 @@ CanvasRenderingContext2D::PutImageData_e
   EnsureTarget();
   if (!IsTargetValid()) {
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<SourceSurface> sourceSurface =
     mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), FORMAT_B8G8R8A8);
 
+  // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
+  // covers the details of how to run into it, but the full detailed
+  // investigation hasn't been done to determine the underlying cause.  We
+  // will just handle the failure to allocate the surface to avoid a crash.
+  if (!sourceSurface) {
+    return NS_ERROR_FAILURE;
+  }
 
   mTarget->CopySurface(sourceSurface,
                        IntRect(dirtyRect.x - x, dirtyRect.y - y,
                                dirtyRect.width, dirtyRect.height),
                        IntPoint(dirtyRect.x, dirtyRect.y));
 
   Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
 
new file mode 100644
--- /dev/null
+++ b/content/html/content/crashtests/832011.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+    var canvas = document.createElement('canvas');
+    var m = Components.lookupMethod(canvas, "itemProp")
+    m();
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/content/html/content/crashtests/crashtests.list
+++ b/content/html/content/crashtests/crashtests.list
@@ -40,8 +40,9 @@ load 795221-1.html
 load 795221-2.html
 load 795221-3.html
 load 795221-4.html
 load 795221-5.xml
 load 798802-1.html
 load 811226.html
 load 819745.html
 load 828180.html
+load 832011.html
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -171,16 +171,19 @@ nsHTMLAudioElement::MozWriteAudio(const 
 
   float* frames = JS_GetFloat32ArrayData(tsrc);
   // Convert the samples back to integers as we are using fixed point audio in
   // the AudioStream.
   // This could be optimized to avoid allocation and memcpy when
   // AudioDataValue is 'float', but it's not worth it for this deprecated API.
   nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[writeLen * mChannels]);
   ConvertAudioSamples(frames, audioData.get(), writeLen * mChannels);
+  if (!mAudioStream->IsStarted()) {
+    mAudioStream->Start();
+  }
   nsresult rv = mAudioStream->Write(audioData.get(), writeLen);
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Return the actual amount written.
   *aRetVal = writeLen * mChannels;
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -3504,22 +3504,24 @@ NS_IMETHODIMP nsHTMLMediaElement::GetPla
 NS_IMETHODIMP nsHTMLMediaElement::SetPlaybackRate(double aPlaybackRate)
 {
   if (aPlaybackRate < 0) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   mPlaybackRate = ClampPlaybackRate(aPlaybackRate);
 
-  if (mPlaybackRate < 0 ||
-      mPlaybackRate > THRESHOLD_HIGH_PLAYBACKRATE_AUDIO ||
-      mPlaybackRate < THRESHOLD_LOW_PLAYBACKRATE_AUDIO) {
-    SetMutedInternal(true);
-  } else {
-    SetMutedInternal(false);
+  if (!mMuted) {
+    if (mPlaybackRate < 0 ||
+        mPlaybackRate > THRESHOLD_HIGH_PLAYBACKRATE_AUDIO ||
+        mPlaybackRate < THRESHOLD_LOW_PLAYBACKRATE_AUDIO) {
+      SetMutedInternal(true);
+    } else {
+      SetMutedInternal(false);
+    }
   }
 
   if (mDecoder) {
     mDecoder->SetPlaybackRate(mPlaybackRate);
   }
   DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
   return NS_OK;
 }
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -100,16 +100,17 @@
 #include "mozilla/Preferences.h"
 #include "nsMimeTypes.h"
 #include "nsIRequest.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5Parser.h"
 #include "nsIDOMJSWindow.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/dom/HTMLBodyElement.h"
+#include "nsCharsetSource.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
 
 #include "prtime.h"
 
@@ -336,79 +337,86 @@ nsHTMLDocument::TryHintCharset(nsIMarkup
         return;
       }
     }
   }
   return;
 }
 
 
-bool
+void
 nsHTMLDocument::TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV,
                                      nsIDocShell*  aDocShell,
                                      int32_t& aCharsetSource,
                                      nsACString& aCharset)
 {
   nsresult rv = NS_OK;
 
   if(kCharsetFromUserForced <= aCharsetSource)
-    return true;
+    return;
+
+  // mCharacterSet not updated yet for channel, so check aCharset, too.
+  if (WillIgnoreCharsetOverride() || !IsAsciiCompatible(aCharset)) {
+    return;
+  }
 
   nsAutoCString forceCharsetFromDocShell;
   if (aMarkupDV) {
+    // XXX mailnews-only
     rv = aMarkupDV->GetForceCharacterSet(forceCharsetFromDocShell);
   }
 
-  // Not making the IsAsciiCompatible() check here to allow the user to
-  // force UTF-16 from the menu.
-  if(NS_SUCCEEDED(rv) && !forceCharsetFromDocShell.IsEmpty()) {
+  if(NS_SUCCEEDED(rv) &&
+     !forceCharsetFromDocShell.IsEmpty() &&
+     IsAsciiCompatible(forceCharsetFromDocShell)) {
     aCharset = forceCharsetFromDocShell;
-    //TODO: we should define appropriate constant for force charset
     aCharsetSource = kCharsetFromUserForced;
-  } else if (aDocShell) {
+    return;
+  }
+
+  if (aDocShell) {
+    // This is the Character Encoding menu code path in Firefox
     nsCOMPtr<nsIAtom> csAtom;
     aDocShell->GetForcedCharset(getter_AddRefs(csAtom));
     if (csAtom) {
-      csAtom->ToUTF8String(aCharset);
+      nsAutoCString charset;
+      csAtom->ToUTF8String(charset);
+      if (!IsAsciiCompatible(charset)) {
+        return;
+      }
+      aCharset = charset;
       aCharsetSource = kCharsetFromUserForced;
       aDocShell->SetForcedCharset(nullptr);
-      return true;
     }
   }
-
-  return false;
 }
 
-bool
+void
 nsHTMLDocument::TryCacheCharset(nsICachingChannel* aCachingChannel,
                                 int32_t& aCharsetSource,
                                 nsACString& aCharset)
 {
   nsresult rv;
 
   if (kCharsetFromCache <= aCharsetSource) {
-    return true;
+    return;
   }
 
   nsCString cachedCharset;
   rv = aCachingChannel->GetCacheTokenCachedCharset(cachedCharset);
   // Check IsAsciiCompatible() even in the cache case, because the value
   // might be stale and in the case of a stale charset that is not a rough
   // ASCII superset, the parser has no way to recover.
   if (NS_SUCCEEDED(rv) &&
       !cachedCharset.IsEmpty() &&
       IsAsciiCompatible(cachedCharset))
   {
     aCharset = cachedCharset;
     aCharsetSource = kCharsetFromCache;
-
-    return true;
   }
-
-  return false;
 }
 
 static bool
 CheckSameOrigin(nsINode* aNode1, nsINode* aNode2)
 {
   NS_PRECONDITION(aNode1, "Null node?");
   NS_PRECONDITION(aNode2, "Null node?");
 
@@ -433,62 +441,79 @@ void
 nsHTMLDocument::TryParentCharset(nsIDocShell*  aDocShell,
                                  nsIDocument* aParentDocument,
                                  int32_t& aCharsetSource,
                                  nsACString& aCharset)
 {
   if (!aDocShell) {
     return;
   }
-  int32_t source;
+  if (aCharsetSource >= kCharsetFromParentForced) {
+    return;
+  }
+
   nsCOMPtr<nsIAtom> csAtom;
   int32_t parentSource;
   nsAutoCString parentCharset;
   aDocShell->GetParentCharset(getter_AddRefs(csAtom));
   if (!csAtom) {
     return;
   }
   aDocShell->GetParentCharsetSource(&parentSource);
   csAtom->ToUTF8String(parentCharset);
-  if (kCharsetFromParentForced <= parentSource) {
-    source = kCharsetFromParentForced;
-  } else if (kCharsetFromHintPrevDoc == parentSource) {
+  if (kCharsetFromParentForced == parentSource ||
+      kCharsetFromUserForced == parentSource) {
+    if (WillIgnoreCharsetOverride() ||
+        !IsAsciiCompatible(aCharset) || // if channel said UTF-16
+        !IsAsciiCompatible(parentCharset)) {
+      return;
+    }
+    aCharset.Assign(parentCharset);
+    aCharsetSource = kCharsetFromParentForced;
+    return;
+  }
+
+  if (aCharsetSource >= kCharsetFromHintPrevDoc) {
+    return;
+  }
+
+  if (kCharsetFromHintPrevDoc == parentSource) {
     // Make sure that's OK
     if (!aParentDocument ||
         !CheckSameOrigin(this, aParentDocument) ||
         !IsAsciiCompatible(parentCharset)) {
       return;
     }
 
     // if parent is posted doc, set this prevent autodetections
     // I'm not sure this makes much sense... but whatever.
-    source = kCharsetFromHintPrevDoc;
-  } else if (kCharsetFromCache <= parentSource) {
+    aCharset.Assign(parentCharset);
+    aCharsetSource = kCharsetFromHintPrevDoc;
+    return;
+  }
+
+  if (aCharsetSource >= kCharsetFromParentFrame) {
+    return;
+  }
+
+  if (kCharsetFromCache <= parentSource) {
     // Make sure that's OK
     if (!aParentDocument ||
         !CheckSameOrigin(this, aParentDocument) ||
         !IsAsciiCompatible(parentCharset)) {
       return;
     }
 
-    source = kCharsetFromParentFrame;
-  } else {
-    return;
+    aCharset.Assign(parentCharset);
+    aCharsetSource = kCharsetFromParentFrame;
   }
-
-  if (source < aCharsetSource) {
-    return;
-  }
-
-  aCharset.Assign(parentCharset);
-  aCharsetSource = source;
 }
 
 void
-nsHTMLDocument::UseWeakDocTypeDefault(int32_t& aCharsetSource,
+nsHTMLDocument::TryWeakDocTypeDefault(int32_t& aCharsetSource,
                                       nsACString& aCharset)
 {
   if (kCharsetFromWeakDocTypeDefault <= aCharsetSource)
     return;
 
   const nsAdoptingCString& defCharset =
     Preferences::GetLocalizedCString("intl.charset.default");
 
@@ -498,38 +523,33 @@ nsHTMLDocument::UseWeakDocTypeDefault(in
     aCharset = defCharset;
   } else {
     aCharset.AssignLiteral("ISO-8859-1");
   }
   aCharsetSource = kCharsetFromWeakDocTypeDefault;
   return;
 }
 
-bool
+void
 nsHTMLDocument::TryDefaultCharset( nsIMarkupDocumentViewer* aMarkupDV,
                                    int32_t& aCharsetSource,
                                    nsACString& aCharset)
 {
   if(kCharsetFromUserDefault <= aCharsetSource)
-    return true;
+    return;
 
   nsAutoCString defaultCharsetFromDocShell;
   if (aMarkupDV) {
     nsresult rv =
       aMarkupDV->GetDefaultCharacterSet(defaultCharsetFromDocShell);
-    // Not making the IsAsciiCompatible() check here to allow the user to
-    // force UTF-16 from the menu.
-    if(NS_SUCCEEDED(rv)) {
+    if(NS_SUCCEEDED(rv) && IsAsciiCompatible(defaultCharsetFromDocShell)) {
       aCharset = defaultCharsetFromDocShell;
-
       aCharsetSource = kCharsetFromUserDefault;
-      return true;
     }
   }
-  return false;
 }
 
 void
 nsHTMLDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
 {
   nsDocument::SetDocumentCharacterSet(aCharSetID);
   // Make sure to stash this charset on our channel as needed if it's a wyciwyg
   // channel.
@@ -731,47 +751,49 @@ nsHTMLDocument::StartDocumentLoad(const 
     parserCharsetSource = charsetSource;
     parserCharset = charset;
   } else {
     NS_ASSERTION(docShell && docShellAsItem, "Unexpected null value");
 
     charsetSource = kCharsetUninitialized;
     wyciwygChannel = do_QueryInterface(aChannel);
 
-    // The following charset resolving calls has implied knowledge
-    // about charset source priority order. Each try will return true
-    // if the source is higher or equal to the source as its name
-    // describes. Some try call might change charset source to
-    // multiple values, like TryHintCharset and TryParentCharset. It
-    // should be always safe to try more sources.
-    if (!TryUserForcedCharset(muCV, docShell, charsetSource, charset)) {
-      TryHintCharset(muCV, charsetSource, charset);
-      TryParentCharset(docShell, parentDocument, charsetSource, charset);
-
-      // Don't actually get the charset from the channel if this is a
-      // wyciwyg channel; it'll always be UTF-16
-      if (!wyciwygChannel &&
-          TryChannelCharset(aChannel, charsetSource, charset, executor)) {
-        // Use the channel's charset (e.g., charset from HTTP
-        // "Content-Type" header).
-      }
-      else if (cachingChan && !urlSpec.IsEmpty() &&
-               TryCacheCharset(cachingChan, charsetSource, charset)) {
-        // Use the cache's charset.
-      }
-      else if (TryDefaultCharset(muCV, charsetSource, charset)) {
-        // Use the default charset.
-        // previous document charset might be inherited as default charset.
-      }
-      else {
-        // Use the weak doc type default charset
-        UseWeakDocTypeDefault(charsetSource, charset);
-      }
+    // The following will try to get the character encoding from various
+    // sources. Each Try* function will return early if the source is already
+    // at least as large as any of the sources it might look at.  Some of
+    // these functions (like TryHintCharset and TryParentCharset) can set
+    // charsetSource to various values depending on where the charset they
+    // end up finding originally comes from.
+
+    // Don't actually get the charset from the channel if this is a
+    // wyciwyg channel; it'll always be UTF-16
+    if (!wyciwygChannel) {
+      // Otherwise, try the channel's charset (e.g., charset from HTTP
+      // "Content-Type" header) first. This way, we get to reject overrides in
+      // TryParentCharset and TryUserForcedCharset if the channel said UTF-16.
+      // This is to avoid socially engineered XSS by adding user-supplied
+      // content to a UTF-16 site such that the byte have a dangerous
+      // interpretation as ASCII and the user can be lured to using the
+      // charset menu.
+      TryChannelCharset(aChannel, charsetSource, charset, executor);
     }
 
+    TryUserForcedCharset(muCV, docShell, charsetSource, charset);
+
+    TryHintCharset(muCV, charsetSource, charset); // XXX mailnews-only
+    TryParentCharset(docShell, parentDocument, charsetSource, charset);
+
+    if (cachingChan && !urlSpec.IsEmpty()) {
+      TryCacheCharset(cachingChan, charsetSource, charset);
+    }
+
+    TryDefaultCharset(muCV, charsetSource, charset);
+
+    TryWeakDocTypeDefault(charsetSource, charset);
+
     bool isPostPage = false;
     // check if current doc is from POST command
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
     if (httpChannel) {
       nsAutoCString methodStr;
       rv = httpChannel->GetRequestMethod(methodStr);
       isPostPage = (NS_SUCCEEDED(rv) &&
                     methodStr.EqualsLiteral("POST"));
@@ -3774,8 +3796,42 @@ nsHTMLDocument::DocSizeOfExcludingThis(n
   // - mLinks
   // - mAnchors
   // - mScripts
   // - mForms
   // - mFormControls
   // - mWyciwygChannel
   // - mMidasCommandManager
 }
+
+bool
+nsHTMLDocument::WillIgnoreCharsetOverride()
+{
+  if (!mIsRegularHTML) {
+    return true;
+  }
+  if (mCharacterSetSource == kCharsetFromByteOrderMark) {
+    return true;
+  }
+  if (!IsAsciiCompatible(mCharacterSet)) {
+    return true;
+  }
+  nsCOMPtr<nsIWyciwygChannel> wyciwyg = do_QueryInterface(mChannel);
+  if (wyciwyg) {
+    return true;
+  }
+  nsIURI* uri = GetOriginalURI();
+  if (uri) {
+    bool schemeIs = false;
+    uri->SchemeIs("about", &schemeIs);
+    if (schemeIs) {
+      return true;
+    }
+    bool isResource;
+    nsresult rv = NS_URIChainHasFlags(uri,
+                                      nsIProtocolHandler::URI_IS_UI_RESOURCE,
+                                      &isResource);
+    if (NS_FAILED(rv) || isResource) {
+      return true;
+    }
+  }
+  return false;
+}
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -168,16 +168,18 @@ public:
     return nsDocument::GetElementById(aElementId);
   }
 
   virtual nsXPCClassInfo* GetClassInfo();
 
   virtual void DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const;
   // DocSizeOfIncludingThis is inherited from nsIDocument.
 
+  virtual bool WillIgnoreCharsetOverride();
+
   // WebIDL API
   void GetDomain(nsAString& aDomain, mozilla::ErrorResult& rv);
   void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
   void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
   void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
   nsGenericHTMLElement *GetBody();
   void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
   Element *GetHead() { return GetHeadElement(); }
@@ -291,34 +293,34 @@ protected:
   /** # of forms in the document, synchronously set */
   int32_t mNumForms;
 
   static uint32_t gWyciwygSessionCnt;
 
   static bool IsAsciiCompatible(const nsACString& aPreferredName);
 
   static void TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
-                               int32_t& aCharsetSource,
-                               nsACString& aCharset);
-  static bool TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV,
-                                     nsIDocShell*  aDocShell,
-                                     int32_t& aCharsetSource,
-                                     nsACString& aCharset);
-  static bool TryCacheCharset(nsICachingChannel* aCachingChannel,
+                             int32_t& aCharsetSource,
+                             nsACString& aCharset);
+  void TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV,
+                            nsIDocShell*  aDocShell,
+                            int32_t& aCharsetSource,
+                            nsACString& aCharset);
+  static void TryCacheCharset(nsICachingChannel* aCachingChannel,
                                 int32_t& aCharsetSource,
                                 nsACString& aCharset);
   // aParentDocument could be null.
   void TryParentCharset(nsIDocShell*  aDocShell,
-                          nsIDocument* aParentDocument,
-                          int32_t& charsetSource, nsACString& aCharset);
-  static void UseWeakDocTypeDefault(int32_t& aCharsetSource,
-                                      nsACString& aCharset);
-  static bool TryDefaultCharset(nsIMarkupDocumentViewer* aMarkupDV,
-                                  int32_t& aCharsetSource,
-                                  nsACString& aCharset);
+                        nsIDocument* aParentDocument,
+                        int32_t& charsetSource, nsACString& aCharset);
+  static void TryWeakDocTypeDefault(int32_t& aCharsetSource,
+                                    nsACString& aCharset);
+  static void TryDefaultCharset(nsIMarkupDocumentViewer* aMarkupDV,
+                                int32_t& aCharsetSource,
+                                nsACString& aCharset);
 
   // Override so we can munge the charset on our wyciwyg channel as needed.
   virtual void SetDocumentCharacterSet(const nsACString& aCharSetID);
 
   // Tracks if we are currently processing any document.write calls (either
   // implicit or explicit). Note that if a write call writes out something which
   // would block the parser, then mWriteLevel will be incorrect until the parser
   // finishes processing that script.
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -54,16 +54,18 @@ class NativeAudioStream : public AudioSt
 
   nsresult Init(int32_t aNumChannels, int32_t aRate,
                 const dom::AudioChannelType aAudioChannelType);
   void Shutdown();
   nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
   uint32_t Available();
   void SetVolume(double aVolume);
   void Drain();
+  nsresult Start();
+  bool IsStarted();
   void Pause();
   void Resume();
   int64_t GetPosition();
   int64_t GetPositionInFrames();
   int64_t GetPositionInFramesInternal();
   bool IsPaused();
   int32_t GetMinWriteSize();
 
@@ -176,16 +178,17 @@ static sa_stream_type_t ConvertChannelTo
       return SA_STREAM_TYPE_MAX;
   }
 }
 
 AudioStream::AudioStream()
 : mInRate(0),
   mOutRate(0),
   mChannels(0),
+  mWritten(0),
   mAudioClock(this)
 {}
 
 void AudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
@@ -271,16 +274,21 @@ nsresult AudioStream::SetPreservesPitch(
     mTimeStretcher->setRate(mAudioClock.GetPlaybackRate());
   }
 
   mAudioClock.SetPreservesPitch(aPreservesPitch);
 
   return NS_OK;
 }
 
+int64_t AudioStream::GetWritten()
+{
+  return mWritten;
+}
+
 NativeAudioStream::NativeAudioStream() :
   mVolume(1.0),
   mAudioHandle(0),
   mPaused(false),
   mInError(false)
 {
 }
 
@@ -381,16 +389,18 @@ nsresult NativeAudioStream::Write(const 
       written = WriteToBackend(data, framesAvailable * mChannels);
     } else {
       written = 0;
     }
   } else {
     written = WriteToBackend(aBuf, samples);
   }
 
+  mWritten += aFrames;
+
   if (written == -1) {
     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_write error"));
     mInError = true;
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
@@ -450,16 +460,29 @@ void NativeAudioStream::Drain()
 
   int r = sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle));
   if (r != SA_SUCCESS && r != SA_ERROR_INVALID) {
     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("NativeAudioStream: sa_stream_drain error"));
     mInError = true;
   }
 }
 
+nsresult NativeAudioStream::Start()
+{
+  // Since sydneyaudio is a push API, the playback is started when enough frames
+  // have been written. Hence, Start() is a noop.
+  return NS_OK;
+}
+
+bool NativeAudioStream::IsStarted()
+{
+  // See the comment for the |Start()| method.
+  return true;
+}
+
 void NativeAudioStream::Pause()
 {
   if (mInError)
     return;
   mPaused = true;
   sa_stream_pause(static_cast<sa_stream_t*>(mAudioHandle));
 }
 
@@ -592,23 +615,29 @@ class BufferedAudioStream : public Audio
 
   nsresult Init(int32_t aNumChannels, int32_t aRate,
                 const dom::AudioChannelType aAudioChannelType);
   void Shutdown();
   nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
   uint32_t Available();
   void SetVolume(double aVolume);
   void Drain();
+  nsresult Start();
+  bool IsStarted();
   void Pause();
   void Resume();
   int64_t GetPosition();
   int64_t GetPositionInFrames();
   int64_t GetPositionInFramesInternal();
   bool IsPaused();
   int32_t GetMinWriteSize();
+  // This method acquires the monitor and forward the call to the base
+  // class, to prevent a race on |mTimeStretcher|, in
+  // |AudioStream::EnsureTimeStretcherInitialized|.
+  void EnsureTimeStretcherInitialized();
 
 private:
   static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
   {
     return static_cast<BufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
   }
 
   static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
@@ -697,16 +726,23 @@ BufferedAudioStream::BufferedAudioStream
 {
 }
 
 BufferedAudioStream::~BufferedAudioStream()
 {
   Shutdown();
 }
 
+void
+BufferedAudioStream::EnsureTimeStretcherInitialized()
+{
+  MonitorAutoLock mon(mMonitor);
+  AudioStream::EnsureTimeStretcherInitialized();
+}
+
 nsresult
 BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
                             const dom::AudioChannelType aAudioChannelType)
 {
   cubeb* cubebContext = GetCubebContext();
 
   if (!cubebContext || aNumChannels < 0 || aRate < 0) {
     return NS_ERROR_FAILURE;
@@ -777,34 +813,29 @@ BufferedAudioStream::Write(const AudioDa
     uint32_t available = std::min(bytesToCopy, mBuffer.Available());
     NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0,
         "Must copy complete frames.");
 
     mBuffer.AppendElements(src, available);
     src += available;
     bytesToCopy -= available;
 
-    if (mState != STARTED) {
-      int r;
-      {
+    if (bytesToCopy > 0) {
+      // If we are not playing, but our buffer is full, start playing to make
+      // room for soon-to-be-decoded data.
+      if (!IsStarted()) {
         MonitorAutoUnlock mon(mMonitor);
-        r = cubeb_stream_start(mCubebStream);
+        Start();
       }
-      mState = r == CUBEB_OK ? STARTED : ERRORED;
-    }
-
-    if (mState != STARTED) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (bytesToCopy > 0) {
       mon.Wait();
     }
   }
 
+  mWritten += aFrames;
+
   return NS_OK;
 }
 
 uint32_t
 BufferedAudioStream::Available()
 {
   MonitorAutoLock mon(mMonitor);
   NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
@@ -833,16 +864,36 @@ BufferedAudioStream::Drain()
     return;
   }
   mState = DRAINING;
   while (mState == DRAINING) {
     mon.Wait();
   }
 }
 
+nsresult
+BufferedAudioStream::Start()
+{
+  if (!mCubebStream) {
+    return NS_ERROR_FAILURE;
+  }
+  if (mState != STARTED) {
+    int r = cubeb_stream_start(mCubebStream);
+    mState = r == CUBEB_OK ? STARTED : ERRORED;
+    return mState == STARTED ? NS_OK : NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+bool
+BufferedAudioStream::IsStarted()
+{
+  return mState == STARTED ? true : false;
+}
+
 void
 BufferedAudioStream::Pause()
 {
   MonitorAutoLock mon(mMonitor);
   if (!mCubebStream || mState != STARTED) {
     return;
   }
 
@@ -957,17 +1008,18 @@ BufferedAudioStream::GetUnprocessed(void
   return BytesToFrames(available) + flushedFrames;
 }
 
 long
 BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames)
 {
   long processedFrames = 0;
 
-  EnsureTimeStretcherInitialized();
+  // We need to call the non-locking version, because we already have the lock.
+  AudioStream::EnsureTimeStretcherInitialized();
 
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
   double playbackRate = static_cast<double>(mInRate) / mOutRate;
   uint32_t toPopBytes = FramesToBytes(ceil(aFrames / playbackRate));
   uint32_t available = 0;
   bool lowOnBufferedData = false;
   do {
     // Check if we already have enough data in the time stretcher pipeline.
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -39,16 +39,18 @@ class AudioClock
     // Called on the audio thread.
     double GetPlaybackRate();
     // Set if we are preserving the pitch.
     // Called on the audio thread.
     void SetPreservesPitch(bool aPreservesPitch);
     // Get the current pitch preservation state.
     // Called on the audio thread.
     bool GetPreservesPitch();
+    // Get the number of frames written to the backend.
+    int64_t GetWritten();
   private:
     // This AudioStream holds a strong reference to this AudioClock. This
     // pointer is garanteed to always be valid.
     AudioStream* mAudioStream;
     // The old output rate, to compensate audio latency for the period inbetween
     // the moment resampled buffers are pushed to the hardware and the moment the
     // clock should take the new rate into account for A/V sync.
     int mOldOutRate;
@@ -125,20 +127,30 @@ public:
 
   // Set the current volume of the audio playback. This is a value from
   // 0 (meaning muted) to 1 (meaning full volume).  Thread-safe.
   virtual void SetVolume(double aVolume) = 0;
 
   // Block until buffered audio data has been consumed.
   virtual void Drain() = 0;
 
-  // Pause audio playback
+  // Start the stream.
+  virtual nsresult Start() = 0;
+
+  // Check if the stream is started.
+  virtual bool IsStarted() = 0;
+
+  // Return the number of frames written so far in the stream. This allow the
+  // caller to check if it is safe to start the stream, if needed.
+  virtual int64_t GetWritten();
+
+  // Pause audio playback.
   virtual void Pause() = 0;
 
-  // Resume audio playback
+  // Resume audio playback.
   virtual void Resume() = 0;
 
   // Return the position in microseconds of the audio frame being played by
   // the audio hardware, compensated for playback rate change. Thread-safe.
   virtual int64_t GetPosition() = 0;
 
   // Return the position, measured in audio frames played since the stream
   // was opened, of the audio hardware.  Thread-safe.
@@ -155,28 +167,30 @@ public:
   // Returns the minimum number of audio frames which must be written before
   // you can be sure that something will be played.
   virtual int32_t GetMinWriteSize() = 0;
 
   int GetRate() { return mOutRate; }
   int GetChannels() { return mChannels; }
 
   // This should be called before attempting to use the time stretcher.
-  void EnsureTimeStretcherInitialized();
+  virtual void EnsureTimeStretcherInitialized();
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   virtual nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   virtual nsresult SetPreservesPitch(bool aPreservesPitch);
 
 protected:
   // Input rate in Hz (characteristic of the media being played)
   int mInRate;
   // Output rate in Hz (characteristic of the playback rate)
   int mOutRate;
   int mChannels;
+  // Number of frames written to the buffers.
+  int64_t mWritten;
   AudioClock mAudioClock;
   nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -604,22 +604,16 @@ nsresult MediaDecoder::Seek(double aTime
     mNextState = paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING;
     PinForSeek();
     ChangeState(PLAY_STATE_SEEKING);
   }
 
   return ScheduleStateMachineThread();
 }
 
-nsresult MediaDecoder::PlaybackRateChanged()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 double MediaDecoder::GetCurrentTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mCurrentTime;
 }
 
 already_AddRefed<nsIPrincipal> MediaDecoder::GetCurrentPrincipal()
 {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -321,21 +321,16 @@ public:
 
   // Initialize state machine and schedule it.
   nsresult InitializeStateMachine(MediaDecoder* aCloneDonor);
 
   // Start playback of a video. 'Load' must have previously been
   // called.
   virtual nsresult Play();
 
-  // Called by the element when the playback rate has been changed.
-  // Adjust the speed of the playback, optionally with pitch correction,
-  // when this is called.
-  virtual nsresult PlaybackRateChanged();
-
   // Pause video playback.
   virtual void Pause();
   // Adjust the speed of the playback, optionally with pitch correction,
   virtual void SetVolume(double aVolume);
   // Sets whether audio is being captured. If it is, we won't play any
   // of our audio.
   virtual void SetAudioCaptured(bool aCaptured);
 
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -363,18 +363,16 @@ private:
 // done on the decode thread. Never hold the decoder monitor when
 // calling into this class. Unless otherwise specified, methods and fields of
 // this class can only be accessed on the decode thread.
 class MediaDecoderReader {
 public:
   MediaDecoderReader(AbstractMediaDecoder* aDecoder);
   virtual ~MediaDecoderReader();
 
-  NS_INLINE_DECL_REFCOUNTING(MediaDecoderReader)
-
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
   // on failure.
   virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
 
   // Resets all state related to decoding, emptying all buffers etc.
   virtual nsresult ResetDecode();
 
   // Decodes an unspecified amount of audio data, enqueuing the audio data
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -104,16 +104,19 @@ static const uint32_t QUICK_BUFFER_THRES
 static const uint32_t QUICK_BUFFERING_LOW_DATA_USECS = 1000000;
 
 // If QUICK_BUFFERING_LOW_DATA_USECS is > AMPLE_AUDIO_USECS, we won't exit
 // quick buffering in a timely fashion, as the decode pauses when it
 // reaches AMPLE_AUDIO_USECS decoded data, and thus we'll never reach
 // QUICK_BUFFERING_LOW_DATA_USECS.
 PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
 
+// This value has been chosen empirically.
+static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000;
+
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
   return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
 
@@ -947,23 +950,38 @@ void MediaDecoderStateMachine::DecodeLoo
 
 bool MediaDecoderStateMachine::IsPlaying()
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   return !mPlayStartTime.IsNull();
 }
 
+// If we have already written enough frames to the AudioStream, start the
+// playback.
+static void
+StartAudioStreamPlaybackIfNeeded(AudioStream* aStream)
+{
+  // We want to have enough data in the buffer to start the stream.
+  if (!aStream->IsStarted() &&
+      static_cast<double>(aStream->GetWritten()) / aStream->GetRate() >=
+      static_cast<double>(AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS) / USECS_PER_S) {
+    aStream->Start();
+  }
+}
+
 static void WriteSilence(AudioStream* aStream, uint32_t aFrames)
 {
   uint32_t numSamples = aFrames * aStream->GetChannels();
   nsAutoTArray<AudioDataValue, 1000> buf;
   buf.SetLength(numSamples);
   memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue));
   aStream->Write(buf.Elements(), aFrames);
+
+  StartAudioStreamPlaybackIfNeeded(aStream);
 }
 
 void MediaDecoderStateMachine::AudioLoop()
 {
   NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
   LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
   int64_t audioDuration = 0;
   int64_t audioStartTime = -1;
@@ -1103,16 +1121,21 @@ void MediaDecoderStateMachine::AudioLoop
     }
   }
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     if (mReader->AudioQueue().AtEndOfStream() &&
         mState != DECODER_STATE_SHUTDOWN &&
         !mStopAudioThread)
     {
+      // If the media was too short to trigger the start of the audio stream,
+      // start it now.
+      if (!mAudioStream->IsStarted()) {
+        mAudioStream->Start();
+      }
       // Last frame pushed to audio hardware, wait for the audio to finish,
       // before the audio thread terminates.
       bool seeking = false;
       {
         int64_t unplayedFrames = audioDuration % minWriteFrames;
         if (minWriteFrames > 1 && unplayedFrames > 0) {
           // Sound is written by libsydneyaudio to the hardware in blocks of
           // frames of size minWriteFrames. So if the number of frames we've
@@ -1207,16 +1230,18 @@ uint32_t MediaDecoderStateMachine::PlayF
   uint32_t frames = 0;
   if (!PR_GetEnv("MOZ_QUIET")) {
     LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of data to stream for AudioData at %lld",
                        mDecoder.get(), audio->mFrames, audio->mTime));
   }
   mAudioStream->Write(audio->mAudioData,
                       audio->mFrames);
 
+  StartAudioStreamPlaybackIfNeeded(mAudioStream);
+
   offset = audio->mOffset;
   frames = audio->mFrames;
 
   // Dispatch events to the DOM for the audio just written.
   mEventManager.QueueWrittenAudioData(audio->mAudioData.get(),
                                       audio->mFrames * aChannels,
                                       (aFrameOffset + frames) * aChannels);
   if (offset != -1) {
--- a/content/media/plugins/MPAPI.h
+++ b/content/media/plugins/MPAPI.h
@@ -7,17 +7,17 @@
 #define MPAPI_h_
 
 #include <stdint.h>
 
 namespace MPAPI {
 
 enum ColorFormat {
   YCbCr,
-  RGB565,
+  RGB565
 };
 
 /*
  * A callback for the plugin to use to request a buffer owned by gecko. This can
  * save us a copy or two down the line.
  */
 class BufferCallback {
 public:
--- a/content/media/webm/WebMReader.cpp
+++ b/content/media/webm/WebMReader.cpp
@@ -462,21 +462,17 @@ nsresult WebMReader::ReadMetadata(VideoI
       } else {
         clusterNum++;
       }
     } while (!done);
   }
 #endif
 
   // We can't seek in buffered regions if we have no cues.
-  bool haveCues;
-  int64_t dummy = -1;
-  haveCues = nestegg_get_cue_point(mContext, 0, -1, &dummy, &dummy,
-                                   (uint64_t*)&dummy) == 0;
-  mDecoder->SetMediaSeekable(haveCues);
+  mDecoder->SetMediaSeekable(nestegg_has_cues(mContext) == 1);
 
   *aInfo = mInfo;
 
   *aTags = nullptr;
 
 #ifdef MOZ_DASH
   mDecoder->OnReadMetadataCompleted();
 #endif
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -301,16 +301,18 @@ MediaEngineWebRTCAudioSource::Process(co
     AudioSegment segment;
     segment.Init(CHANNELS);
     nsAutoTArray<const sample*,1> channels;
     channels.AppendElement(dest);
     segment.AppendFrames(buffer.forget(), channels, length);
 
     SourceMediaStream *source = mSources[i];
     if (source) {
+      // This is safe from any thread, and is safe if the track is Finished
+      // or Destroyed
       source->AppendToTrack(mTrackID, &segment);
     }
   }
 
   return;
 }
 
 }
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -34,17 +34,16 @@ CPPSRCS		= \
 		DOMSVGTransform.cpp \
 		DOMSVGTransformList.cpp \
 		nsDOMSVGZoomEvent.cpp \
 		nsDOMSVGEvent.cpp \
 		nsISVGPoint.cpp \
 		nsSVGAngle.cpp \
 		nsSVGBoolean.cpp \
 		nsSVGClass.cpp \
-		nsSVGClipPathElement.cpp \
 		nsSVGDataParser.cpp \
 		nsSVGElement.cpp \
 		nsSVGElementFactory.cpp \
 		nsSVGEnum.cpp \
 		nsSVGFeatures.cpp \
 		nsSVGFilterElement.cpp \
 		nsSVGFilters.cpp \
 		nsSVGInteger.cpp \
@@ -72,16 +71,17 @@ CPPSRCS		= \
 		SVGAnimatedPointList.cpp \
 		SVGAnimatedPreserveAspectRatio.cpp \
 		SVGAnimatedTransformList.cpp \
 		SVGAnimateElement.cpp \
 		SVGAnimateTransformElement.cpp \
 		SVGAnimateMotionElement.cpp \
 		SVGAnimationElement.cpp \
 		SVGAttrValueWrapper.cpp \
+		SVGClipPathElement.cpp \
 		SVGCircleElement.cpp \
 		SVGContentUtils.cpp \
 		SVGDefsElement.cpp \
 		SVGDescElement.cpp \
 		SVGEllipseElement.cpp \
 		SVGForeignObjectElement.cpp \
 		SVGFragmentIdentifier.cpp \
 		SVGGElement.cpp \
@@ -158,16 +158,17 @@ EXPORTS_mozilla/dom = \
 	SVGAltGlyphElement.h \
 	SVGAngle.h \
 	SVGAnimatedAngle.h \
 	SVGAnimatedBoolean.h \
 	SVGAnimateElement.h \
 	SVGAnimateTransformElement.h \
 	SVGAnimateMotionElement.h \
 	SVGAnimationElement.h \
+	SVGClipPathElement.h \
 	SVGCircleElement.h \
 	SVGDefsElement.h \
 	SVGDescElement.h \
 	SVGEllipseElement.h \
 	SVGForeignObjectElement.h \
 	SVGGElement.h \
 	SVGGradientElement.h \
 	SVGGraphicsElement.h \
rename from content/svg/content/src/nsSVGClipPathElement.cpp
rename to content/svg/content/src/SVGClipPathElement.cpp
--- a/content/svg/content/src/nsSVGClipPathElement.cpp
+++ b/content/svg/content/src/SVGClipPathElement.cpp
@@ -1,64 +1,84 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Util.h"
 
-#include "nsSVGClipPathElement.h"
+#include "mozilla/dom/SVGClipPathElement.h"
+#include "mozilla/dom/SVGClipPathElementBinding.h"
 #include "nsGkAtoms.h"
 
-using namespace mozilla;
+NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(ClipPath)
+
+DOMCI_NODE_DATA(SVGClipPathElement, mozilla::dom::SVGClipPathElement)
+
+namespace mozilla {
+namespace dom {
 
-nsSVGElement::EnumInfo nsSVGClipPathElement::sEnumInfo[1] =
+JSObject*
+SVGClipPathElement::WrapNode(JSContext *aCx, JSObject *aScope, bool *aTriedToWrap)
+{
+  return SVGClipPathElementBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+nsSVGElement::EnumInfo SVGClipPathElement::sEnumInfo[1] =
 {
   { &nsGkAtoms::clipPathUnits,
     sSVGUnitTypesMap,
     nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE
   }
 };
 
-NS_IMPL_NS_NEW_SVG_ELEMENT(ClipPath)
-
 //----------------------------------------------------------------------
 // nsISupports methods
 
-NS_IMPL_ADDREF_INHERITED(nsSVGClipPathElement,nsSVGClipPathElementBase)
-NS_IMPL_RELEASE_INHERITED(nsSVGClipPathElement,nsSVGClipPathElementBase)
+NS_IMPL_ADDREF_INHERITED(SVGClipPathElement,SVGClipPathElementBase)
+NS_IMPL_RELEASE_INHERITED(SVGClipPathElement,SVGClipPathElementBase)
 
-DOMCI_NODE_DATA(SVGClipPathElement, nsSVGClipPathElement)
-
-NS_INTERFACE_TABLE_HEAD(nsSVGClipPathElement)
-  NS_NODE_INTERFACE_TABLE5(nsSVGClipPathElement, nsIDOMNode, nsIDOMElement,
+NS_INTERFACE_TABLE_HEAD(SVGClipPathElement)
+  NS_NODE_INTERFACE_TABLE5(SVGClipPathElement, nsIDOMNode, nsIDOMElement,
                            nsIDOMSVGElement,
                            nsIDOMSVGClipPathElement,
                            nsIDOMSVGUnitTypes)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGClipPathElement)
-NS_INTERFACE_MAP_END_INHERITING(nsSVGClipPathElementBase)
+NS_INTERFACE_MAP_END_INHERITING(SVGClipPathElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
-nsSVGClipPathElement::nsSVGClipPathElement(already_AddRefed<nsINodeInfo> aNodeInfo)
-  : nsSVGClipPathElementBase(aNodeInfo)
+SVGClipPathElement::SVGClipPathElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  : SVGClipPathElementBase(aNodeInfo)
 {
+  SetIsDOMBinding();
 }
 
 /* readonly attribute nsIDOMSVGAnimatedEnumeration clipPathUnits; */
-NS_IMETHODIMP nsSVGClipPathElement::GetClipPathUnits(nsIDOMSVGAnimatedEnumeration * *aClipPathUnits)
+NS_IMETHODIMP SVGClipPathElement::GetClipPathUnits(nsIDOMSVGAnimatedEnumeration * *aClipPathUnits)
 {
-  return mEnumAttributes[CLIPPATHUNITS].ToDOMAnimatedEnum(aClipPathUnits, this);
+  *aClipPathUnits = ClipPathUnits().get();
+  return NS_OK;
+}
+
+already_AddRefed<nsIDOMSVGAnimatedEnumeration>
+SVGClipPathElement::ClipPathUnits()
+{
+  nsCOMPtr<nsIDOMSVGAnimatedEnumeration> unit;
+  mEnumAttributes[CLIPPATHUNITS].ToDOMAnimatedEnum(getter_AddRefs(unit), this);
+  return unit.forget();
 }
 
 nsSVGElement::EnumAttributesInfo
-nsSVGClipPathElement::GetEnumInfo()
+SVGClipPathElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             ArrayLength(sEnumInfo));
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
-NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGClipPathElement)
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGClipPathElement)
 
+} // namespace dom
+} // namespace mozilla
rename from content/svg/content/src/nsSVGClipPathElement.h
rename to content/svg/content/src/SVGClipPathElement.h
--- a/content/svg/content/src/nsSVGClipPathElement.h
+++ b/content/svg/content/src/SVGClipPathElement.h
@@ -1,53 +1,69 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef __NS_SVGCLIPPATHELEMENT_H__
-#define __NS_SVGCLIPPATHELEMENT_H__
+#ifndef mozilla_dom_SVGClipPathElement_h
+#define mozilla_dom_SVGClipPathElement_h
 
 #include "nsIDOMSVGClipPathElement.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsSVGEnum.h"
 #include "mozilla/dom/SVGTransformableElement.h"
 
-typedef mozilla::dom::SVGTransformableElement nsSVGClipPathElementBase;
+class nsSVGClipPathFrame;
+
+nsresult NS_NewSVGClipPathElement(nsIContent **aResult,
+                                  already_AddRefed<nsINodeInfo> aNodeInfo);
+
+namespace mozilla {
+namespace dom {
 
-class nsSVGClipPathElement : public nsSVGClipPathElementBase,
-                             public nsIDOMSVGClipPathElement,
-                             public nsIDOMSVGUnitTypes
+typedef SVGTransformableElement SVGClipPathElementBase;
+
+class SVGClipPathElement MOZ_FINAL : public SVGClipPathElementBase,
+                                     public nsIDOMSVGClipPathElement,
+                                     public nsIDOMSVGUnitTypes
 {
-  friend class nsSVGClipPathFrame;
+  friend class ::nsSVGClipPathFrame;
 
 protected:
-  friend nsresult NS_NewSVGClipPathElement(nsIContent **aResult,
-                                           already_AddRefed<nsINodeInfo> aNodeInfo);
-  nsSVGClipPathElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  friend nsresult (::NS_NewSVGClipPathElement(nsIContent **aResult,
+                                              already_AddRefed<nsINodeInfo> aNodeInfo));
+  SVGClipPathElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual JSObject* WrapNode(JSContext *cx, JSObject *scope, bool *triedToWrap) MOZ_OVERRIDE;
 
 public:
   // interfaces:
-  
+
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGCLIPPATHELEMENT
 
   // xxx I wish we could use virtual inheritance
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
   NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
-  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGClipPathElementBase::)
+  NS_FORWARD_NSIDOMSVGELEMENT(SVGClipPathElementBase::)
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
+
+  // WebIDL
+  already_AddRefed<nsIDOMSVGAnimatedEnumeration> ClipPathUnits();
+
 protected:
 
   // nsIDOMSVGClipPathElement values
   enum { CLIPPATHUNITS };
   nsSVGEnum mEnumAttributes[1];
   static EnumInfo sEnumInfo[1];
 
   virtual EnumAttributesInfo GetEnumInfo();
 };
 
-#endif
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SVGClipPathElement_h
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -34,17 +34,17 @@ nsDefaultURIFixup::nsDefaultURIFixup()
 }
 
 
 nsDefaultURIFixup::~nsDefaultURIFixup()
 {
   /* destructor code */
 }
 
-/* nsIURI createExposableURI (in nsIRUI aURI); */
+/* nsIURI createExposableURI (in nsIURI aURI); */
 NS_IMETHODIMP
 nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn)
 {
     NS_ENSURE_ARG_POINTER(aURI);
     NS_ENSURE_ARG_POINTER(aReturn);
 
     bool isWyciwyg = false;
     aURI->SchemeIs("wyciwyg", &isWyciwyg);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2244,16 +2244,35 @@ nsDocShell::SetFullscreenAllowed(bool aF
         // across process boundaries.
         return NS_ERROR_UNEXPECTED;
     }
     mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetMayEnableCharacterEncodingMenu(bool* aMayEnableCharacterEncodingMenu)
+{
+  *aMayEnableCharacterEncodingMenu = false;
+  if (!mContentViewer) {
+    return NS_OK;
+  }
+  nsIDocument* doc = mContentViewer->GetDocument();
+  if (!doc) {
+    return NS_OK;
+  }
+  if (doc->WillIgnoreCharsetOverride()) {
+    return NS_OK;
+  }
+
+  *aMayEnableCharacterEncodingMenu = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator **outEnum)
 {
     NS_ENSURE_ARG_POINTER(outEnum);
     *outEnum = nullptr;
     
     nsRefPtr<nsDocShellEnumerator> docShellEnum;
     if (aDirection == ENUMERATE_FORWARDS)
         docShellEnum = new nsDocShellForwardsEnumerator;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -34,17 +34,17 @@ interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 
-[scriptable, builtinclass, uuid(008f5c86-b915-458c-aaf1-78de3b484e68)]
+[scriptable, builtinclass, uuid(ca15d803-1330-4154-b3f9-063fb2b443e7)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -578,17 +578,17 @@ interface nsIDocShell : nsISupports
 
   /**
    * In a child docshell, this is the charset of the parent docshell
    */
   attribute nsIAtom parentCharset;
 
   /*
    * In a child docshell, this is the source of parentCharset
-   * @see nsIParser
+   * @see nsCharsetSource.h
    */
   attribute int32_t parentCharsetSource;
 
   /**
    * Add an observer to the list of parties to be notified when this docshell's
    * private browsing status is changed. |obs| must support weak references.
    */
   void addWeakPrivacyTransitionObserver(in nsIPrivacyTransitionObserver obs);
@@ -720,9 +720,15 @@ interface nsIDocShell : nsISupports
    * to propagate the value of the cross process parent's iframe's
    * "allowfullscreen" attribute to the child process. Setting
    * fullscreenAllowed on docshells which aren't content boundaries throws an
    * exception.
    */
   [infallible] readonly attribute boolean fullscreenAllowed;
   
   void setFullscreenAllowed(in boolean allowed);
+
+  /**
+   * Indicates whether the UI may enable the character encoding menu. The UI
+   * must disable the menu when this property is false.
+   */
+  [infallible] readonly attribute boolean mayEnableCharacterEncodingMenu;
 };
--- a/docshell/base/nsIMarkupDocumentViewer.idl
+++ b/docshell/base/nsIMarkupDocumentViewer.idl
@@ -41,29 +41,30 @@ interface nsIMarkupDocumentViewer : nsIS
 	/** Disable entire author style level (including HTML presentation hints) */
 	attribute boolean authorStyleDisabled;
 
 	/*
 	XXX Comment here!
 	*/
 	attribute ACString defaultCharacterSet;
 
-	/*
-	XXX Comment here!
-	*/
+	/**
+	 * XXX comm-central only: bug 829543. Not the Character Encoding menu in 
+     * browser!
+	 */
 	attribute ACString forceCharacterSet;
 
-	/*
-	XXX Comment here!
-	*/
+	/**
+	 * XXX comm-central only: bug 829543.
+	 */
 	attribute ACString hintCharacterSet;
 
-	/*
-	XXX Comment here!
-	*/
+	/**
+	 * XXX comm-central only: bug 829543.
+	 */
 	attribute int32_t hintCharacterSetSource;
 
 	/*
 	character set from prev document 
 	*/
 	attribute ACString prevDocCharacterSet;
 
 	//void GetCharacterSetHint(in wstring hintCharset, in int32_t charsetSource);
--- a/docshell/test/browser/Makefile.in
+++ b/docshell/test/browser/Makefile.in
@@ -27,11 +27,47 @@ MOCHITEST_BROWSER_FILES =	\
 		browser_bug554155.js \
 		browser_bug655273.js \
 		browser_bug655270.js \
 		file_bug655270.html \
 		favicon_bug655270.ico \
 		browser_bug670318.js \
 		file_bug670318.html \
 		browser_bug673467.js \
+		browser_bug234628-1.js \
+		file_bug234628-1.html \
+		file_bug234628-1-child.html \
+		browser_bug234628-2.js \
+		file_bug234628-2.html \
+		file_bug234628-2-child.html \
+		browser_bug234628-3.js \
+		file_bug234628-3.html \
+		file_bug234628-3-child.html \
+		browser_bug234628-4.js \
+		file_bug234628-4.html \
+		file_bug234628-4-child.html \
+		browser_bug234628-5.js \
+		file_bug234628-5.html \
+		file_bug234628-5-child.html \
+		browser_bug234628-6.js \
+		file_bug234628-6.html \
+		file_bug234628-6-child.html \
+		file_bug234628-6-child.html^headers^ \
+		browser_bug234628-7.js \
+		file_bug234628-7.html \
+		file_bug234628-7-child.html \
+		file_bug234628-7-child.html^headers^ \
+		browser_bug234628-8.js \
+		file_bug234628-8.html \
+		file_bug234628-8-child.html \
+		browser_bug234628-9.js \
+		file_bug234628-9.html \
+		file_bug234628-9-child.html \
+		browser_bug234628-10.js \
+		file_bug234628-10.html \
+		file_bug234628-10-child.xhtml \
+		browser_bug234628-11.js \
+		file_bug234628-11.html \
+		file_bug234628-11-child.xhtml \
+		file_bug234628-11-child.xhtml^headers^ \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug134911.js
@@ -2,17 +2,17 @@
 const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
 
 const enteredText1="The quick brown fox jumps over the lazy dog";
 const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
 
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
   gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
 function afterOpen() {
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-1.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-1.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-1.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 129, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 85, "Child doc should be windows-1252 initially");  
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 129, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0402'), 85, "Child doc should decode as windows-1251 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-10.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-10.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-10.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 151, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 71, "Child doc should be utf-8 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 151, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 71, "Child doc should decode as utf-8 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-11.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-11.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-11.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 193, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should be utf-8 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 193, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should decode as utf-8 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-2.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-2.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-2.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 129, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u00E2\u201A\u00AC'), 78, "Child doc should be windows-1252 initially");  
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 129, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 78, "Child doc should decode as windows-1251 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-3.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-3.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-3.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 118, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 73, "Child doc should be utf-8 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 118, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 73, "Child doc should decode as windows-1251 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-4.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-4.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-4.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 132, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 79, "Child doc should be utf-8 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 132, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 79, "Child doc should decode as utf-8 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-5.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-5.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-5.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 146, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 87, "Child doc should be utf-16 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 146, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 87, "Child doc should decode as utf-16 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-16", "Child doc should report UTF-16 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-6.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-6.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-6.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 190, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 109, "Child doc should be utf-16 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 190, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 109, "Child doc should decode as utf-16 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-16BE", "Child doc should report UTF-16 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-7.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-7.js
@@ -1,38 +1,39 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-7.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
   gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
 
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 188, "Parent doc should be windows-1252 initially");
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should be utf-8 initially");
+
+  BrowserSetForcedCharacterSet("windows-1251");
 }
   
-function afterChangeCharset() {
+function afterChangeCharset(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
+
   gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 188, "Parent doc should decode as windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 107, "Child doc should decode as windows-1251 subsequently");  
+
+  is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
 
   gBrowser.removeCurrentTab();
   finish();
 }
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-8.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-8.js
@@ -1,38 +1,23 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-8.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
-  gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
-  gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
-
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
-}
-  
-function afterChangeCharset() {
-  gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
+  gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 156, "Parent doc should be windows-1251");
+
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0402'), 99, "Child doc should be windows-1251");
 
   gBrowser.removeCurrentTab();
   finish();
 }
+
copy from docshell/test/browser/browser_bug134911.js
copy to docshell/test/browser/browser_bug234628-9.js
--- a/docshell/test/browser/browser_bug134911.js
+++ b/docshell/test/browser/browser_bug234628-9.js
@@ -1,38 +1,23 @@
-/* The test text decoded correctly as Shift_JIS */
-const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
-
-const enteredText1="The quick brown fox jumps over the lazy dog";
-const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
-
 function test() {
   waitForExplicitFinish();
 
-  var rootDir = getRootDirectory(gTestPath);
-  gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
+  var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+  gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-9.html");
   gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
 }
 
-function afterOpen() {
-  gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
-  gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
-
-  gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
-  gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
+function afterOpen(event) {
+  if (event.target != gBrowser.contentDocument) {
+    return;
+  }
 
-  /* Force the page encoding to Shift_JIS */
-  BrowserSetForcedCharacterSet("Shift_JIS");
-}
-  
-function afterChangeCharset() {
-  gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
+  gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
 
-  is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
-     "encoding successfully changed");
-  is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
-     "text preserved in <textarea>");
-  is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
-     "text preserved in <input>");
+  is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 145, "Parent doc should be UTF-16");
+
+  is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 96, "Child doc should be windows-1252");
 
   gBrowser.removeCurrentTab();
   finish();
 }
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-1-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<h1>No encoding declaration in parent or child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-1-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-10-child.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head><title>XML child with no encoding declaration</title></head>
+<body><p>Euro sign if decoded as UTF-8: €</p></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-10.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in HTML parent or XHTML child</title>
+</head>
+<body>
+<h1>No encoding declaration in HTML parent or XHTML child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-10-child.xhtml"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-11-child.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head><title>No encoding declaration in HTML parent and HTTP declaration in XHTML child</title></head>
+<body><p>Euro sign if decoded as UTF-8: €</p></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-11-child.xhtml^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/xhtml+xml; charset=utf-8
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-11.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in HTML parent and HTTP declaration in XHTML child</title>
+</head>
+<body>
+<h1>No encoding declaration in HTML parent and HTTP declaration in XHTML child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-11-child.xhtml"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-2-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<h1>No encoding declaration in parent or child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-2-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-3-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-3.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and child</title>
+</head>
+<body>
+<h1>meta declaration in parent and child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-3-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-4-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOM in child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-4.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOM in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and BOM in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-4-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a650552f63907ef61222adb5b151725efdf7027f
GIT binary patch
literal 498
zc${64Jx&8L5QSg8r*Kk(qBImJNdbf|f&vQ4ur`UMgk5C~h&ypc;2USLvJ}?%dFK7t
z{`sD1q#NDoS`T{Cy_Ry?Drv8s7OFHW$g$RX)kbrT71)i-M4;n)v<D@yQ>})nlxI_B
zeJC=QV9cpO@8Ftxe$qLE8DT_FL}y30W6E{`-V_~2f1RC7)l>osEq(`Bf4NfDFxKKE
zmHF2BE(ey)SjwZPL&MIGiCw<0r#|vJx8#`W0(8ZDtqP+ib41VMI2m|W`DFMy&$xMg
q_Nb*U^@d}EQU`a_XL%_}n_PWP)BVeG3I1~C(iXp=!({NAm3{$NKvOmV
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-5.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and UTF-16 BOM in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and UTF-16 BOM in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-5-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..52c37f259691ef5dff02847d3c1da69ccad2fa3e
GIT binary patch
literal 540
zc${64%TB^T6o&tGpJHIGA#7Y2V4*_X7+v7PC3Z$S3D|~?65q*(^qWIzsA(o=&gH+%
zsb(7KLC?C^T5o#MQcjhU_S$KoLbE<|oQ*!U)m&o*ZsUF;(D7ZiCrji`rD~><%_c(~
z6`4yQ=ER_H@{)Lc(YXQ{K}50$&yH@(RK5jpQ+OQxjVWc;%o1Q|&^v%S`%1=<QHu*#
zS+}&e95{I<yF6(+ICA~u*x7qM@s15W8B@#W^vbqY_?qg5>}Wa_w@2!0wwT{u8B=Sz
zXZBo;@Oa!!2L1}G3^)0XGq0a6YlusI!CAx9g57q$Kl)eI7QH$D@AFp9{hCVC>kSnq
IgFj#V0js)MHUIzs
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-6-child.html^headers^
@@ -0,0 +1,1 @@
+Content-Type: text/html; charset=utf-16be
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-6.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOMless UTF-16 with HTTP charset in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and BOMless UTF-16 with HTTP charset in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-6-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-7-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOMless UTF-8 with HTTP charset in child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-7-child.html^headers^
@@ -0,0 +1,1 @@
+Content-Type: text/html; charset=utf-8
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-7.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOMless UTF-8 with HTTP charset in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and BOMless UTF-8 with HTTP charset in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-7-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-8-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and no declaration in child</title>
+</head>
+<body>
+<p>Capital dje if decoded as Windows-1251: </p>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-8.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1251">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and no declaration in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and no declaration in child</h1>
+
+<p>Capital dje if decoded as Windows-1251: </p>
+
+<iframe src="file_bug234628-8-child.html"></iframe>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-9-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>UTF-16 with BOM in parent and no declaration in child</title>
+</head>
+<body>
+<p>Euro sign if decoded as Windows-1251: </p>
+
+</body>
+</html>
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8a469da3aa4395fef8a7b57c59f60f5e84d58265
GIT binary patch
literal 740
zc$~eG%}&Bl5QWdWPjO|flA<wcfQ5=XqYGWQGT0Vq0+zHz;yd|_>Ni8F-~+h0y>~iu
zzBBjd`>U@5-RNG|depNXG*Zw^wHBIds7U=b3XHMdG}S;IWvn`FMMfrVq6JY+9F(Y_
zs`lF2Rcqzc)hGtcncj&L=A)g=A<7&@PL#u$ldY(-RfaBx6UdL<$*4xjAT04KQEmIG
zonfk>p4i_lq*!OD*PfZNx?=Bljbnf?hOp^3_-<48D)?HziJw8qFm~aZjWU6jlTGpM
z?m0*PXa9^|(Wd%n&oBP>4mX~W`!89Iv_fT+imGX&o3zxz=hQtbzPsIC5`PAxE?cH)
z%jS-ZS;DZpns^VHUO0mlK0Tf26h=?y&@LFieIB=O&u-NCyVR{y_s?l|_ug-$AKqno
A!T<mO
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -53,16 +53,18 @@ this.AppsUtils = {
       downloadAvailable: aApp.downloadAvailable,
       downloading: aApp.downloading,
       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,
       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.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -1119,16 +1119,87 @@ this.DOMApplicationRegistry = {
     AppDownloadManager.add(aApp.manifestURL, download);
 
     cacheUpdate.addObserver(new AppcacheObserver(aApp), false);
     if (aOfflineCacheObserver) {
       cacheUpdate.addObserver(aOfflineCacheObserver, false);
     }
   },
 
+  // Returns the MD5 hash of a file, doing async IO off the main thread.
+  computeFileHash: function computeFileHash(aFile, aCallback) {
+    Cu.import("resource://gre/modules/osfile.jsm");
+    const CHUNK_SIZE = 16384;
+
+    // Return the two-digit hexadecimal code for a byte.
+    function toHexString(charCode) {
+      return ("0" + charCode.toString(16)).slice(-2);
+    }
+
+    let hasher = Cc["@mozilla.org/security/hash;1"]
+                   .createInstance(Ci.nsICryptoHash);
+    // We want to use the MD5 algorithm.
+    hasher.init(hasher.MD5);
+
+    OS.File.open(aFile.path, { read: true }).then(
+      function opened(file) {
+        let readChunk = function readChunk() {
+          file.read(CHUNK_SIZE).then(
+            function readSuccess(array) {
+              hasher.update(array, array.length);
+              if (array.length == CHUNK_SIZE) {
+                readChunk();
+              } else {
+                // We're passing false to get the binary hash and not base64.
+                let hash = hasher.finish(false);
+                // convert the binary hash data to a hex string.
+                aCallback([toHexString(hash.charCodeAt(i)) for (i in hash)]
+                          .join(""));
+              }
+            },
+            function readError() {
+              debug("Error reading " + aFile.path);
+              aCallback(null);
+            }
+          );
+        }
+
+        readChunk();
+      },
+      function openError() {
+        debug("Error opening " + aFile.path);
+        aCallback(null);
+      }
+    );
+  },
+
+  // Returns the MD5 hash of the manifest.
+  computeManifestHash: function(aManifest) {
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                      .createInstance(Ci.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+    let result = {};
+    // Data is an array of bytes.
+    let data = converter.convertToByteArray(JSON.stringify(aManifest), result);
+
+    let hasher = Cc["@mozilla.org/security/hash;1"]
+                   .createInstance(Ci.nsICryptoHash);
+    hasher.init(hasher.MD5);
+    hasher.update(data, data.length);
+    // We're passing false to get the binary hash and not base64.
+    let hash = hasher.finish(false);
+
+    function toHexString(charCode) {
+      return ("0" + charCode.toString(16)).slice(-2);
+    }
+
+    // Convert the binary hash data to a hex string.
+    return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+  },
+
   checkForUpdate: function(aData, aMm) {
     debug("checkForUpdate for " + aData.manifestURL);
     let id = this._appIdForManifestURL(aData.manifestURL);
     let app = this.webapps[id];
 
     if (!app) {
       aData.error = "NO_SUCH_APP";
       aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
@@ -1331,47 +1402,70 @@ this.DOMApplicationRegistry = {
       debug("adding manifest etag:" + app.etag);
       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;
+
       if (xhr.status == 200) {
         let manifest = xhr.response;
         if (manifest == null) {
           sendError("MANIFEST_PARSE_ERROR");
           return;
         }
+
         if (!AppsUtils.checkManifest(manifest, app)) {
           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);
           app.lastCheckedUpdate = Date.now();
           if (app.origin.startsWith("app://")) {
-            updatePackagedApp.call(this, manifest);
+            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 = {
+                lastCheckedUpdate: app.lastCheckedUpdate
+              }
+              aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
+              this._saveApps();
+            }
           } else {
             this._readManifests([{ id: id }], (function(aResult) {
-              updateHostedApp.call(this, aResult[0].manifest, manifest);
+              // Update only the appcache if the manifest has not changed
+              // 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 the app is a packaged app, we just send a 'downloadapplied'
-          // event.
+          // or downloadavailable event.
           app.lastCheckedUpdate = Date.now();
-          aData.event = "downloadapplied";
+          aData.event = app.downloadAvailable ? "downloadavailable"
+                                              : "downloadapplied";
           aData.app = {
             lastCheckedUpdate: app.lastCheckedUpdate
           }
           aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
           this._saveApps();
         } else {
           // For hosted apps, even if the manifest has not changed, we check
           // for offline cache updates.
@@ -1461,16 +1555,17 @@ this.DOMApplicationRegistry = {
         if (!AppsUtils.checkManifest(app.manifest, app)) {
           sendError("INVALID_MANIFEST");
         } else if (!AppsUtils.checkInstallAllowed(app.manifest, app.installOrigin)) {
           sendError("INSTALL_FROM_DENIED");
         } else if (!checkAppStatus(app.manifest)) {
           sendError("INVALID_SECURITY_LEVEL");
         } else {
           app.etag = xhr.getResponseHeader("Etag");
+          app.manifestHash = this.computeManifestHash(app.manifest);
           // We allow bypassing the install confirmation process to facilitate
           // automation.
           let prefName = "dom.mozApps.auto_confirm_install";
           if (Services.prefs.prefHasUserValue(prefName) &&
               Services.prefs.getBoolPref(prefName)) {
             this.confirmInstall(aData);
           } else {
             Services.obs.notifyObservers(aMm, "webapps-ask-install",
@@ -1515,23 +1610,25 @@ this.DOMApplicationRegistry = {
           return;
         }
 
         let manifest = app.updateManifest = xhr.response;
         if (!manifest) {
           sendError("MANIFEST_PARSE_ERROR");
           return;
         }
+
         if (!(AppsUtils.checkManifest(manifest, app) &&
               manifest.package_path)) {
           sendError("INVALID_MANIFEST");
         } else if (!AppsUtils.checkInstallAllowed(manifest, app.installOrigin)) {
           sendError("INSTALL_FROM_DENIED");
         } else {
           app.etag = xhr.getResponseHeader("Etag");
+          app.manifestHash = this.computeManifestHash(manifest);
           debug("at install package got app etag=" + app.etag);
           Services.obs.notifyObservers(aMm, "webapps-ask-install",
                                        JSON.stringify(aData));
         }
       }
       else {
         sendError("MANIFEST_URL_ERROR");
       }
@@ -1946,161 +2043,168 @@ this.DOMApplicationRegistry = {
           bufferedOutputStream.close();
           outputStream.close();
 
           if (!Components.isSuccessCode(aStatusCode)) {
             cleanup("NETWORK_ERROR");
             return;
           }
 
-          if (requestChannel.responseStatus == 304) {
-            // The package's Etag has not changed.
-            // We send a "applied" event right away.
-            app.downloading = false;
-            app.downloadAvailable = false;
-            app.downloadSize = 0;
-            app.installState = "installed";
-            app.readyToApplyDownload = false;
-            self.broadcastMessage("Webapps:PackageEvent", {
-                                    type: "applied",
-                                    manifestURL: aApp.manifestURL,
-                                    app: app });
-            // Save the updated registry, and cleanup the tmp directory.
-            self._saveApps();
-            let file = FileUtils.getFile("TmpD", ["webapps", id], false);
-            if (file && file.exists()) {
-              file.remove(true);
-            }
-            return;
-          }
+          self.computeFileHash(zipFile, function onHashComputed(aHash) {
+            debug("packageHash=" + aHash);
+            let newPackage = (requestChannel.responseStatus != 304) &&
+                             (aHash != app.packageHash);
 
-          let certdb;
-          try {
-            certdb = Cc["@mozilla.org/security/x509certdb;1"]
-                       .getService(Ci.nsIX509CertDB);
-          } catch (e) {
-            cleanup("CERTDB_ERROR");
-            return;
-          }
+            if (!newPackage) {
+              // The package's Etag or hash has not changed.
+              // We send a "applied" event right away.
+              app.downloading = false;
+              app.downloadAvailable = false;
+              app.downloadSize = 0;
+              app.installState = "installed";
+              app.readyToApplyDownload = false;
+              self.broadcastMessage("Webapps:PackageEvent", {
+                                      type: "applied",
+                                      manifestURL: aApp.manifestURL,
+                                      app: app });
+              // Save the updated registry, and cleanup the tmp directory.
+              self._saveApps();
+              let file = FileUtils.getFile("TmpD", ["webapps", id], false);
+              if (file && file.exists()) {
+                file.remove(true);
+              }
+              return;
+            }
 
-          certdb.openSignedJARFileAsync(zipFile, function(aRv, aZipReader) {
-            let zipReader;
+            let certdb;
             try {
-              let isSigned;
-              if (Components.isSuccessCode(aRv)) {
-                isSigned = true;
-                zipReader = aZipReader;
-              } else if (aRv != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) {
-                throw "INVALID_SIGNATURE";
-              } else {
-                isSigned = false;
-                zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
-                              .createInstance(Ci.nsIZipReader);
-                zipReader.open(zipFile);
-              }
+              certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                         .getService(Ci.nsIX509CertDB);
+            } catch (e) {
+              cleanup("CERTDB_ERROR");
+              return;
+            }
+
+            certdb.openSignedJARFileAsync(zipFile, function(aRv, aZipReader) {
+              let zipReader;
+              try {
+                let isSigned;
+                if (Components.isSuccessCode(aRv)) {
+                  isSigned = true;
+                  zipReader = aZipReader;
+                } else if (aRv != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) {
+                  throw "INVALID_SIGNATURE";
+                } else {
+                  isSigned = false;
+                  zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
+                                .createInstance(Ci.nsIZipReader);
+                  zipReader.open(zipFile);
+                }
 
-              // XXX Security: You CANNOT safely add a new app store for
-              // installing privileged apps just by modifying this pref and
-              // adding the signing cert for that store to the cert trust
-              // database. *Any* origin listed can install apps signed with
-              // *any* certificate trusted; we don't try to maintain a strong
-              // association between certificate with installOrign. The
-              // expectation here is that in production builds the pref will
-              // contain exactly one origin. However, in custom development
-              // builds it may contain more than one origin so we can test
-              // different stages (dev, staging, prod) of the same app store.
-              //
-              // Only allow signed apps to be installed from a whitelist of
-              // domains, and require all packages installed from any of the
-              // domains on the whitelist to be signed. This is a stopgap until
-              // we have a real story for handling multiple app stores signing
-              // apps.
-              let signedAppOriginsStr =
-                Services.prefs.getCharPref(
-                  "dom.mozApps.signed_apps_installable_from");
-              let isSignedAppOrigin
-                = signedAppOriginsStr.split(",").indexOf(aApp.installOrigin) > -1;
-              if (!isSigned && isSignedAppOrigin) {
-                // Packaged apps installed from these origins must be signed;
-                // if not, assume somebody stripped the signature.
-                throw "INVALID_SIGNATURE";
-              } else if (isSigned && !isSignedAppOrigin) {
-                // Other origins are *prohibited* from installing signed apps.
-                // One reason is that our app revociation mechanism requires
-                // strong cooperation from the host of the mini-manifest, which
-                // we assume to be under the control of the install origin,
-                // even if it has a different origin.
-                throw "INSTALL_FROM_DENIED";
-              }
+                // XXX Security: You CANNOT safely add a new app store for
+                // installing privileged apps just by modifying this pref and
+                // adding the signing cert for that store to the cert trust
+                // database. *Any* origin listed can install apps signed with
+                // *any* certificate trusted; we don't try to maintain a strong
+                // association between certificate with installOrign. The
+                // expectation here is that in production builds the pref will
+                // contain exactly one origin. However, in custom development
+                // builds it may contain more than one origin so we can test
+                // different stages (dev, staging, prod) of the same app store.
+                //
+                // Only allow signed apps to be installed from a whitelist of
+                // domains, and require all packages installed from any of the
+                // domains on the whitelist to be signed. This is a stopgap until
+                // we have a real story for handling multiple app stores signing
+                // apps.
+                let signedAppOriginsStr =
+                  Services.prefs.getCharPref(
+                    "dom.mozApps.signed_apps_installable_from");
+                let isSignedAppOrigin
+                  = signedAppOriginsStr.split(",").indexOf(aApp.installOrigin) > -1;
+                if (!isSigned && isSignedAppOrigin) {
+                  // Packaged apps installed from these origins must be signed;
+                  // if not, assume somebody stripped the signature.
+                  throw "INVALID_SIGNATURE";
+                } else if (isSigned && !isSignedAppOrigin) {
+                  // Other origins are *prohibited* from installing signed apps.
+                  // One reason is that our app revociation mechanism requires
+                  // strong cooperation from the host of the mini-manifest, which
+                  // we assume to be under the control of the install origin,
+                  // even if it has a different origin.
+                  throw "INSTALL_FROM_DENIED";
+                }
 
-              if (!zipReader.hasEntry("manifest.webapp")) {
-                throw "MISSING_MANIFEST";
-              }
+                if (!zipReader.hasEntry("manifest.webapp")) {
+                  throw "MISSING_MANIFEST";
+                }
 
-              let istream = zipReader.getInputStream("manifest.webapp");
+                let istream = zipReader.getInputStream("manifest.webapp");
 
-              // Obtain a converter to read from a UTF-8 encoded input stream.
-              let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                                .createInstance(Ci.nsIScriptableUnicodeConverter);
-              converter.charset = "UTF-8";
+                // Obtain a converter to read from a UTF-8 encoded input stream.
+                let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                                  .createInstance(Ci.nsIScriptableUnicodeConverter);
+                converter.charset = "UTF-8";
 
-              let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
-                                                                   istream.available()) || ""));
+                let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
+                                                                     istream.available()) || ""));
 
-              // Call checkManifest before compareManifests, as checkManifest
-              // will normalize some attributes that has already been normalized
-              // for aManifest during checkForUpdate.
-              if (!AppsUtils.checkManifest(manifest, app)) {
-                throw "INVALID_MANIFEST";
-              }
+                // Call checkManifest before compareManifests, as checkManifest
+                // will normalize some attributes that has already been normalized
+                // for aManifest during checkForUpdate.
+                if (!AppsUtils.checkManifest(manifest, app)) {
+                  throw "INVALID_MANIFEST";
+                }
+
+                if (!AppsUtils.compareManifests(manifest,
+                                                aManifest._manifest)) {
+                  throw "MANIFEST_MISMATCH";
+                }
+
+                if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
+                  throw "INSTALL_FROM_DENIED";
+                }
+
+                let maxStatus = isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
+                                         : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
 
-              if (!AppsUtils.compareManifests(manifest,
-                                              aManifest._manifest)) {
-                throw "MANIFEST_MISMATCH";
-              }
+                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 {
+                  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");
+                }
 
-              if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
-                throw "INSTALL_FROM_DENIED";
-              }
-
-              let maxStatus = isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
-                                       : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
-
-              if (AppsUtils.getAppManifestStatus(manifest) > maxStatus) {
-                throw "INVALID_SECURITY_LEVEL";
+                if (aOnSuccess) {
+                  aOnSuccess(id, manifest);
+                }
+              } catch (e) {
+                // Something bad happened when reading the package.
+                if (typeof e == 'object') {
+                  Cu.reportError("Error while reading package:" + e);
+                  cleanup("INVALID_PACKAGE");
+                } else {
+                  cleanup(e);
+                }
+              } finally {
+                AppDownloadManager.remove(aApp.manifestURL);
+                if (zipReader)
+                  zipReader.close();
               }
-              aApp.appStatus = AppsUtils.getAppManifestStatus(manifest);
-              // Save the new Etag for the package.
-              try {
-                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");
-              }
-
-              if (aOnSuccess) {
-                aOnSuccess(id, manifest);
-              }
-            } catch (e) {
-              // Something bad happened when reading the package.
-              if (typeof e == 'object') {
-                Cu.reportError("Error while reading package:" + e);
-                cleanup("INVALID_PACKAGE");
-              } else {
-                cleanup(e);
-              }
-            } finally {
-              AppDownloadManager.remove(aApp.manifestURL);
-              if (zipReader)
-                zipReader.close();
-            }
+            });
           });
         }
       });
 
       requestChannel.asyncOpen(listener, null);
     };
 
     let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser")
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(35dddb4c-3d35-11e2-978c-10bf48d64bd4)]
+[function, scriptable, uuid(c7227506-5f8e-11e2-8bb3-10bf48d64bd4)]
 interface nsIAudioChannelAgentCallback : nsISupports
 {
   /**
    * Notified when the playable status of channel is changed.
    *
    * @param canPlay
    *        Callback from agent to notify component of the playable status
    *        of the channel. If canPlay is false, component SHOULD stop playing
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -5027,17 +5027,17 @@ nsWindowSH::InstallGlobalScopePolluter(J
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, jsval *vp, bool *_retval)
 {
-  nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
+  DebugOnly<nsGlobalWindow*> win = nsGlobalWindow::FromWrapper(wrapper);
 
   JSAutoRequest ar(cx);
 
 #ifdef DEBUG_SH_FORWARDING
   {
     JSString *jsstr = ::JS_ValueToString(cx, id);
     if (jsstr) {
       nsDependentJSString str(jsstr);
@@ -5054,58 +5054,16 @@ nsWindowSH::GetProperty(nsIXPConnectWrap
     }
   }
 #endif
 
   // The order in which things are done in this method are a bit
   // whacky, that's because this method is *extremely* performace
   // critical. Don't touch this unless you know what you're doing.
 
-  if (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) {
-    // If we're accessing a numeric property we'll treat that as if
-    // window.frames[n] is accessed (since window.frames === window),
-    // if window.frames[n] is a child frame, wrap the frame and return
-    // it without doing a security check.
-    uint32_t index = uint32_t(JSID_TO_INT(id));
-    bool found = false;
-    if (nsCOMPtr<nsIDOMWindow> frame = win->IndexedGetter(index, found)) {
-      // A numeric property accessed and the numeric property is a
-      // child frame, wrap the child frame without doing a security
-      // check and return.
-
-      nsGlobalWindow *frameWin = (nsGlobalWindow *)frame.get();
-      NS_ASSERTION(frameWin->IsOuterWindow(), "IndexedGetter gave us an inner?");
-
-      frameWin->EnsureInnerWindow();
-      JSObject *global = frameWin->GetGlobalJSObject();
-
-      // This null check fixes a hard-to-reproduce crash that occurs when we
-      // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
-      // comment 105.
-      if (MOZ_UNLIKELY(!global)) {
-        return NS_ERROR_FAILURE;
-      }
-
-      nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-      jsval v;
-      nsresult rv = WrapNative(cx, xpc_UnmarkGrayObject(global), frame,
-                               &NS_GET_IID(nsIDOMWindow), true, &v,
-                               getter_AddRefs(holder));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (!JS_WrapValue(cx, &v)) {
-        return NS_ERROR_FAILURE;
-      }
-
-      *vp = v;
-    }
-
-    return NS_SUCCESS_I_DID_SOMETHING;
-  }
-
   if (JSID_IS_STRING(id) && !JSVAL_IS_PRIMITIVE(*vp) &&
       ::JS_TypeOfValue(cx, *vp) != JSTYPE_FUNCTION) {
     // A named property accessed which could have been resolved to a
     // child frame in nsWindowSH::NewResolve() (*vp will tell us if
     // that's the case). If *vp is a window object (i.e. a child
     // frame), return without doing a security check.
     //
     // Calling GetWrappedNativeOfJSObject() is not all that cheap, so
@@ -6595,44 +6553,23 @@ LocationSetterUnwrapper(JSContext *cx, J
 NS_IMETHODIMP
 nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj_, jsid id_, uint32_t flags,
                        JSObject **objp, bool *_retval)
 {
   js::RootedObject obj(cx, obj_);
   js::RootedId id(cx, id_);
 
+  if (!JSID_IS_STRING(id)) {
+    return NS_OK;
+  }
+
   nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
   MOZ_ASSERT(win->IsInnerWindow());
 
-  if (!JSID_IS_STRING(id)) {
-    if (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0 && !(flags & JSRESOLVE_ASSIGNING)) {
-      // If we're resolving a numeric property, treat that as if
-      // window.frames[n] is resolved (since window.frames ===
-      // window), if window.frames[n] is a child frame, define a
-      // property for this index.
-      uint32_t index = uint32_t(JSID_TO_INT(id));
-      bool found;
-      nsCOMPtr<nsIDOMWindow> frame = win->IndexedGetter(index, found);
-      if (found) {
-        // A numeric property accessed and the numeric property is a
-        // child frame. Define a property for this index.
-
-        *_retval = ::JS_DefineElement(cx, obj, index, JSVAL_VOID,
-                                      nullptr, nullptr, JSPROP_SHARED);
-
-        if (*_retval) {
-          *objp = obj;
-        }
-      }
-    }
-
-    return NS_OK;
-  }
-
   nsIScriptContext *my_context = win->GetContextInternal();
 
   // Resolve standard classes on my_context's JSContext (or on cx,
   // if we don't have a my_context yet), in case the two contexts
   // have different origins.  We want lazy standard class
   // initialization to behave as if it were done eagerly, on each
   // window's own context (not on some other window-caller's
   // context).
@@ -6836,16 +6773,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
 
   if (s_content_id == id) {
     // Map window._content to window.content for backwards
     // compatibility, this should spit out an message on the JS
     // console.
 
     JSObject *windowObj = win->GetGlobalJSObject();
 
+    JSAutoCompartment ac(cx, windowObj);
     JSAutoRequest ar(cx);
 
     JSFunction *fun = ::JS_NewFunction(cx, ContentWindowGetter, 0, 0,
                                        windowObj, "_content");
     if (!fun) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set sw=2 ts=2 et tw=78: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 #include <algorithm>
 
 /* This must occur *after* base/basictypes.h to avoid typedefs conflicts. */
@@ -18,16 +18,17 @@
 #include "nsPerformance.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsBarProps.h"
 #include "nsDOMStorage.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsIPowerManagerService.h"
+#include "nsISizeOfEventTarget.h"
 
 #ifdef XP_WIN
 #ifdef GetClassName
 #undef GetClassName
 #endif // GetClassName
 #endif // XP_WIN
 
 // Helper Classes
@@ -529,56 +530,320 @@ class nsOuterWindowProxy : public js::Wr
 public:
   nsOuterWindowProxy() : js::Wrapper(0) { setSafeToUnwrap(false); }
 
   virtual bool isOuterWindow() {
     return true;
   }
   virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
   virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
+
+  // Fundamental traps
+  virtual bool getPropertyDescriptor(JSContext* cx, JSObject* proxy,
+                                     jsid id, JSPropertyDescriptor* desc,
+                                     unsigned flags) MOZ_OVERRIDE;
+  virtual bool getOwnPropertyDescriptor(JSContext* cx, JSObject* proxy,
+                                        jsid id, JSPropertyDescriptor* desc,
+                                        unsigned flags) MOZ_OVERRIDE;
+  virtual bool defineProperty(JSContext* cx, JSObject* proxy,
+                              jsid id, JSPropertyDescriptor* desc) MOZ_OVERRIDE;
+  virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy,
+                                   JS::AutoIdVector &props) MOZ_OVERRIDE;
+  virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id,
+                       bool *bp) MOZ_OVERRIDE;
+  virtual bool enumerate(JSContext *cx, JSObject *proxy,
+                         JS::AutoIdVector &props) MOZ_OVERRIDE;
+
+  // Derived traps
+  virtual bool has(JSContext *cx, JSObject *proxy, jsid id,
+                   bool *bp) MOZ_OVERRIDE;
+  virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id,
+                      bool *bp) MOZ_OVERRIDE;
   virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver,
-                   jsid id, js::Value *vp) MOZ_OVERRIDE;
+                   jsid id, JS::Value *vp) MOZ_OVERRIDE;
+  virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver,
+                   jsid id, bool strict, JS::Value *vp) MOZ_OVERRIDE;
+  virtual bool keys(JSContext *cx, JSObject *proxy,
+                    JS::AutoIdVector &props) MOZ_OVERRIDE;
+  virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags,
+                       JS::Value *vp) MOZ_OVERRIDE;
 
   static nsOuterWindowProxy singleton;
+
+protected:
+  nsGlobalWindow* GetWindow(JSObject *proxy)
+  {
+    return nsGlobalWindow::FromSupports(
+      static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
+  }
+
+  // False return value means we threw an exception.  True return value
+  // but false "found" means we didn't have a subframe at that index.
+  bool GetSubframeWindow(JSContext *cx, JSObject *proxy, jsid id,
+                         JS::Value *vp, bool &found);
+
+  // Returns a non-null window only if id is an index and we have a
+  // window at that index.
+  already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx,
+                                                   JSObject *proxy,
+                                                   jsid id);
+
+  bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
+                                  JS::AutoIdVector &props);
 };
 
 
 JSString *
 nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy)
 {
     MOZ_ASSERT(js::IsProxy(proxy));
 
     return JS_NewStringCopyZ(cx, "[object Window]");
 }
 
 void
 nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy)
 {
-  nsISupports *global =
-    static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate());
+  nsGlobalWindow* global = GetWindow(proxy);
   if (global) {
-    nsWrapperCache *cache;
-    CallQueryInterface(global, &cache);
-    cache->ClearWrapper();
+    global->ClearWrapper();
   }
 }
 
 bool
+nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx, JSObject* proxy,
+                                          jsid id,
+                                          JSPropertyDescriptor* desc,
+                                          unsigned flags)
+{
+  // The only thing we can do differently from js::Wrapper is shadow stuff with
+  // our indexed properties, so we can just try getOwnPropertyDescriptor and if
+  // that gives us nothing call on through to js::Wrapper.
+  desc->obj = nullptr;
+  if (!getOwnPropertyDescriptor(cx, proxy, id, desc, flags)) {
+    return false;
+  }
+
+  if (desc->obj) {
+    return true;
+  }
+
+  return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc, flags);
+}
+
+bool
+nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx, JSObject* proxy,
+                                             jsid id,
+                                             JSPropertyDescriptor* desc,
+                                             unsigned flags)
+{
+  bool found;
+  if (!GetSubframeWindow(cx, proxy, id, &desc->value, found)) {
+    return false;
+  }
+  if (found) {
+    FillPropertyDescriptor(desc, proxy, true);
+    return true;
+  }
+  // else fall through to js::Wrapper
+
+  return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc, flags);
+}
+
+bool
+nsOuterWindowProxy::defineProperty(JSContext* cx, JSObject* proxy,
+                                   jsid id, JSPropertyDescriptor* desc)
+{
+  if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
+    // Don't define anything; we're done here, since the spec requires
+    // that we treat our indexed properties as readonly.
+    return true;
+  }
+
+  return js::Wrapper::defineProperty(cx, proxy, id, desc);
+}
+
+bool
+nsOuterWindowProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy,
+                                        JS::AutoIdVector &props)
+{
+  // Just our indexed stuff followed by our "normal" own property names.
+  if (!AppendIndexedPropertyNames(cx, proxy, props)) {
+    return false;
+  }
+
+  JS::AutoIdVector innerProps(cx);
+  if (!js::Wrapper::getOwnPropertyNames(cx, proxy, innerProps)) {
+    return false;
+  }
+  return js::AppendUnique(cx, props, innerProps);
+}
+
+bool
+nsOuterWindowProxy::delete_(JSContext *cx, JSObject *proxy, jsid id,
+                            bool *bp)
+{
+  if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
+    // Reject (which means throw if and only if strict) the set.
+    // Except we don't even know whether we're strict.  See bug 803157.
+    *bp = false;
+    return true;
+  }
+
+  return js::Wrapper::delete_(cx, proxy, id, bp);
+}
+
+bool
+nsOuterWindowProxy::enumerate(JSContext *cx, JSObject *proxy,
+                              JS::AutoIdVector &props)
+{
+  // Just our indexed stuff followed by our "normal" own property names.
+  if (!AppendIndexedPropertyNames(cx, proxy, props)) {
+    return false;
+  }
+
+  JS::AutoIdVector innerProps(cx);
+  if (!js::Wrapper::enumerate(cx, proxy, innerProps)) {
+    return false;
+  }
+  return js::AppendUnique(cx, props, innerProps);
+}
+
+bool
+nsOuterWindowProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+{
+  if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
+    *bp = true;
+    return true;
+  }
+
+  return js::Wrapper::has(cx, proxy, id, bp);
+}
+
+bool
+nsOuterWindowProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+{
+  if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
+    *bp = true;
+    return true;
+  }
+
+  return js::Wrapper::hasOwn(cx, proxy, id, bp);
+}
+
+bool
 nsOuterWindowProxy::get(JSContext *cx, JSObject *wrapper, JSObject *receiver,
-                        jsid id, js::Value *vp)
+                        jsid id, JS::Value *vp)
 {
   if (id == nsDOMClassInfo::sWrappedJSObject_id &&
       xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
     *vp = JS::ObjectValue(*wrapper);
     return true;
   }
 
+  bool found;
+  if (!GetSubframeWindow(cx, wrapper, id, vp, found)) {
+    return false;
+  }
+  if (found) {
+    return true;
+  }
+  // Else fall through to js::Wrapper
+
   return js::Wrapper::get(cx, wrapper, receiver, id, vp);
 }
 
+bool
+nsOuterWindowProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver,
+                        jsid id, bool strict, JS::Value *vp)
+{
+  if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
+    // Reject (which means throw if and only if strict) the set.
+    if (strict) {
+      // XXXbz This needs to throw, but see bug 828137.
+    }
+    return true;
+  }
+
+  return js::Wrapper::set(cx, proxy, receiver, id, strict, vp);
+}
+
+bool
+nsOuterWindowProxy::keys(JSContext *cx, JSObject *proxy,
+                         JS::AutoIdVector &props)
+{
+  // BaseProxyHandler::keys seems to do what we want here: call
+  // getOwnPropertyNames and then filter out the non-enumerable properties.
+  return js::BaseProxyHandler::keys(cx, proxy, props);
+}
+
+bool
+nsOuterWindowProxy::iterate(JSContext *cx, JSObject *proxy, unsigned flags,
+                            JS::Value *vp)
+{
+  // BaseProxyHandler::iterate seems to do what we want here: fall
+  // back on the property names returned from keys() and enumerate().
+  return js::BaseProxyHandler::iterate(cx, proxy, flags, vp);
+}
+
+bool
+nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, JSObject *proxy,
+                                      jsid id, JS::Value* vp,
+                                      bool& found)
+{
+  nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id);
+  if (!frame) {
+    found = false;
+    return true;
+  }
+
+  found = true;
+  // Just return the window's global
+  nsGlobalWindow* global = static_cast<nsGlobalWindow*>(frame.get());
+  global->EnsureInnerWindow();
+  JSObject* obj = global->FastGetGlobalJSObject();
+  // This null check fixes a hard-to-reproduce crash that occurs when we
+  // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
+  // comment 105.
+  if (MOZ_UNLIKELY(!obj)) {
+    return xpc::Throw(cx, NS_ERROR_FAILURE);
+  }
+  *vp = JS::ObjectValue(*obj);
+  return JS_WrapValue(cx, vp);
+}
+
+already_AddRefed<nsIDOMWindow>
+nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, JSObject *proxy, jsid id)
+{
+  int32_t index = GetArrayIndexFromId(cx, id);
+  if (!IsArrayIndex(index)) {
+    return nullptr;
+  }
+
+  nsGlobalWindow* win = GetWindow(proxy);
+  bool unused;
+  return win->IndexedGetter(index, unused);
+}
+
+bool
+nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
+                                               JS::AutoIdVector &props)
+{
+  uint32_t length = GetWindow(proxy)->GetLength();
+  MOZ_ASSERT(int32_t(length) >= 0);
+  if (!props.reserve(props.length() + length)) {
+    return false;
+  }
+  for (int32_t i = 0; i < int32_t(length); ++i) {
+    props.append(INT_TO_JSID(i));
+  }
+
+  return true;
+}
+
 nsOuterWindowProxy
 nsOuterWindowProxy::singleton;
 
 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
 {
 public:
   nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
 
@@ -10794,16 +11059,27 @@ nsGlobalWindow::HasIndexedDBSupport()
 
 // static
 bool
 nsPIDOMWindow::HasPerformanceSupport()
 {
   return Preferences::GetBool("dom.enable_performance", false);
 }
 
+static size_t
+SizeOfEventTargetObjectsEntryExcludingThisFun(
+  nsPtrHashKey<nsDOMEventTargetHelper> *aEntry,
+  nsMallocSizeOfFun aMallocSizeOf,
+  void *arg)
+{
+  nsISupports *supports = aEntry->GetKey();
+  nsCOMPtr<nsISizeOfEventTarget> iface = do_QueryInterface(supports);
+  return iface ? iface->SizeOfEventTargetIncludingThis(aMallocSizeOf) : 0;
+}
+
 void
 nsGlobalWindow::SizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
 {
   aWindowSizes->mDOMOther += aWindowSizes->mMallocSizeOf(this);
 
   if (IsInnerWindow()) {
     nsEventListenerManager* elm =
       const_cast<nsGlobalWindow*>(this)->GetListenerManager(false);
@@ -10814,16 +11090,21 @@ nsGlobalWindow::SizeOfIncludingThis(nsWi
     if (mDoc) {
       mDoc->DocSizeOfIncludingThis(aWindowSizes);
     }
   }
 
   aWindowSizes->mDOMOther +=
     mNavigator ?
       mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf) : 0;
+
+  aWindowSizes->mDOMEventTargets +=
+    mEventTargetObjects.SizeOfExcludingThis(
+      SizeOfEventTargetObjectsEntryExcludingThisFun,
+      aWindowSizes->mMallocSizeOf);
 }
 
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                   nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
new file mode 100644
--- /dev/null
+++ b/dom/base/nsISizeOfEventTarget.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsISizeOfEventTarget_h___
+#define nsISizeOfEventTarget_h___
+
+#include "nsISupports.h"
+
+#define NS_ISIZEOFEVENTTARGET_IID \
+  {0xa1e08cb9, 0x5455, 0x4593, \
+    { 0xb4, 0x1f, 0x38, 0x7a, 0x85, 0x44, 0xd0, 0xb5 }}
+
+/**
+ * This class is much the same as nsISizeOf, but is specifically for measuring
+ * the contents of nsGlobalWindow::mEventTargetObjects.
+ *
+ * We don't use nsISizeOf because if we did, any object belonging to
+ * mEventTargetObjects that implements nsISizeOf would be measured, which we
+ * may not want (perhaps because the object is also measured elsewhere).
+ */
+class nsISizeOfEventTarget : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISIZEOFEVENTTARGET_IID)
+
+  /**
+   * Measures the size of the things pointed to by the object, plus the object
+   * itself.
+   */
+  virtual size_t
+    SizeOfEventTargetIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISizeOfEventTarget, NS_ISIZEOFEVENTTARGET_IID)
+
+#endif /* nsISizeOfEventTarget_h___ */
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWindowMemoryReporter.h"
 #include "nsGlobalWindow.h"
 #include "nsIEffectiveTLDService.h"
 #include "mozilla/Services.h"
@@ -164,37 +165,42 @@ CollectWindowReports(nsGlobalWindow *aWi
                       NS_LITERAL_CSTRING(_desc), aClosure);                   \
         NS_ENSURE_SUCCESS(rv, rv);                                            \
     }                                                                         \
   } while (0)
 
   nsWindowSizes windowSizes(WindowsMallocSizeOf);
   aWindow->SizeOfIncludingThis(&windowSizes);
 
-  REPORT("/dom/other", windowSizes.mDOMOther,
-         "Memory used by a window's DOM, excluding element, text, CDATA, "
-         "and comment nodes.");
-  aWindowTotalSizes->mDOMOther += windowSizes.mDOMOther;
-
   REPORT("/dom/element-nodes", windowSizes.mDOMElementNodes,
          "Memory used by the element nodes in a window's DOM.");
   aWindowTotalSizes->mDOMElementNodes += windowSizes.mDOMElementNodes;
 
   REPORT("/dom/text-nodes", windowSizes.mDOMTextNodes,
          "Memory used by the text nodes in a window's DOM.");
   aWindowTotalSizes->mDOMTextNodes += windowSizes.mDOMTextNodes;
 
   REPORT("/dom/cdata-nodes", windowSizes.mDOMCDATANodes,
          "Memory used by the CDATA nodes in a window's DOM.");
   aWindowTotalSizes->mDOMCDATANodes += windowSizes.mDOMCDATANodes;
 
   REPORT("/dom/comment-nodes", windowSizes.mDOMCommentNodes,
          "Memory used by the comment nodes in a window's DOM.");
   aWindowTotalSizes->mDOMCommentNodes += windowSizes.mDOMCommentNodes;
 
+  REPORT("/dom/event-targets", windowSizes.mDOMEventTargets,
+         "Memory used by the event targets table in a window's DOM, and the "
+         "objects it points to, which include XHRs.");
+  aWindowTotalSizes->mDOMEventTargets += windowSizes.mDOMEventTargets;
+
+  REPORT("/dom/other", windowSizes.mDOMOther,
+         "Memory used by a window's DOM that isn't measured by the other "
+         "'dom/' numbers.");
+  aWindowTotalSizes->mDOMOther += windowSizes.mDOMOther;
+
   REPORT("/property-tables",
          windowSizes.mPropertyTables,
          "Memory used for the property tables within a window.");
   aWindowTotalSizes->mPropertyTables += windowSizes.mPropertyTables;
 
   REPORT("/style-sheets", windowSizes.mStyleSheets,
          "Memory used by style sheets within a window.");
   aWindowTotalSizes->mStyleSheets += windowSizes.mStyleSheets;
@@ -258,17 +264,17 @@ CollectWindowReports(nsGlobalWindow *aWi
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 
   if (frameSundriesSize > 0) {
     REPORT("/layout/frames/sundries", frameSundriesSize,
            "The sum of all memory used by frames which were too small "
            "to be shown individually.");
   }
- 
+
 #undef REPORT
 
   return NS_OK;
 }
 
 typedef nsTArray< nsRefPtr<nsGlobalWindow> > WindowArray;
 
 static
@@ -327,90 +333,77 @@ nsWindowMemoryReporter::CollectReports(n
     nsresult rv;                                                              \
     rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),             \
                        nsIMemoryReporter::KIND_OTHER,                         \
                        nsIMemoryReporter::UNITS_BYTES, _amount,               \
                        NS_LITERAL_CSTRING(_desc), aClosure);                  \
     NS_ENSURE_SUCCESS(rv, rv);                                                \
   } while (0)
 
-  REPORT("window-objects/dom/other", windowTotalSizes.mDOMOther, 
-         "Memory used for the DOM within windows, "
-         "excluding element, text, CDATA, and comment nodes. "
-         "This is the sum of all windows' 'dom/other' numbers.");
-
   REPORT("window-objects/dom/element-nodes", windowTotalSizes.mDOMElementNodes,
-         "Memory used for DOM element nodes within windows. "
          "This is the sum of all windows' 'dom/element-nodes' numbers.");
 
   REPORT("window-objects/dom/text-nodes", windowTotalSizes.mDOMTextNodes,
-         "Memory used for DOM text nodes within windows. "
          "This is the sum of all windows' 'dom/text-nodes' numbers.");
 
   REPORT("window-objects/dom/cdata-nodes", windowTotalSizes.mDOMCDATANodes,
-         "Memory used for DOM CDATA nodes within windows. "
          "This is the sum of all windows' 'dom/cdata-nodes' numbers.");
 
   REPORT("window-objects/dom/comment-nodes", windowTotalSizes.mDOMCommentNodes,
-         "Memory used for DOM comment nodes within windows. "
          "This is the sum of all windows' 'dom/comment-nodes' numbers.");
 
+  REPORT("window-objects/dom/event-targets", windowTotalSizes.mDOMEventTargets,
+         "This is the sum of all windows' 'dom/event-targets' numbers.");
+
+  REPORT("window-objects/dom/other", windowTotalSizes.mDOMOther,
+         "This is the sum of all windows' 'dom/other' numbers.");
+
   REPORT("window-objects/property-tables",
          windowTotalSizes.mPropertyTables,
-         "Memory used for property tables within windows. "
          "This is the sum of all windows' 'property-tables' numbers.");
 
-  REPORT("window-objects/style-sheets", windowTotalSizes.mStyleSheets, 
-         "Memory used for style sheets within windows. "
+  REPORT("window-objects/style-sheets", windowTotalSizes.mStyleSheets,
          "This is the sum of all windows' 'style-sheets' numbers.");
-    
-  REPORT("window-objects/layout/pres-shell", windowTotalSizes.mLayoutPresShell, 
-         "Memory used by layout PresShell and other related "
-         "areas within windows. This is the sum of all windows' "
-         "'layout/arenas' numbers.");
-    
+
+  REPORT("window-objects/layout/pres-shell", windowTotalSizes.mLayoutPresShell,
+         "This is the sum of all windows' 'layout/arenas' numbers.");
+
   REPORT("window-objects/layout/line-boxes",
-         windowTotalSizes.mArenaStats.mLineBoxes, 
-         "Memory used for line-boxes within windows. "
+         windowTotalSizes.mArenaStats.mLineBoxes,
          "This is the sum of all windows' 'layout/line-boxes' numbers.");
 
   REPORT("window-objects/layout/rule-nodes",
          windowTotalSizes.mArenaStats.mRuleNodes,
-         "Memory used for CSS rule nodes within windows. "
          "This is the sum of all windows' 'layout/rule-nodes' numbers.");
 
   REPORT("window-objects/layout/style-contexts",
          windowTotalSizes.mArenaStats.mStyleContexts,
-         "Memory used for style contexts within windows. "
          "This is the sum of all windows' 'layout/style-contexts' numbers.");
 
-  REPORT("window-objects/layout/style-sets", windowTotalSizes.mLayoutStyleSets, 
-         "Memory used for style sets within windows. "
+  REPORT("window-objects/layout/style-sets", windowTotalSizes.mLayoutStyleSets,
          "This is the sum of all windows' 'layout/style-sets' numbers.");
-    
-  REPORT("window-objects/layout/text-runs", windowTotalSizes.mLayoutTextRuns, 
-         "Memory used for text runs within windows. "
+
+  REPORT("window-objects/layout/text-runs", windowTotalSizes.mLayoutTextRuns,
          "This is the sum of all windows' 'layout/text-runs' numbers.");
 
   REPORT("window-objects/layout/pres-contexts", windowTotalSizes.mLayoutPresContext,
-         "Memory used for layout PresContexts within windows. "
          "This is the sum of all windows' 'layout/pres-contexts' numbers.");
 
   size_t frameTotal = 0;
 #define FRAME_ID(classname)                \
   frameTotal += windowTotalSizes.mArenaStats.FRAME_ID_STAT_FIELD(classname);
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 
   REPORT("window-objects/layout/frames", frameTotal,
          "Memory used for layout frames within windows. "
          "This is the sum of all windows' 'layout/frames/' numbers.");
 
 #undef REPORT
-    
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::GetExplicitNonHeap(int64_t* aAmount)
 {
   // This reporter only measures heap memory, so we don't need to report any
   // bytes for it.  However, the JS multi-reporter needs to be invoked.
@@ -458,17 +451,17 @@ nsWindowMemoryReporter::ObserveDOMWindow
   }
 }
 
 static PLDHashOperator
 BackdateTimeStampsEnumerator(nsISupports *aKey, TimeStamp &aTimeStamp,
                              void* aClosure)
 {
   TimeStamp *minTimeStamp = static_cast<TimeStamp*>(aClosure);
-  
+
   if (!aTimeStamp.IsNull() && aTimeStamp > *minTimeStamp) {
     aTimeStamp = *minTimeStamp;
   }
 
   return PL_DHASH_NEXT;
 }
 
 void
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWindowMemoryReporter_h__
 #define nsWindowMemoryReporter_h__
 
 #include "nsIMemoryReporter.h"
@@ -29,16 +30,17 @@ public:
     mMallocSizeOf = aMallocSizeOf;
   }
   nsMallocSizeOfFun mMallocSizeOf;
   nsArenaMemoryStats mArenaStats;
   size_t mDOMElementNodes;
   size_t mDOMTextNodes;
   size_t mDOMCDATANodes;
   size_t mDOMCommentNodes;
+  size_t mDOMEventTargets;
   size_t mDOMOther;
   size_t mStyleSheets;
   size_t mLayoutPresShell;
   size_t mLayoutStyleSets;
   size_t mLayoutTextRuns;
   size_t mLayoutPresContext;
   size_t mPropertyTables;
 };
--- a/dom/base/test/Makefile.in
+++ b/dom/base/test/Makefile.in
@@ -18,16 +18,17 @@ MOCHITEST_FILES = \
   test_e4x_for_each.html \
   test_gsp-standards.html \
   test_gsp-quirks.html \
   test_gsp-qualified.html \
   test_nondomexception.html \
   test_screen_orientation.html \
   test_window_constructor.html \
   test_window_enumeration.html \
+  test_window_indexing.html \
   test_writable-replaceable.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
    test_bug715041.xul \
    test_bug715041_removal.xul \
    $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_indexing.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=823228
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 823228</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=823228">Mozilla Bug 823228</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe name="x" id="x"></iframe>
+  <iframe name="y" id="y"></iframe>
+</div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+  /** Test for Bug 823228 **/
+  is(window, window.frames, "These better be equal");
+  ok("0" in window, "We have a subframe");
+  ok("1" in window, "We have two subframes");
+  ok(!("2" in window), "But we don't have three subframes");
+  window[2] = "myString";
+  ok("2" in window, "Should be able to set expando");
+  Object.getPrototypeOf(window)[3] = "Hey there";
+  ok("3" in window, "Should be walking up the proto chain");
+
+  is(window[0].name, "x", "First frame is x");
+  is(window[1].name, "y", "Second frame is y");
+  is(window[2], "myString", "We should still have our expando");
+  is(window[3], "Hey there", "We should still have our prop on the proto chain");
+
+  var x = $("x");
+  var y = $("y");
+
+  is(x.contentWindow, window[0], "First frame should have correct window");
+  is(y.contentWindow, window[1], "Second frame should have correct window");
+
+  // set() hook test
+  window[1] = "FAIL";
+  is(window[1].name, "y", "Second frame is still y");
+  y.parentNode.removeChild(y);
+  ok(!("1" in window), "We no longer have two subframes");
+  is(window[1], undefined, "We should not have a value here");
+
+  // defineProperty() hook test
+  x.parentNode.appendChild(y);
+  Object.defineProperty(window, "1", { value: "FAIL2", configurable: true,
+				       writable: true })
+  y.parentNode.removeChild(y);
+  ok(!("1" in window), "We no longer have two subframes, again");
+  is(window[1], undefined, "We should not have a value here either");
+
+  // More set() hook test
+  window[1] = "PASS";
+  is(window[1], "PASS", "Should be able to set expando on window[1] now");
+  var desc = Object.getOwnPropertyDescriptor(window, "1");
+  ok(desc.configurable, "Expando should be configurable");
+  ok(desc.enumerable, "Expando should be configurable");
+  ok(desc.writable, "Expando should be writable");
+  is(desc.value, "PASS", "Expando should have correct value");
+
+  // Sneaky shadowing (get() hook)
+  x.parentNode.appendChild(y);
+  is(window[1], y.contentWindow, "Second frame should now shadow expando");
+  desc = Object.getOwnPropertyDescriptor(window, "1");
+  ok(desc.configurable, "Subframe should be configurable");
+  ok(desc.enumerable, "Subframe should be configurable");
+  ok(!desc.writable, "Subframe should not be writable");
+  is(desc.value, y.contentWindow, "Subframe should have correct value");
+
+  // And unshadowing
+  y.parentNode.removeChild(y);
+  is(window[1], "PASS", "And now we should be able to see the expando again");
+
+  // And more defineProperty()
+  Object.defineProperty(window, "1", { value: "PASS2", configurable: true,
+				       writable: true })
+  is(window[1], "PASS2", "Defining past end of list should work");
+
+  // Enumeration tests
+  x.parentNode.appendChild(y);
+
+  var names = Object.getOwnPropertyNames(window);
+  is(names[0], "0", "Must start with 0");
+  is(names[1], "1", "Must continue with 1");
+  isnot(names.indexOf("2"), -1, "And then 2, defined earlier, should be in there");
+  is(names.indexOf("3"), -1, "But no 3; that's on the proto");
+
+  names = [];
+  for (var name in window) {
+    names.push(name);
+  }
+  is(names[0], "0", "Enumeration must start with 0");
+  is(names[1], "1", "Enumeration must continue with 1");
+  isnot(names.indexOf("2"), -1, "Enumeration: and then 2, defined earlier");
+  isnot(names.indexOf("3"), -1, "Enumeration: and then 3, defined on the proto");
+  is(names.indexOf("4"), -1, "But no 4 around");
+
+  // Delete tests
+  delete window[1];
+  is(window[1], y.contentWindow, "Shouldn't be able to delete a supported index");
+  y.parentNode.removeChild(y);
+  is(window[1], "PASS2",
+     "And shouldn't have deleted the thing we were shadowing either");
+  delete window[1];
+  is(window[1], undefined,
+     "But should be able to delete unshadowed things");
+  </script>
+</body>
+</html>
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -719,41 +719,52 @@ BluetoothHfpManager::ReceiveSocketData(U
     NS_ASSERTION(vgm >= 0 && vgm <= 15, "Received invalid VGM value");
     mCurrentVgm = vgm;
   } else if (msg.Find("AT+CHLD=?") != -1) {
     SendLine("+CHLD: (1,2)");
   } else if (msg.Find("AT+CHLD=") != -1) {
     ParseAtCommand(msg, 8, atCommandValues);
 
     if (atCommandValues.IsEmpty()) {
-      NS_WARNING("Could't get the value of command [AT+VGS=]");
-      goto respond_with_ok;
-    }
-
-    nsresult rv;
-    int chld = atCommandValues[0].ToInteger(&rv);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to extract volume value from bluetooth headset!");
+      NS_WARNING("Could't get the value of command [AT+CHLD=]");
       goto respond_with_ok;
     }
 
-    switch(chld) {
-      case 1:
-        // Releases active calls and accepts the other (held or waiting) call
-        NotifyDialer(NS_LITERAL_STRING("CHUP+ATA"));
-        break;
-      case 2:
-        // Places active calls on hold and accepts the other (held or waiting) call
-        NotifyDialer(NS_LITERAL_STRING("CHLD+ATA"));
-        break;
-      default:
-#ifdef DEBUG
-        NS_WARNING("Not handling chld value");
-#endif
-        break;
+    char chld = atCommandValues[0][0];
+    if (chld < '0' || chld > '4') {
+      NS_WARNING("Wrong value of command [AT+CHLD]");
+      SendLine("ERROR");
+      return;
+    }
+
+    /**
+     * The following two cases are supported:
+     * AT+CHLD=1 - Releases active calls and accepts the other (held or
+     *             waiting) call
+     * AT+CHLD=2 - Places active calls on hold and accepts the other (held
+     *             or waiting) call
+     *
+     * The following cases are NOT supported yet:
+     * AT+CHLD=0, AT+CHLD=1<idx>, AT+CHLD=2<idx>, AT+CHLD=3, AT+CHLD=4
+     * Please see 4.33.2 in Bluetooth hands-free profile 1.6 for more
+     * information.
+     */
+
+    // No idx shall be included
+    if (atCommandValues[0].Length() > 1) {
+      SendLine("ERROR");
+      return;
+    }
+
+    if (chld == '1') {
+      NotifyDialer(NS_LITERAL_STRING("CHUP+ATA"));
+    } else if (chld == '2') {
+      NotifyDialer(NS_LITERAL_STRING("CHLD+ATA"));
+    } else {
+      NS_WARNING("Not handling chld value");
     }
   } else if (msg.Find("AT+VGS=") != -1) {
     // Adjust volume by headset
     mReceiveVgsFlag = true;
     ParseAtCommand(msg, 7, atCommandValues);
 
     if (atCommandValues.IsEmpty()) {
       NS_WARNING("Could't get the value of command [AT+VGS=]");
@@ -766,34 +777,34 @@ BluetoothHfpManager::ReceiveSocketData(U
       NS_WARNING("Failed to extract volume value from bluetooth headset!");
       goto respond_with_ok;
     }
 
     if (newVgs == mCurrentVgs) {
       goto respond_with_ok;
     }
 
-#ifdef DEBUG
     NS_ASSERTION(newVgs >= 0 && newVgs <= 15, "Received invalid VGS value");
-#endif
 
     nsString data;
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     data.AppendInt(newVgs);
     os->NotifyObservers(nullptr, "bluetooth-volume-change", data.get());
   } else if (msg.Find("AT+BLDN") != -1) {
     NotifyDialer(NS_LITERAL_STRING("BLDN"));
   } else if (msg.Find("ATA") != -1) {
     NotifyDialer(NS_LITERAL_STRING("ATA"));
   } else if (msg.Find("AT+CHUP") != -1) {
     NotifyDialer(NS_LITERAL_STRING("CHUP"));
   } else if (msg.Find("ATD>") != -1) {
     // Currently, we don't support memory dialing in Dialer app
     SendLine("ERROR");
     return;
+  } else if (msg.Find("AT+CLCC") != -1) {
+    SendCommand("+CLCC: ");
   } else if (msg.Find("ATD") != -1) {
     nsAutoCString message(msg), newMsg;
     int end = message.FindChar(';');
     if (end < 0) {
       NS_WARNING("Could't get the value of command [ATD]");
       goto respond_with_ok;
     }
 
--- a/dom/camera/GonkNativeWindow.cpp
+++ b/dom/camera/GonkNativeWindow.cpp
@@ -45,25 +45,30 @@ GonkNativeWindow::GonkNativeWindow(GonkN
 GonkNativeWindow::GonkNativeWindow()
   : mNewFrameCallback(nullptr)
 {
     GonkNativeWindow::init();
 }
 
 GonkNativeWindow::~GonkNativeWindow()
 {
-    freeAllBuffersLocked();
+    nsAutoTArray<SurfaceDescriptor, NUM_BUFFER_SLOTS> freeList;
+    freeAllBuffersLocked(freeList);
+    releaseBufferFreeListUnlocked(freeList);
 }
 
 void GonkNativeWindow::abandon()
 {
-    Mutex::Autolock lock(mMutex);
-    ++mGeneration;
-    CNW_LOGD("abandon: new generation %d", mGeneration);
-    freeAllBuffersLocked();
+    nsAutoTArray<SurfaceDescriptor, NUM_BUFFER_SLOTS> freeList;
+    {
+        Mutex::Autolock lock(mMutex);
+        freeAllBuffersLocked(freeList);
+    }
+
+    releaseBufferFreeListUnlocked(freeList);
     mDequeueCondition.signal();
 }
 
 void GonkNativeWindow::init()
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
     ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
@@ -128,168 +133,234 @@ int GonkNativeWindow::hook_query(const A
 int GonkNativeWindow::hook_perform(ANativeWindow* window, int operation, ...)
 {
     va_list args;
     va_start(args, operation);
     GonkNativeWindow* c = getSelf(window);
     return c->perform(operation, args);
 }
 
-void GonkNativeWindow::freeBufferLocked(int i)
+void GonkNativeWindow::freeAllBuffersLocked(nsTArray<SurfaceDescriptor>& freeList)
 {
-    ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
-    if (mSlots[i].mGraphicBuffer != NULL) {
-        // Don't destroy the gralloc buffer if it is still in the
-        // video stream awaiting rendering.
-        if (mSlots[i].mBufferState != BufferSlot::RENDERING) {
-            ibc->DeallocSurfaceDescriptorGralloc(mSlots[i].mSurfaceDescriptor);
+    CNW_LOGD("freeAllBuffersLocked: from generation %d", mGeneration);
+    ++mGeneration;
+
+    for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+        if (mSlots[i].mGraphicBuffer != NULL) {
+            // Don't try to destroy the gralloc buffer if it is still in the
+            // video stream awaiting rendering.
+            if (mSlots[i].mBufferState != BufferSlot::RENDERING) {
+                SurfaceDescriptor* desc = freeList.AppendElement();
+                *desc = mSlots[i].mSurfaceDescriptor;
+            }
+            mSlots[i].mGraphicBuffer = NULL;
+            mSlots[i].mBufferState = BufferSlot::FREE;
+            mSlots[i].mFrameNumber = 0;
         }
-        mSlots[i].mGraphicBuffer = NULL;
-        mSlots[i].mBufferState = BufferSlot::FREE;
-        mSlots[i].mFrameNumber = 0;
-    }
-}
-
-void GonkNativeWindow::freeAllBuffersLocked()
-{
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        freeBufferLocked(i);
     }
 }
 
-int GonkNativeWindow::setBufferCount(int bufferCount) {
-    CNW_LOGD("setBufferCount: count=%d", bufferCount);
-    Mutex::Autolock lock(mMutex);
-
-    if (bufferCount > NUM_BUFFER_SLOTS) {
-        CNW_LOGE("setBufferCount: bufferCount larger than slots available");
-        return BAD_VALUE;
-    }
+void GonkNativeWindow::releaseBufferFreeListUnlocked(nsTArray<SurfaceDescriptor>& freeList)
+{
+    // This function MUST ONLY be called with mMutex unlocked; else there
+    // is a risk of deadlock with the ImageBridge thread.
 
-    // special-case, nothing to do
-    if (bufferCount == mBufferCount) {
-        return OK;
-    }
+    CNW_LOGD("releaseBufferFreeListUnlocked: E");
+    ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
 
-    if (bufferCount < MIN_BUFFER_SLOTS) {
-        CNW_LOGE("setBufferCount: requested buffer count (%d) is less than "
-                "minimum (%d)", bufferCount, MIN_BUFFER_SLOTS);
-        return BAD_VALUE;
+    for (uint32_t i = 0; i < freeList.Length(); ++i) {
+        ibc->DeallocSurfaceDescriptorGralloc(freeList[i]);
     }
 
-    // Error out if the user has dequeued buffers or sent buffers to
-    // video stream
-    for (int i=0 ; i<mBufferCount ; i++) {
-        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED ||
-            mSlots[i].mBufferState == BufferSlot::RENDERING) {
-            CNW_LOGE("setBufferCount: client owns some buffers");
-            return -EINVAL;
+    freeList.Clear();
+    CNW_LOGD("releaseBufferFreeListUnlocked: X");
+}
+
+int GonkNativeWindow::setBufferCount(int bufferCount)
+{
+    CNW_LOGD("setBufferCount: count=%d", bufferCount);
+    nsAutoTArray<SurfaceDescriptor, NUM_BUFFER_SLOTS> freeList;
+
+    {
+        Mutex::Autolock lock(mMutex);
+
+        if (bufferCount > NUM_BUFFER_SLOTS) {
+            CNW_LOGE("setBufferCount: bufferCount larger than slots available");
+            return BAD_VALUE;
+        }
+
+        // special-case, nothing to do
+        if (bufferCount == mBufferCount) {
+            return OK;
         }
+
+        if (bufferCount < MIN_BUFFER_SLOTS) {
+            CNW_LOGE("setBufferCount: requested buffer count (%d) is less than "
+                    "minimum (%d)", bufferCount, MIN_BUFFER_SLOTS);
+            return BAD_VALUE;
+        }
+
+        // Error out if the user has dequeued buffers or sent buffers to
+        // video stream
+        for (int i=0 ; i<mBufferCount ; i++) {
+            if (mSlots[i].mBufferState == BufferSlot::DEQUEUED ||
+                mSlots[i].mBufferState == BufferSlot::RENDERING) {
+                CNW_LOGE("setBufferCount: client owns some buffers");
+                return -EINVAL;
+            }
+        }
+
+        if (bufferCount > mBufferCount) {
+            // easy, we just have more buffers
+            mBufferCount = bufferCount;
+            mDequeueCondition.signal();
+            return OK;
+        }
+
+        // reducing the number of buffers
+        // here we're guaranteed that the client doesn't have dequeued buffers
+        // and will release all of its buffer references.
+        freeAllBuffersLocked(freeList);
+        mBufferCount = bufferCount;
+        mDequeueCondition.signal();
     }
 
-    if (bufferCount > mBufferCount) {
-        // easy, we just have more buffers
-        mBufferCount = bufferCount;
-        mDequeueCondition.signal();
-        return OK;
-    }
-
-    // reducing the number of buffers
-    // here we're guaranteed that the client doesn't have dequeued buffers
-    // and will release all of its buffer references.
-    freeAllBuffersLocked();
-    mBufferCount = bufferCount;
-    mDequeueCondition.signal();
+    releaseBufferFreeListUnlocked(freeList);
     return OK;
 }
 
 int GonkNativeWindow::dequeueBuffer(android_native_buffer_t** buffer)
 {
-    Mutex::Autolock lock(mMutex);
+    uint32_t defaultWidth;
+    uint32_t defaultHeight;
+    uint32_t pixelFormat;
+    uint32_t usage;
+    uint32_t generation;
+    bool alloc = false;
+    int buf = INVALID_BUFFER_SLOT;
 
-    int found = -1;
-    int dequeuedCount = 0;
-    bool tryAgain = true;
+    {
+        Mutex::Autolock lock(mMutex);
+        generation = mGeneration;
+
+        int found = -1;
+        int dequeuedCount = 0;
+        bool tryAgain = true;
 
-    CNW_LOGD("dequeueBuffer: E");
-    while (tryAgain) {
-        // look for a free buffer to give to the client
-        found = INVALID_BUFFER_SLOT;
-        dequeuedCount = 0;
-        for (int i = 0; i < mBufferCount; i++) {
-            const int state = mSlots[i].mBufferState;
-            if (state == BufferSlot::DEQUEUED) {
-                dequeuedCount++;
+        CNW_LOGD("dequeueBuffer: E");
+        while (tryAgain) {
+            // look for a free buffer to give to the client
+            found = INVALID_BUFFER_SLOT;
+            dequeuedCount = 0;
+            for (int i = 0; i < mBufferCount; i++) {
+                const int state = mSlots[i].mBufferState;
+                if (state == BufferSlot::DEQUEUED) {
+                    dequeuedCount++;
+                }
+                else if (state == BufferSlot::FREE) {
+                    /* We return the oldest of the free buffers to avoid
+                     * stalling the producer if possible.  This is because
+                     * the consumer may still have pending reads of the
+                     * buffers in flight.
+                     */
+                    if (found < 0 ||
+                        mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
+                        found = i;
+                    }
+                }
             }
-            else if (state == BufferSlot::FREE) {
-                /* We return the oldest of the free buffers to avoid
-                 * stalling the producer if possible.  This is because
-                 * the consumer may still have pending reads of the
-                 * buffers in flight.
-                 */
-                if (found < 0 ||
-                    mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
-                    found = i;
-                }
+
+            // we're in synchronous mode and didn't find a buffer, we need to
+            // wait for some buffers to be consumed
+            tryAgain = (found == INVALID_BUFFER_SLOT);
+            if (tryAgain) {
+                CNW_LOGD("dequeueBuffer: Try again");
+                mDequeueCondition.wait(mMutex);
+                CNW_LOGD("dequeueBuffer: Now");
             }
         }
 
-        // we're in synchronous mode and didn't find a buffer, we need to
-        // wait for some buffers to be consumed
-        tryAgain = (found == INVALID_BUFFER_SLOT);
-        if (tryAgain) {
-            CNW_LOGD("dequeueBuffer: Try again");
-            mDequeueCondition.wait(mMutex);
-            CNW_LOGD("dequeueBuffer: Now");
+        if (found == INVALID_BUFFER_SLOT) {
+            // This should not happen.
+            CNW_LOGE("dequeueBuffer: no available buffer slots");
+            return -EBUSY;
+        }
+
+        buf = found;
+
+        // buffer is now in DEQUEUED
+        mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+        const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer);
+        alloc = (gbuf == NULL);
+        if (alloc) {
+            // get local copies for graphics buffer allocations
+            defaultWidth = mDefaultWidth;
+            defaultHeight = mDefaultHeight;
+            pixelFormat = mPixelFormat;
+            usage = mUsage;
+        }
+    }
+    
+    // At this point, the buffer is now marked DEQUEUED, and no one else
+    // should touch it, except for freeAllBuffersLocked(); we handle that
+    // after trying to create the surface descriptor below.
+    //
+    // So we don't need mMutex locked, which would otherwise run the risk
+    // of a deadlock on calling AllocSurfaceDescriptorGralloc().    
+
+    SurfaceDescriptor desc;
+    ImageBridgeChild* ibc;
+    sp<GraphicBuffer> graphicBuffer;
+    if (alloc) {
+        status_t error;
+        ibc = ImageBridgeChild::GetSingleton();
+        CNW_LOGD("dequeueBuffer: about to alloc surface descriptor");
+        ibc->AllocSurfaceDescriptorGralloc(gfxIntSize(defaultWidth, defaultHeight),
+                                           pixelFormat,
+                                           usage,
+                                           &desc);
+        // We can only use a gralloc buffer here.  If we didn't get
+        // one back, something went wrong.
+        CNW_LOGD("dequeueBuffer: got surface descriptor");
+        if (SurfaceDescriptor::TSurfaceDescriptorGralloc != desc.type()) {
+            MOZ_ASSERT(SurfaceDescriptor::T__None == desc.type());
+            CNW_LOGE("dequeueBuffer: failed to alloc gralloc buffer");
+            return -ENOMEM;
+        }
+        graphicBuffer = GrallocBufferActor::GetFrom(desc.get_SurfaceDescriptorGralloc());
+        error = graphicBuffer->initCheck();
+        if (error != NO_ERROR) {
+            CNW_LOGE("dequeueBuffer: createGraphicBuffer failed with error %d", error);
+            return error;
         }
     }
 
-    if (found == INVALID_BUFFER_SLOT) {
-        // This should not happen.
-        CNW_LOGE("dequeueBuffer: no available buffer slots");
-        return -EBUSY;
+    bool tooOld = false;
+    {
+        Mutex::Autolock lock(mMutex);
+        if (generation == mGeneration) {
+            if (alloc) {
+                mSlots[buf].mGraphicBuffer = graphicBuffer;
+                mSlots[buf].mSurfaceDescriptor = desc;
+                mSlots[buf].mSurfaceDescriptor.get_SurfaceDescriptorGralloc().external() = true;
+            }
+            *buffer = mSlots[buf].mGraphicBuffer.get();
+
+            CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
+                    mSlots[buf].mGraphicBuffer->handle);
+        } else {
+            *buffer = nullptr;
+            tooOld = true;
+        }
     }
 
-    const int buf = found;
-
-    // buffer is now in DEQUEUED
-    mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
-
-    const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer);
-
-    if (gbuf == NULL) {
-        status_t error;
-        SurfaceDescriptor buffer;
-        ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
-        ibc->AllocSurfaceDescriptorGralloc(gfxIntSize(mDefaultWidth, mDefaultHeight),
-                                           mPixelFormat,
-                                           mUsage,
-                                           &buffer);
-        // We can only use a gralloc buffer here.  If we didn't get
-        // one back, something went wrong.
-        if (SurfaceDescriptor::TSurfaceDescriptorGralloc != buffer.type()) {
-            MOZ_ASSERT(SurfaceDescriptor::T__None == buffer.type());
-            CNW_LOGE("dequeueBuffer: failed to alloc gralloc buffer");
-            return -ENOMEM;
-        }
-        sp<GraphicBuffer> graphicBuffer =
-          GrallocBufferActor::GetFrom(buffer.get_SurfaceDescriptorGralloc());
-        error = graphicBuffer->initCheck();
-        if (error != NO_ERROR) {
-            CNW_LOGE("dequeueBuffer: createGraphicBuffer failed with error %d",error);
-            return error;
-        }
-        mSlots[buf].mGraphicBuffer = graphicBuffer;
-        mSlots[buf].mSurfaceDescriptor = buffer;
-        mSlots[buf].mSurfaceDescriptor.get_SurfaceDescriptorGralloc().external() = true;
+    if (alloc && tooOld) {
+        ibc->DeallocSurfaceDescriptorGralloc(desc);
     }
-    *buffer = mSlots[buf].mGraphicBuffer.get();
-
-    CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
-            mSlots[buf].mGraphicBuffer->handle );
 
     CNW_LOGD("dequeueBuffer: X");
     return NO_ERROR;
 }
 
 int GonkNativeWindow::getSlotFromBufferLocked(
         android_native_buffer_t* buffer) const
 {
@@ -397,17 +468,17 @@ GonkNativeWindow::returnBuffer(uint32_t 
   CNW_LOGD("GonkNativeWindow::returnBuffer: slot=%d (generation=%d)", aIndex, aGeneration);
   Mutex::Autolock lock(mMutex);
 
   if (aGeneration != mGeneration) {
     CNW_LOGD("returnBuffer: buffer is from generation %d (current is %d)",
       aGeneration, mGeneration);
     return false;
   }
-  if (aIndex < 0 || aIndex >= mBufferCount) {
+  if (aIndex >= mBufferCount) {
     CNW_LOGE("returnBuffer: slot index out of range [0, %d]: %d",
              mBufferCount, aIndex);
     return false;
   }
   if (mSlots[aIndex].mBufferState != BufferSlot::RENDERING) {
     CNW_LOGE("returnBuffer: slot %d is not owned by the compositor (state=%d)",
                   aIndex, mSlots[aIndex].mBufferState);
     return false;
--- a/dom/camera/GonkNativeWindow.h
+++ b/dom/camera/GonkNativeWindow.h
@@ -94,23 +94,24 @@ protected:
     virtual int setSwapInterval(int interval);
 
     virtual int setBufferCount(int bufferCount);
     virtual int setBuffersDimensions(int w, int h);
     virtual int setBuffersFormat(int format);
     virtual int setBuffersTimestamp(int64_t timestamp);
     virtual int setUsage(uint32_t reqUsage);
 
-    // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
-    // for the given slot.
-    void freeBufferLocked(int index);
+    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
+    // EGLImage) for all slots by removing them from the slots and appending
+    // then to the freeList.  This must be called with mMutex locked.
+    void freeAllBuffersLocked(nsTArray<SurfaceDescriptor>& freeList);
 
-    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
-    // EGLImage) for all slots.
-    void freeAllBuffersLocked();
+    // releaseBufferFreeListUnlocked releases the resources in the freeList;
+    // this must be called with mMutex unlocked.
+    void releaseBufferFreeListUnlocked(nsTArray<SurfaceDescriptor>& freeList);
 
 private:
     void init();
 
     int dispatchSetBufferCount(va_list args);
     int dispatchSetBuffersGeometry(va_list args);
     int dispatchSetBuffersDimensions(va_list args);
     int dispatchSetBuffersFormat(va_list args);
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -288,16 +288,21 @@ DeviceStorageRequestParent::PostBlobSucc
 
   nsString mime;
   CopyASCIItoUTF16(mMimeType, mime);
 
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(mFile->mPath, mime, mLength, mFile->mFile, mLastModificationDate);
 
   ContentParent* cp = static_cast<ContentParent*>(mParent->Manager());
   BlobParent* actor = cp->GetOrCreateActorForBlob(blob);
+  if (!actor) {
+    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);
   return NS_OK;
 }
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -3000,18 +3000,19 @@ AddHelper::PackArgumentsForParentProcess
     for (uint32_t index = 0; index < fileCount; index++) {
       const StructuredCloneFile& file = files[index];
 
       NS_ASSERTION(file.mFile, "This should never be null!");
       NS_ASSERTION(!file.mFileInfo, "This is not yet supported!");
 
       BlobChild* actor =
         contentChild->GetOrCreateActorForBlob(file.mFile);
-      NS_ASSERTION(actor, "This should never fail without aborting!");
-
+      if (!actor) {
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
       blobsChild.AppendElement(actor);
     }
   }
 
   if (mOverwrite) {
     PutParams putParams;
     putParams.commonParams() = commonParams;
     aParams = putParams;
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -251,19 +251,19 @@ IDBTransaction::SetTransactionListener(I
 }
 
 nsresult
 IDBTransaction::CommitOrRollback()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
-    NS_ASSERTION(mActorChild, "Must have an actor!");
-
-    mActorChild->SendAllRequestsFinished();
+    if (mActorChild) {
+      mActorChild->SendAllRequestsFinished();
+    }
 
     return NS_OK;
   }
 
   nsRefPtr<CommitHelper> helper =
     new CommitHelper(this, mListener, mCreatedObjectStores);
 
   TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -17,16 +17,17 @@
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/StorageChild.h"
 #include "mozilla/Hal.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/jsipc/PContextWrapperChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/Preferences.h"
@@ -499,16 +500,27 @@ ContentChild::AllocPCompositor(mozilla::
 
 PImageBridgeChild*
 ContentChild::AllocPImageBridge(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherProcess)
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
+bool
+ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
+{
+  ChildPrivileges privs = (aPrivs == PRIVILEGES_DEFAULT) ?
+                          GeckoChildProcessHost::DefaultChildPrivileges() :
+                          aPrivs;
+  // If this fails, we die.
+  SetCurrentProcessPrivileges(privs);
+  return true;
+}
+
 static CancelableTask* sFirstIdleTask;
 
 static void FirstIdle(void)
 {
     MOZ_ASSERT(sFirstIdleTask);
     sFirstIdleTask = nullptr;
     ContentChild::GetSingleton()->SendFirstIdle();
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -75,16 +75,18 @@ public:
 
     PCompositorChild*
     AllocPCompositor(mozilla::ipc::Transport* aTransport,
                      base::ProcessId aOtherProcess) MOZ_OVERRIDE;
     PImageBridgeChild*
     AllocPImageBridge(mozilla::ipc::Transport* aTransport,
                       base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
+    virtual bool RecvSetProcessPrivileges(const ChildPrivileges& aPrivs);
+
     virtual PBrowserChild* AllocPBrowser(const IPCTabContext &aContext,
                                          const uint32_t &chromeFlags);
     virtual bool DeallocPBrowser(PBrowserChild*);
 
     virtual PDeviceStorageRequestChild* AllocPDeviceStorageRequest(const DeviceStorageParams&);
     virtual bool DeallocPDeviceStorageRequest(PDeviceStorageRequestChild*);
 
     virtual PBlobChild* AllocPBlob(const BlobConstructorParams& aParams);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -217,17 +217,19 @@ ContentParent::PreallocateAppProcess()
         // We were called directly while a delayed task was scheduled.
         sPreallocateAppProcessTask->Cancel();
         sPreallocateAppProcessTask = nullptr;
     }
 
     sPreallocatedAppProcess =
         new ContentParent(MAGIC_PREALLOCATED_APP_MANIFEST_URL,
                           /*isBrowserElement=*/false,
-                          base::PRIVILEGES_DEFAULT);
+                          // Final privileges are set when we
+                          // transform into our app.
+                          base::PRIVILEGES_INHERIT);
     sPreallocatedAppProcess->Init();
 }
 
 /*static*/ void
 ContentParent::DelayedPreallocateAppProcess()
 {
     sPreallocateAppProcessTask = nullptr;
     if (!sPreallocatedAppProcess) {
@@ -435,30 +437,24 @@ ContentParent::CreateBrowserOrApp(const 
     if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) {
         NS_ERROR("Failed to get manifest URL");
         return nullptr;
     }
 
     nsRefPtr<ContentParent> p = gAppContentParents->Get(manifestURL);
     if (!p) {
         ChildPrivileges privs = PrivilegesForApp(ownApp);
-        if (privs != base::PRIVILEGES_DEFAULT) {
+        p = MaybeTakePreallocatedAppProcess();
+        if (p) {
+            p->TransformPreallocatedIntoApp(manifestURL, privs);            
+        } else {
+            NS_WARNING("Unable to use pre-allocated app process");
             p = new ContentParent(manifestURL, /* isBrowserElement = */ false,
                                   privs);
             p->Init();
-        } else {
-            p = MaybeTakePreallocatedAppProcess();
-            if (p) {
-                p->SetManifestFromPreallocated(manifestURL);
-            } else {
-                NS_WARNING("Unable to use pre-allocated app process");
-                p = new ContentParent(manifestURL, /* isBrowserElement = */ false,
-                                      base::PRIVILEGES_DEFAULT);
-                p->Init();
-            }
         }
         gAppContentParents->Put(manifestURL, p);
     }
 
     nsRefPtr<TabParent> tp = new TabParent(aContext);
     PBrowserParent* browser = p->SendPBrowserConstructor(
         tp.forget().get(), // DeallocPBrowserParent() releases this ref.
         aContext.AsIPCTabContext(),
@@ -531,22 +527,25 @@ ContentParent::Init()
     }
 #endif
 
     DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
     NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 }
 
 void
-ContentParent::SetManifestFromPreallocated(const nsAString& aAppManifestURL)
+ContentParent::TransformPreallocatedIntoApp(const nsAString& aAppManifestURL,
+                                            ChildPrivileges aPrivs)
 {
     MOZ_ASSERT(mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL);
     // Clients should think of mAppManifestURL as const ... we're
     // bending the rules here just for the preallocation hack.
     const_cast<nsString&>(mAppManifestURL) = aAppManifestURL;
+    // If this fails, the child process died.
+    unused << SendSetProcessPrivileges(aPrivs);
 }
 
 void
 ContentParent::ShutDownProcess()
 {
   if (!mIsDestroyed) {
     const InfallibleTArray<PIndexedDBParent*>& idbParents =
       ManagedPIndexedDBParent();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -173,17 +173,18 @@ private:
     ContentParent(const nsAString& aAppManifestURL, bool aIsForBrowser,
                   ChildOSPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT);
     virtual ~ContentParent();
 
     void Init();
 
     // Transform a pre-allocated app process into a "real" app
     // process, for the specified manifest URL.
-    void SetManifestFromPreallocated(const nsAString& aAppManifestURL);
+    void TransformPreallocatedIntoApp(const nsAString& aAppManifestURL,
+                                      ChildPrivileges aPrivs);
 
     /**
      * Mark this ContentParent as dead for the purposes of Get*().
      * This method is idempotent.
      */
     void MarkAsDead();
 
     /**
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -30,16 +30,17 @@ include "mozilla/net/NeckoMessageUtils.h
 include "nsGeoPositionIPCSerialiser.h";
 
 using GeoPosition;
 using PrefTuple;
 
 using ChromePackage;
 using ResourceMapping;
 using OverrideMapping;
+using base::ChildPrivileges;
 using IPC::Permission;
 using IPC::Principal;
 using mozilla::null_t;
 using mozilla::void_t;
 using mozilla::dom::AudioChannelType;
 using mozilla::dom::NativeThreadId;
 using mozilla::layout::ScrollingBehavior;
 using gfxIntSize;
@@ -238,16 +239,23 @@ both:
     // privileges by requesting a PBrowser corresponding to a highly-privileged
     // app; the child can only request privileges for an app which the child has
     // access to (in the form of a TabChild).
     async PBrowser(IPCTabContext context, uint32_t chromeFlags);
 
     async PBlob(BlobConstructorParams params);
 
 child:
+    /**
+     * Update OS process privileges to |privs|.  Can usually only be
+     * performed zero or one times.  The child will abnormally exit if
+     * the privilege update fails.
+     */
+    async SetProcessPrivileges(ChildPrivileges privs);
+
     PMemoryReportRequest();
 
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
     /**
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -120,17 +120,20 @@ public:
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     // allow calling even if inactive (!mStream) for easier cleanup
     // Caller holds strong reference to us, so no death grip required
     MutexAutoLock lock(mLock); // protect access to mRemoved
     if (mStream && !mRemoved) {
       MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
       mRemoved = true; // RemoveListener is async, avoid races
-      mStream->RemoveListener(this);
+      // If it's destroyed, don't call - listener will be removed and we'll be notified!
+      if (!mStream->IsDestroyed()) {
+        mStream->RemoveListener(this);
+      }
     }
   }
 
   // Proxy NotifyPull() to sources
   virtual void
   NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) MOZ_OVERRIDE
   {
     // Currently audio sources ignore NotifyPull, but they could
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -51,49 +51,65 @@ GlobalPCList.prototype = {
       }
       return _globalPCList.QueryInterface(iid);
     }
   },
 
   addPC: function(pc) {
     let winID = pc._winID;
     if (this._list[winID]) {
-      this._list[winID].push(pc);
+      this._list[winID].push(Components.utils.getWeakReference(pc));
     } else {
-      this._list[winID] = [pc];
+      this._list[winID] = [Components.utils.getWeakReference(pc)];
     }
+    this.removeNullRefs(winID);
+  },
+
+  removeNullRefs: function(winID) {
+    if (this._list === undefined || this._list[winID] === undefined) {
+      return;
+    }
+    this._list[winID] = this._list[winID].filter(
+      function (e,i,a) { return e.get() !== null; });
   },
 
   hasActivePeerConnection: function(winID) {
+    this.removeNullRefs(winID);
     return this._list[winID] ? true : false;
   },
 
   observe: function(subject, topic, data) {
     if (topic == "inner-window-destroyed") {
       let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
       if (this._list[winID]) {
-        this._list[winID].forEach(function(pc) {
-          pc._pc.close(false);
-          delete pc._observer;
-          pc._pc = null;
+        this._list[winID].forEach(function(pcref) {
+          let pc = pcref.get();
+          if (pc !== null) {
+            pc._pc.close(false);
+            delete pc._observer;
+            pc._pc = null;
+          }
         });
         delete this._list[winID];
       }
     } else if (topic == "profile-change-net-teardown" ||
                topic == "network:offline-about-to-go-offline") {
       // Delete all peerconnections on shutdown - synchronously (we need
       // them to be done deleting transports before we return)!
       // Also kill them if "Work Offline" is selected - more can be created
       // while offline, but attempts to connect them should fail.
       let array;
       while ((array = this._list.pop()) != undefined) {
-        array.forEach(function(pc) {
-          pc._pc.close(true);
-          delete pc._observer;
-          pc._pc = null;
+        array.forEach(function(pcref) {
+          let pc = pcref.get();
+          if (pc !== null) {
+            pc._pc.close(true);
+            delete pc._observer;
+            pc._pc = null;
+          }
         });
       };
       this._networkdown = true;
     }
     else if (topic == "network:offline-status-changed") {
       if (data == "offline") {
 	// this._list shold be empty here
         this._networkdown = true;
@@ -225,17 +241,19 @@ PeerConnection.prototype = {
                                     contractID: PC_CONTRACT,
                                     classDescription: "PeerConnection",
                                     interfaces: [
                                       Ci.nsIDOMRTCPeerConnection
                                     ],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIDOMRTCPeerConnection, Ci.nsIDOMGlobalObjectConstructor
+    Ci.nsIDOMRTCPeerConnection,
+    Ci.nsIDOMGlobalObjectConstructor,
+    Ci.nsISupportsWeakReference
   ]),
 
   // Constructor is an explicit function, because of nsIDOMGlobalObjectConstructor.
   constructor: function(win) {
     if (!Services.prefs.getBoolPref("media.peerconnection.enabled")) {
       throw new Error("PeerConnection not enabled (did you set the pref?)");
     }
     if (this._win) {
@@ -326,20 +344,16 @@ PeerConnection.prototype = {
     if (constraints.optional && !isArray(constraints.optional)) {
       return false;
     }
 
     return true;
   },
 
   createOffer: function(onSuccess, onError, constraints) {
-    if (this._onCreateOfferSuccess) {
-      throw new Error("createOffer already called");
-    }
-
     if (!constraints) {
       constraints = {};
     }
 
     if (!this._validateConstraints(constraints)) {
       throw new Error("createOffer passed invalid constraints");
     }
 
@@ -349,20 +363,16 @@ PeerConnection.prototype = {
     this._queueOrRun({
       func: this._pc.createOffer,
       args: [constraints],
       wait: true
     });
   },
 
   createAnswer: function(onSuccess, onError, constraints, provisional) {
-    if (this._onCreateAnswerSuccess) {
-      throw new Error("createAnswer already called");
-    }
-
     if (!this.remoteDescription) {
       throw new Error("setRemoteDescription not called");
     }
 
     if (this.remoteDescription.type != "offer") {
       throw new Error("No outstanding offer");
     }
 
@@ -541,17 +551,18 @@ PeerConnection.prototype = {
   }
 };
 
 // This is a seperate object because we don't want to expose it to DOM.
 function PeerConnectionObserver(dompc) {
   this._dompc = dompc;
 }
 PeerConnectionObserver.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.IPeerConnectionObserver]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.IPeerConnectionObserver,
+                                         Ci.nsISupportsWeakReference]),
 
   onCreateOfferSuccess: function(offer) {
     if (this._dompc._onCreateOfferSuccess) {
       try {
         this._dompc._onCreateOfferSuccess.onCallback({
           type: "offer", sdp: offer,
           __exposedProps__: { type: "rw", sdp: "rw" }
         });
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -34,14 +34,14 @@ function runTest(aCallback, desktopSuppo
     });
   }
 }
 
 /**
  * A callback function fired only under unexpected circumstances while
  * running the tests. Kills off the test as well gracefully.
  *
- * @param {String} obj the object fired back from the callback
+ * @param {object} aObj The object fired back from the callback
  */
-function unexpectedCallbackAndFinish(obj) {
-  ok(false, "Unexpected error callback with " + obj);
+function unexpectedCallbackAndFinish(aObj) {
+  ok(false, "Unexpected error callback with " + aObj);
   SimpleTest.finish();
 }
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -7,40 +7,36 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>mozGetUserMedia Basic Audio Test</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
-<audio id="testAudio"></audio>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Audio Test</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-
+  <audio id="testAudio"></audio>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/**
- * Run a test to verify that we can complete a start and stop media playback
- * cycle for an audio LocalMediaStream on an audio HTMLMediaElement.
- */
-runTest(function () {
-  try {
-    navigator.mozGetUserMedia({audio: true, fake: true}, function(stream) {
-      var testAudio = document.getElementById('testAudio');
-      var audioStreamPlayback = new MediaStreamPlayback(testAudio, stream);
-      audioStreamPlayback.playMedia(10000, function() {
-				stream.stop();
-				SimpleTest.finish();
-			}, unexpectedCallbackAndFinish);
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for an audio LocalMediaStream on an audio HTMLMediaElement.
+   */
+  runTest(function () {
+    var testAudio = document.getElementById('testAudio');
+
+    navigator.mozGetUserMedia({audio: true, fake: true}, function (aStream) {
+      var playback = new MediaStreamPlayback(testAudio, aStream);
+      playback.playMedia(10000, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
-  } catch (err) {
-    unexpectedCallbackAndFinish(err);
-  }
-}, true);
+  }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -7,40 +7,36 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>mozGetUserMedia Basic Video Test</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
-<video id="testVideo"></video>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video Test</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-
+  <video id="testVideo"></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/**
- * Run a test to verify that we can complete a start and stop media playback
- * cycle for an video LocalMediaStream on a video HTMLMediaElement.
- */
-runTest(function () {
-  try {
-    navigator.mozGetUserMedia({video: true, fake: true}, function(stream) {
-      var testVideo = document.getElementById('testVideo');
-      var videoStreamPlayback = new MediaStreamPlayback(testVideo, stream);
-      videoStreamPlayback.playMedia(10000, function() {
-				stream.stop();
-				SimpleTest.finish();
-			}, unexpectedCallbackAndFinish);
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for an video LocalMediaStream on a video HTMLMediaElement.
+   */
+  runTest(function () {
+    var testVideo = document.getElementById('testVideo');
+
+    navigator.mozGetUserMedia({video: true, fake: true}, function (aStream) {
+      var playback = new MediaStreamPlayback(testVideo, aStream);
+      playback.playMedia(10000, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
-  } catch (err) {
-    unexpectedCallbackAndFinish(err);
-  }
-}, true);
+  }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -7,42 +7,37 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>mozGetUserMedia Basic Video & Audio Test</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
-<video id="testVideoAudio"></video>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video & Audio Test</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-
+  <video id="testVideoAudio"></video>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/**
- * Run a test to verify that we can complete a start and stop media playback
- * cycle for a video and audio LocalMediaStream on a video HTMLMediaElement.
- */
-runTest(function () {
-  try {
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for a video and audio LocalMediaStream on a video HTMLMediaElement.
+   */
+  runTest(function () {
+    var testVideoAudio = document.getElementById('testVideoAudio');
+
     navigator.mozGetUserMedia({video: true, audio: true, fake: true},
-      function(stream) {
-      var testVideoAudio = document.getElementById('testVideoAudio');
-      var videoAudioStreamPlayback = new MediaStreamPlayback(testVideoAudio,
-        stream);
-      videoAudioStreamPlayback.playMedia(10000, function() {
-				stream.stop();
-				SimpleTest.finish();
-			}, unexpectedCallbackAndFinish);
+      function (aStream) {
+        var playback = new MediaStreamPlayback(testVideoAudio, aStream);
+        playback.playMedia(10000, function () {
+          aStream.stop();
+          SimpleTest.finish();
+        }, unexpectedCallbackAndFinish);
     }, unexpectedCallbackAndFinish);
-  } catch (err) {
-    unexpectedCallbackAndFinish(err);
-  }
-}, true);
+  }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/mms/src/ril/WspPduHelper.jsm
+++ b/dom/mms/src/ril/WspPduHelper.jsm
@@ -2138,16 +2138,38 @@ this.ApplicationIdValue = {
     }
 
     return entry.urn;
   },
 };
 
 this.PduHelper = {
   /**
+   * @param data
+   *        A UInt8Array of data for decode.
+   * @param charset
+   *        charset for decode
+   *
+   * @return Decoded string.
+   */
+  decodeStringContent: function decodeStringContent(data, charset) {
+      let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                 .createInstance(Ci.nsIScriptableUnicodeConverter);
+
+      let entry = WSP_WELL_KNOWN_CHARSETS[charset];
+      // Set converter to default one if (entry && entry.converter) is null.
+      // @see OMA-TS-MMS-CONF-V1_3-20050526-D 7.1.9
+      conv.charset = (entry && entry.converter) || "UTF-8";
+      try {
+        return conv.convertFromByteArray(data, data.length);
+      } catch (e) {
+      }
+      return null;
+  },
+  /**
    * Parse multiple header fields with end mark.
    *
    * @param data
    *        A wrapped object containing raw PDU data.
    * @param end
    *        An ending offset indicating the end of headers.
    * @param headers [optional]
    *        An optional object to store parsed header fields. Created
@@ -2235,18 +2257,24 @@ this.PduHelper = {
         headers["content-type"] = contentType;
         headers["content-length"] = contentLen;
 
         headers = this.parseHeaders(data, headersEnd, headers);
 
         let octetArray = Octet.decodeMultiple(data, contentEnd);
         let content = null;
         if (octetArray) {
-          content = new Blob([octetArray],
-            {"type" : headers["content-type"].media});
+          if (headers["content-type"].media.indexOf("text/") === 0) {
+            content = this.decodeStringContent(octetArray,
+              headers["content-type"].params.charset["charset"]);
+          }
+          if (!content) {
+            content = new Blob([octetArray],
+              {"type" : headers["content-type"].media});
+          }
         }
 
         parts[i] = {
           index: i,
           headers: headers,
           content: content,
         };
       } catch (e) {
@@ -2671,19 +2699,43 @@ this.WSP_WELL_KNOWN_CHARSETS = (function
       name: name,
       number: number,
       converter: converter,
     };
 
     charsets[name] = charsets[number] = entry;
   }
 
-  add("ansi_x3.4-1968",     3, null);
-  add("iso_8859-1:1987",    4, "ISO-8859-1");
+  add("us-ascii",           3, null);
+  add("iso-8859-1",         4, "ISO-8859-1");
+  add("iso-8859-2",         5, "ISO-8859-2");
+  add("iso-8859-3",         6, "ISO-8859-3");
+  add("iso-8859-4",         7, "ISO-8859-4");
+  add("iso-8859-5",         8, "ISO-8859-5");
+  add("iso-8859-6",         9, "ISO-8859-6");
+  add("iso-8859-7",        10, "ISO-8859-7");
+  add("iso-8859-8",        11, "ISO-8859-8");
+  add("iso-8859-9",        12, "ISO-8859-9");
+  add("iso-8859-10",       13, "ISO-8859-10");
+  add("shift_jis",         17, "Shift_JIS");
+  add("euc-jp",            18, "EUC-JP");
+  add("iso-2022-kr",       37, "ISO-2022-KR");
+  add("euc-kr",            38, "EUC-KR");
+  add("iso-2022-jp",       39, "ISO-2022-JP");
+  add("iso-2022-jp-2",     40, "iso-2022-jp-2");
+  add("iso-8859-6-e",      81, "ISO-8859-6-E");
+  add("iso-8859-6-i",      82, "ISO-8859-6-I");
+  add("iso-8859-8-e",      84, "ISO-8859-8-E");
+  add("iso-8859-8-i",      85, "ISO-8859-8-I");
   add("utf-8",            106, "UTF-8");
+  add("iso-10646-ucs-2", 1000, "iso-10646-ucs-2");
+  add("utf-16",          1015, "UTF-16");
+  add("gb2312",          2025, "GB2312");
+  add("big5",            2026, "Big5");
+  add("koi8-r",          2084, "KOI8-R");
   add("windows-1252",    2252, "windows-1252");
 
   return charsets;
 })();
 
 // OMNA PUSH Application ID
 // @see http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
 this.OMNA_PUSH_APPLICATION_IDS = (function () {
--- a/dom/mms/tests/test_mms_pdu_helper.js
+++ b/dom/mms/tests/test_mms_pdu_helper.js
@@ -354,16 +354,29 @@ add_test(function test_EncodedStringValu
                .createInstance(Ci.nsIScriptableUnicodeConverter);
     conv.charset = entry.converter;
 
     let raw = conv.convertToByteArray(str).concat([0]);
     wsp_decode_test(MMS.EncodedStringValue,
                     [raw.length + 2, 0x80 | entry.number, 127].concat(raw), str);
   }
 
+  let (entry = MMS.WSP.WSP_WELL_KNOWN_CHARSETS["utf-16"]) {
+    // "Mozilla" in full width.
+    let str = "\u004d\u006F\u007A\u0069\u006C\u006C\u0061";
+
+    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+               .createInstance(Ci.nsIScriptableUnicodeConverter);
+    conv.charset = entry.converter;
+
+    let raw = conv.convertToByteArray(str).concat([0]);
+    wsp_decode_test(MMS.EncodedStringValue,
+                    [raw.length + 3, 2, 3, 247].concat(raw), str);
+  }
+
   run_next_test();
 });
 
 //// EncodedStringValue.encode ////
 
 add_test(function test_EncodedStringValue_encode() {
   // Test for normal TextString
   wsp_encode_test(MMS.EncodedStringValue, "Hello", strToCharCodeArray("Hello"));
--- a/dom/mms/tests/test_wsp_pdu_helper.js
+++ b/dom/mms/tests/test_wsp_pdu_helper.js
@@ -1013,29 +1013,83 @@ add_test(function test_AcceptCharsetValu
 
 //// WellKnownCharset.decode ////
 
 add_test(function test_WellKnownCharset_decode() {
   wsp_decode_test(WSP.WellKnownCharset, [0xFF], null, "NotWellKnownEncodingError");
   // Test for Any-Charset
   wsp_decode_test(WSP.WellKnownCharset, [128], {charset: "*"});
   // Test for number-typed return value from IntegerValue
-  wsp_decode_test(WSP.WellKnownCharset, [1, 3], {charset: "ansi_x3.4-1968"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 3], {charset: "us-ascii"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 4], {charset: "iso-8859-1"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 5], {charset: "iso-8859-2"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 6], {charset: "iso-8859-3"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 7], {charset: "iso-8859-4"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 8], {charset: "iso-8859-5"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 9], {charset: "iso-8859-6"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 10], {charset: "iso-8859-7"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 11], {charset: "iso-8859-8"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 12], {charset: "iso-8859-9"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 13], {charset: "iso-8859-10"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 17], {charset: "shift_jis"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 18], {charset: "euc-jp"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 37], {charset: "iso-2022-kr"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 38], {charset: "euc-kr"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 39], {charset: "iso-2022-jp"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 40], {charset: "iso-2022-jp-2"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 81], {charset: "iso-8859-6-e"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 82], {charset: "iso-8859-6-i"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 84], {charset: "iso-8859-8-e"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 85], {charset: "iso-8859-8-i"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 1000], {charset: "iso-10646-ucs-2"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 1015], {charset: "utf-16"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 2025], {charset: "gb2312"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 2026], {charset: "big5"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 2084], {charset: "koi8-r"});
+  wsp_decode_test(WSP.WellKnownCharset, [1, 2252], {charset: "windows-1252"});
+  wsp_decode_test(WSP.WellKnownCharset, [2, 3, 247], {charset: "utf-16"});
   // Test for array-typed return value from IntegerValue
   wsp_decode_test(WSP.WellKnownCharset, [7, 0, 0, 0, 0, 0, 0, 0, 3], null, "CodeError");
 
   run_next_test();
 });
 
 //// WellKnownCharset.encode ////
 
 add_test(function test_WellKnownCharset_encode() {
   // Test for Any-charset
   wsp_encode_test(WSP.WellKnownCharset, {charset: "*"}, [0x80]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "us-ascii"}, [128 + 3]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-1"}, [128 + 4]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-2"}, [128 + 5]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-3"}, [128 + 6]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-4"}, [128 + 7]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-5"}, [128 + 8]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-6"}, [128 + 9]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-7"}, [128 + 10]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-8"}, [128 + 11]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-9"}, [128 + 12]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-10"}, [128 + 13]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "shift_jis"}, [128 + 17]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "euc-jp"}, [128 + 18]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-2022-kr"}, [128 + 37]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "euc-kr"}, [128 + 38]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-2022-jp"}, [128 + 39]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-2022-jp-2"}, [128 + 40]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-6-e"}, [128 + 81]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-6-i"}, [128 + 82]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-8-e"}, [128 + 84]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-8859-8-i"}, [128 + 85]);
   wsp_encode_test(WSP.WellKnownCharset, {charset: "UTF-8"}, [128 + 106]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "iso-10646-ucs-2"}, [2, 0x3, 0xe8]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "UTF-16"}, [2, 0x3, 0xf7]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "gb2312"}, [2, 0x7, 0xe9]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "big5"}, [2, 0x7, 0xea]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "koi8-r"}, [2, 0x8, 0x24]);
+  wsp_encode_test(WSP.WellKnownCharset, {charset: "windows-1252"}, [2, 0x8, 0xcc]);
 
   run_next_test();
 });
 
 //
 // Test target: ContentTypeValue
 //
 
@@ -1217,8 +1271,46 @@ add_test(function test_PduHelper_parseHe
       return WSP.PduHelper.parseHeaders(data, data.array.length);
     }, [0x80 | 0x05, 2, 0x23, 0x28, 0x80 | 0x2F, 0x80 | 0x04],
     {"age": 9000, "x-wap-application-id": "x-wap-application:mms.ua"}
   );
 
   run_next_test();
 });
 
+//// PduHelper.decodeStringContent ////
+
+add_test(function StringContent_decode() {
+  //Test for utf-8
+  let (entry = WSP.WSP_WELL_KNOWN_CHARSETS["utf-8"]) {
+    // "Mozilla" in full width.
+    let str = "\uff2d\uff4f\uff5a\uff49\uff4c\uff4c\uff41";
+
+    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+               .createInstance(Ci.nsIScriptableUnicodeConverter);
+    conv.charset = entry.converter;
+
+    let raw = conv.convertToByteArray(str);
+    let data = {array: raw, offset: 0};
+    let octetArray = WSP.Octet.decodeMultiple(data, data.array.length);
+    wsp_decode_test_ex(function (data) {
+        return WSP.PduHelper.decodeStringContent(data.array, "utf-8");
+      }, octetArray, str);
+  }
+
+  let (entry = WSP.WSP_WELL_KNOWN_CHARSETS["utf-16"]) {
+    // "Mozilla" in full width.
+    let str = "\u004d\u006F\u007A\u0069\u006C\u006C\u0061";
+
+    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+               .createInstance(Ci.nsIScriptableUnicodeConverter);
+    conv.charset = entry.converter;
+
+    let raw = conv.convertToByteArray(str);
+    let data = {array: raw, offset: 0};
+    let octetArray = WSP.Octet.decodeMultiple(data, data.array.length);
+    wsp_decode_test_ex(function (data) {
+        return WSP.PduHelper.decodeStringContent(data.array, "utf-16");
+      }, raw, str);
+  }
+
+  run_next_test();
+});
--- a/dom/permission/PermissionSettings.js
+++ b/dom/permission/PermissionSettings.js
@@ -77,23 +77,27 @@ PermissionSettings.prototype = {
 
     return isExplicitInPermissionsTable(aPermName, principal.appStatus);
   },
 
   set: function set(aPermName, aPermValue, aManifestURL, aOrigin,
                     aBrowserFlag) {
     debug("Set called with: " + aPermName + ", " + aManifestURL + ", " +
           aOrigin + ",  " + aPermValue + ", " + aBrowserFlag); 
+    let currentPermValue = this.get(aPermName, aManifestURL, aOrigin, 
+                                    aBrowserFlag);
     let action;
     // Check for invalid calls so that we throw an exception rather than get
     // killed by parent process
-    if (aPermValue === "unknown" ||
+    if (currentPermValue === "unknown" || 
+        aPermValue === "unknown" ||
         !this.isExplicit(aPermName, aManifestURL, aOrigin, aBrowserFlag)) {
       let errorMsg = "PermissionSettings.js: '" + aPermName + "'" +
-                     " is an implicit permission for '" + aManifestURL+"'";
+                     " is an implicit permission for '" + aManifestURL +
+                     "' or the permission isn't set";
       Cu.reportError(errorMsg);
       throw new Components.Exception(errorMsg);
     }
 
     cpm.sendSyncMessage("PermissionSettings:AddPermission", {
       type: aPermName,
       origin: aOrigin,
       manifestURL: aManifestURL,
--- a/dom/permission/PermissionSettings.jsm
+++ b/dom/permission/PermissionSettings.jsm
@@ -154,17 +154,17 @@ this.PermissionSettingsModule = {
           if (!success) { 
             // Just kill the calling process
             mm.assertPermission("permissions-modify-implicit");
             errorMsg = " had an implicit permission change. Child process killed.";
           }
         }
 
         if (!success) {
-          Cu.reportError("PermissionSettings message " + msg.name + errorMsg);
+          Cu.reportError("PermissionSettings message " + msg.type + errorMsg);
           return null;
         }
         break;
     }
   }
 }
 
 PermissionSettingsModule.init();
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2577,20 +2577,22 @@ nsPluginHost::WritePluginInfo()
   PR_Close(fd);
   nsCOMPtr<nsIFile> parent;
   rv = pluginReg->GetParent(getter_AddRefs(parent));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
   return rv;
 }
 
-#define PLUGIN_REG_MIMETYPES_ARRAY_SIZE 12
 nsresult
 nsPluginHost::ReadPluginInfo()
 {
+  const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
+  const long PLUGIN_REG_MAX_MIMETYPES = 1000;
+
   nsresult rv;
 
   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
   if (NS_FAILED(rv))
     return rv;
 
   directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                         getter_AddRefs(mPluginRegFile));
@@ -2788,17 +2790,21 @@ nsPluginHost::ReadPluginInfo()
       version = &description[16];
     }
 #endif
 
     const char *name = reader.LinePtr();
     if (!reader.NextLine())
       return rv;
 
-    int mimetypecount = atoi(reader.LinePtr());
+    long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
+    if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
+        mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
+      return NS_ERROR_FAILURE;
+    }
 
     char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
     char **mimetypes;
     char **mimedescriptions;
     char **extensions;
     char **heapalloced = 0;
     if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
       heapalloced = new char *[mimetypecount * 3];
--- a/dom/plugins/ipc/MiniShmParent.cpp
+++ b/dom/plugins/ipc/MiniShmParent.cpp
@@ -121,16 +121,18 @@ MiniShmParent::Init(MiniShmObserver* aOb
   ScopedMappedFileView view(::MapViewOfFile(mapping,
                                             FILE_MAP_WRITE,
                                             0, 0, 0));
   if (!view.IsValid()) {
     return NS_ERROR_FAILURE;
   }
   nsresult rv = SetView(view, aSectionSize, false);
   NS_ENSURE_SUCCESS(rv, rv);
+  rv = SetGuard(childGuard, aTimeout);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   MiniShmInit* initStruct = nullptr;
   rv = GetWritePtrInternal(initStruct);
   NS_ENSURE_SUCCESS(rv, rv);
   initStruct->mParentEvent = parentEvent;
   initStruct->mParentGuard = parentGuard;
   initStruct->mChildEvent = childEvent;
   initStruct->mChildGuard = childGuard;
@@ -169,22 +171,19 @@ MiniShmParent::GetCookie(std::wstring& c
   }
   cookie = oss.str();
   return NS_OK;
 }
 
 nsresult
 MiniShmParent::Send()
 {
-  if (!mChildEvent || !mChildGuard) {
+  if (!mChildEvent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
-  if (::WaitForSingleObject(mChildGuard, mTimeout) != WAIT_OBJECT_0) {
-    return NS_ERROR_FAILURE;
-  }
   if (!::SetEvent(mChildEvent)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 bool
 MiniShmParent::IsConnected() const
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -317,16 +317,22 @@ PluginHangUIParent::GetHangUIOwnerWindow
 void
 PluginHangUIParent::OnMiniShmEvent(MiniShmBase *aMiniShmObj)
 {
   const PluginHangUIResponse* response = nullptr;
   nsresult rv = aMiniShmObj->GetReadPtr(response);
   NS_ASSERTION(NS_SUCCEEDED(rv),
                "Couldn't obtain read pointer OnMiniShmEvent");
   if (NS_SUCCEEDED(rv)) {
+    // The child process has returned a response so we shouldn't worry about 
+    // its state anymore.
+    if (::UnregisterWaitEx(mRegWait, NULL)) {
+      mRegWait = NULL;
+    }
+
     RecvUserResponse(response->mResponseBits);
   }
 }
 
 void
 PluginHangUIParent::OnMiniShmConnect(MiniShmBase* aMiniShmObj)
 {
   PluginHangUICommand* cmd = nullptr;
--- a/dom/plugins/ipc/hangui/MiniShmBase.h
+++ b/dom/plugins/ipc/hangui/MiniShmBase.h
@@ -116,27 +116,31 @@ public:
    * other types used by the protocol being implemented.
    *
    * @param aPtr Pointer to receive the shared memory address.
    *             This value is set if and only if the function 
    *             succeeded.
    * @return NS_OK if and only if aPtr was successfully obtained.
    *         NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm.
    *         NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
+   *         NS_ERROR_NOT_AVAILABLE if the memory is not safe to write.
    */
   template<typename T> nsresult
   GetWritePtr(T*& aPtr)
   {
-    if (!mWriteHeader) {
+    if (!mWriteHeader || !mGuard) {
       return NS_ERROR_NOT_INITIALIZED;
     }
     if (sizeof(T) > mPayloadMaxLen ||
         T::identifier <= RESERVED_CODE_LAST) {
       return NS_ERROR_ILLEGAL_VALUE;
     }
+    if (::WaitForSingleObject(mGuard, mTimeout) != WAIT_OBJECT_0) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
     mWriteHeader->mId = T::identifier;
     mWriteHeader->mPayloadLen = sizeof(T);
     aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
     return NS_OK;
   }
 
   /**
    * Obtains a readable pointer into shared memory of type T.
@@ -214,17 +218,19 @@ protected:
     };
     bool      mSucceeded;
   };
 
   MiniShmBase()
     : mObserver(nullptr),
       mWriteHeader(nullptr),
       mReadHeader(nullptr),
-      mPayloadMaxLen(0)
+      mPayloadMaxLen(0),
+      mGuard(NULL),
+      mTimeout(INFINITE)
   {
   }
   virtual ~MiniShmBase()
   { }
 
   virtual void
   OnEvent()
   {
@@ -256,16 +262,27 @@ protected:
       mWriteHeader = static_cast<MiniShmHeader*>(aView);
       mReadHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
                                                      + aSize / 2U);
     }
     mPayloadMaxLen = aSize / 2U - sizeof(MiniShmHeader);
     return NS_OK;
   }
 
+  nsresult
+  SetGuard(HANDLE aGuard, DWORD aTimeout)
+  {
+    if (!aGuard || !aTimeout) {
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+    mGuard = aGuard;
+    mTimeout = aTimeout;
+    return NS_OK;
+  }
+
   inline void
   SetObserver(MiniShmObserver *aObserver) { mObserver = aObserver; }
 
   /**
    * Obtains a writable pointer into shared memory of type T. This version 
    * differs from GetWritePtr in that it allows typename T to be one of 
    * the private data structures declared in MiniShmBase.
    *
@@ -299,16 +316,18 @@ protected:
     object->OnEvent();
   }
 
 private:
   MiniShmObserver*  mObserver;
   MiniShmHeader*    mWriteHeader;
   MiniShmHeader*    mReadHeader;
   unsigned int      mPayloadMaxLen;
+  HANDLE            mGuard;
+  DWORD             mTimeout;
 
   DISALLOW_COPY_AND_ASSIGN(MiniShmBase);
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // mozilla_plugins_MiniShmBase_h
--- a/dom/plugins/ipc/hangui/MiniShmChild.cpp
+++ b/dom/plugins/ipc/hangui/MiniShmChild.cpp
@@ -23,22 +23,24 @@ MiniShmChild::MiniShmChild()
     mTimeout(INFINITE)
 {}
 
 MiniShmChild::~MiniShmChild()
 {
   if (mRegWait) {
     ::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
   }
+  if (mParentGuard) {
+    // Try to avoid shutting down while the parent's event handler is running.
+    ::WaitForSingleObject(mParentGuard, mTimeout);
+    ::CloseHandle(mParentGuard);
+  }
   if (mParentEvent) {
     ::CloseHandle(mParentEvent);
   }
-  if (mParentGuard) {
-    ::CloseHandle(mParentGuard);
-  }
   if (mChildEvent) {
     ::CloseHandle(mChildEvent);
   }
   if (mChildGuard) {
     ::CloseHandle(mChildGuard);
   }
   if (mView) {
     ::UnmapViewOfFile(mView);
@@ -90,16 +92,20 @@ MiniShmChild::Init(MiniShmObserver* aObs
   rv = GetReadPtr(initStruct);
   if (NS_FAILED(rv)) {
     return rv;
   }
   if (!initStruct->mParentEvent || !initStruct->mParentGuard ||
       !initStruct->mChildEvent || !initStruct->mChildGuard) {
     return NS_ERROR_FAILURE;
   }
+  rv = SetGuard(initStruct->mParentGuard, aTimeout);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
   if (!::RegisterWaitForSingleObject(&mRegWait,
                                      initStruct->mChildEvent,
                                      &SOnEvent,
                                      this,
                                      INFINITE,
                                      WT_EXECUTEDEFAULT)) {
     return NS_ERROR_FAILURE;
   }
@@ -141,22 +147,19 @@ MiniShmChild::Init(MiniShmObserver* aObs
 
   OnConnect();
   return NS_OK;
 }
 
 nsresult
 MiniShmChild::Send()
 {
-  if (!mParentEvent || !mParentGuard) {
+  if (!mParentEvent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
-  if (::WaitForSingleObject(mParentGuard, mTimeout) != WAIT_OBJECT_0) {
-    return NS_ERROR_FAILURE;
-  }
   if (!::SetEvent(mParentEvent)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 void
 MiniShmChild::OnEvent()
--- a/dom/plugins/test/unit/head_plugins.js
+++ b/dom/plugins/test/unit/head_plugins.js
@@ -27,8 +27,56 @@ function get_test_plugin() {
     plugin.append("nptest.dll");
     if (plugin.exists()) {
       plugin.normalize();
       return plugin;
     }
   }
   return null;
 }
+
+// Finds the test nsIPluginTag
+function get_test_plugintag() {
+  const Cc = Components.classes;
+  const Ci = Components.interfaces;
+
+  var host = Cc["@mozilla.org/plugin/host;1"].
+             getService(Ci.nsIPluginHost);
+  var tags = host.getPluginTags();
+  for (var i = 0; i < tags.length; i++) {
+    if (tags[i].name == "Test Plug-in")
+      return tags[i];
+  }
+  return null;
+}
+
+// Creates a fake ProfDS directory key, copied from do_get_profile
+function do_get_profile_startup() {
+  let env = Components.classes["@mozilla.org/process/environment;1"]
+                      .getService(Components.interfaces.nsIEnvironment);
+  // the python harness sets this in the environment for us
+  let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
+  let file = Components.classes["@mozilla.org/file/local;1"]
+                       .createInstance(Components.interfaces.nsILocalFile);
+  file.initWithPath(profd);
+
+  let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
+                         .getService(Components.interfaces.nsIProperties);
+  let provider = {
+    getFile: function(prop, persistent) {
+      persistent.value = true;
+      if (prop == "ProfDS") {
+        return file.clone();
+      }
+      throw Components.results.NS_ERROR_FAILURE;
+    },
+    QueryInterface: function(iid) {
+      if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
+          iid.equals(Components.interfaces.nsISupports)) {
+        return this;
+      }
+      throw Components.results.NS_ERROR_NO_INTERFACE;
+    }
+  };
+  dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService)
+        .registerProvider(provider);
+  return file.clone();
+}
--- a/dom/plugins/test/unit/test_bug455213.js
+++ b/dom/plugins/test/unit/test_bug455213.js
@@ -43,28 +43,16 @@ function write_registry(version, info) {
            createInstance(Ci.nsIConverterOutputStream);
   os.init(foStream, charset, 0, 0x0000);
 
   os.writeString(header);
   os.writeString(info);
   os.close();
 }
 
-// Finds the test nsIPluginTag
-function get_test_plugintag() {
-  var host = Cc["@mozilla.org/plugin/host;1"].
-             getService(Ci.nsIPluginHost);
-  var tags = host.getPluginTags();
-  for (var i = 0; i < tags.length; i++) {
-    if (tags[i].name == "Test Plug-in")
-      return tags[i];
-  }
-  return null;
-}
-
 function run_test() {
   var file = get_test_plugin();
   if (!file)
     do_throw("Plugin library not found");
 
   // Write out a 0.9 version registry that marks the test plugin as disabled
   var registry = "";
   if (isMac) {
--- a/dom/plugins/test/unit/test_bug471245.js
+++ b/dom/plugins/test/unit/test_bug471245.js
@@ -1,61 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
-// Creates a fake ProfDS directory key, copied from do_get_profile
-function do_get_profile_startup() {
-  let env = Components.classes["@mozilla.org/process/environment;1"]
-                      .getService(Components.interfaces.nsIEnvironment);
-  // the python harness sets this in the environment for us
-  let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
-  let file = Components.classes["@mozilla.org/file/local;1"]
-                       .createInstance(Components.interfaces.nsILocalFile);
-  file.initWithPath(profd);
-
-  let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
-                         .getService(Components.interfaces.nsIProperties);
-  let provider = {
-    getFile: function(prop, persistent) {
-      persistent.value = true;
-      if (prop == "ProfDS") {
-        return file.clone();
-      }
-      throw Components.results.NS_ERROR_FAILURE;
-    },
-    QueryInterface: function(iid) {
-      if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
-          iid.equals(Components.interfaces.nsISupports)) {
-        return this;
-      }
-      throw Components.results.NS_ERROR_NO_INTERFACE;
-    }
-  };
-  dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService)
-        .registerProvider(provider);
-  return file.clone();
-}
-
-// Finds the test nsIPluginTag
-function get_test_plugintag() {
-  var host = Cc["@mozilla.org/plugin/host;1"].
-             getService(Ci.nsIPluginHost);
-  var tags = host.getPluginTags();
-  for (var i = 0; i < tags.length; i++) {
-    if (tags[i].name == "Test Plug-in")
-      return tags[i];
-  }
-  return null;
-}
-
 function run_test() {
   do_get_profile_startup();
 
   var plugin = get_test_plugintag();
   do_check_true(plugin == null);
 
   // Initialises a profile folder
   do_get_profile();
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/unit/test_bug813245.js
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+// v0.9+ registry field meanings are different on Mac OS X
+const CWD = do_get_cwd();
+function checkOS(os) {
+  const nsILocalFile_ = "nsILocalFile" + os;
+  return nsILocalFile_ in Components.interfaces &&
+         CWD instanceof Components.interfaces[nsILocalFile_];
+}
+const isMac = checkOS("Mac");
+
+// Plugin registry uses different field delimeters on different platforms
+var DELIM = ":";
+if ("@mozilla.org/windows-registry-key;1" in Components.classes)
+  DELIM = "|";
+
+var gProfD = do_get_profile_startup();
+var gDirSvc = Cc["@mozilla.org/file/directory_service;1"].
+             getService(Ci.nsIProperties);
+
+// Writes out some plugin registry to the profile
+function write_registry(version, info) {
+  let runtime = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime);
+
+  var header = "Generated File. Do not edit.\n\n";
+  header += "[HEADER]\n";
+  header += "Version" + DELIM + version + DELIM + "$\n";
+  header += "Arch" + DELIM + runtime.XPCOMABI + DELIM + "$\n";
+  header += "\n";
+  header += "[PLUGINS]\n";
+
+  var registry = gProfD.clone();
+  registry.append("pluginreg.dat");
+  var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                           .createInstance(Components.interfaces.nsIFileOutputStream);
+  // write, create, truncate
+  foStream.init(registry, 0x02 | 0x08 | 0x20, 0666, 0);
+
+  var charset = "UTF-8"; // Can be any character encoding name that Mozilla supports
+  var os = Cc["@mozilla.org/intl/converter-output-stream;1"].
+           createInstance(Ci.nsIConverterOutputStream);
+  os.init(foStream, charset, 0, 0x0000);
+
+  os.writeString(header);
+  os.writeString(info);
+  os.close();
+}
+
+function run_test() {
+  var plugin = get_test_plugintag();
+  do_check_true(plugin == null);
+
+  var file = get_test_plugin();
+  if (!file) {
+    do_throw("Plugin library not found");
+  }
+
+  // write plugin registry data
+  let registry = "";
+
+  if (isMac) {
+    registry += file.leafName + DELIM + "$\n";
+    registry += file.path + DELIM + "$\n";
+  } else {
+    registry += file.path + DELIM + "$\n";
+    registry += DELIM + "$\n";
+  }
+  registry += "0.0.0.0" + DELIM + "$\n";
+  registry += "16725225600" + DELIM + "0" + DELIM + "5" + DELIM + "$\n";
+  registry += "Plug-in for testing purposes." + DELIM + "$\n";
+  registry += "Test Plug-in" + DELIM + "$\n";
+  registry += "999999999999999999999999999999999999999999999999|0|5|$\n";
+  registry += "0" + DELIM + "application/x-test" + DELIM + "Test mimetype" +
+              DELIM + "tst" + DELIM + "$\n";
+
+  registry += "\n";
+  registry += "[INVALID]\n";
+  registry += "\n";
+  write_registry("0.15", registry);
+
+  // Initialise profile folder
+  do_get_profile();
+
+  var plugin = get_test_plugintag();
+  if (!plugin)
+    do_throw("Plugin tag not found");
+
+  // The plugin registry should have been rejected.
+  // If not, the test plugin version would be 0.0.0.0
+  do_check_eq(plugin.version, "1.0.0.0");
+}
--- a/dom/plugins/test/unit/xpcshell.ini
+++ b/dom/plugins/test/unit/xpcshell.ini
@@ -3,8 +3,11 @@ head = head_plugins.js
 tail = 
 
 [test_bug455213.js]
 # Bug 676953: test fails consistently on Android
 fail-if = os == "android"
 [test_bug471245.js]
 # Bug 676953: test fails consistently on Android
 fail-if = os == "android"
+[test_bug813245.js]
+# Bug 676953: test fails consistently on Android
+fail-if = os == "android"
--- a/dom/sms/src/ril/SmsDatabaseService.js
+++ b/dom/sms/src/ril/SmsDatabaseService.js
@@ -10,17 +10,17 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
 
 const RIL_SMSDATABASESERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsdatabaseservice;1";
 const RIL_SMSDATABASESERVICE_CID = Components.ID("{a1fa610c-eb6c-4ac2-878f-b005d5e89249}");
 
 const DEBUG = false;
 const DB_NAME = "sms";
-const DB_VERSION = 6;
+const DB_VERSION = 7;
 const STORE_NAME = "sms";
 const MOST_RECENT_STORE_NAME = "most-recent";
 
 const DELIVERY_SENDING = "sending";
 const DELIVERY_RECEIVED = "received";
 
 const DELIVERY_STATUS_NOT_APPLICABLE = "not-applicable";
 const DELIVERY_STATUS_SUCCESS = "success";
@@ -190,16 +190,20 @@ SmsDatabaseService.prototype = {
           case 4:
             if (DEBUG) debug("Upgrade to version 5. Populate quick threads view.")
             self.upgradeSchema4(event.target.transaction);
             break;
           case 5:
             if (DEBUG) debug("Upgrade to version 6. Use PhonenumberJS.")
             self.upgradeSchema5(event.target.transaction);
             break;
+          case 6:
+            if (DEBUG) debug("Upgrade to version 7. Use multiple entry indexes.")
+            self.upgradeSchema6(event.target.transaction);
+            break;
           default:
             event.target.transaction.abort();
             callback("Old database version: " + event.oldVersion, null);
             break;
         }
         currentVersion++;
       }
     }
@@ -266,19 +270,16 @@ SmsDatabaseService.prototype = {
    * Create the initial database schema.
    *
    * TODO need to worry about number normalization somewhere...
    * TODO full text search on body???
    */
   createSchema: function createSchema(db) {
     // This objectStore holds the main SMS data.
     let objectStore = db.createObjectStore(STORE_NAME, { keyPath: "id" });
-    objectStore.createIndex("delivery", "delivery", { unique: false });
-    objectStore.createIndex("sender", "sender", { unique: false });
-    objectStore.createIndex("receiver", "receiver", { unique: false });
     objectStore.createIndex("timestamp", "timestamp", { unique: false });
     if (DEBUG) debug("Created object stores and indexes");
   },
 
   /**
    * Upgrade to the corresponding database schema version.
    */
   upgradeSchema: function upgradeSchema(objectStore) {
@@ -362,99 +363,293 @@ SmsDatabaseService.prototype = {
       cursor.continue();
     }
   },
 
   upgradeSchema5: function upgradeSchema5(transaction) {
     // Don't perform any upgrade. See Bug 819560.
   },
 
-  /**
-   * Helper function to make the intersection of the partial result arrays
-   * obtained within createMessageList.
-   *
-   * @param keys
-   *        Object containing the partial result arrays.
-   * @param fiter
-   *        Object containing the filter search criteria used to retrieved the
-   *        partial results.
-   *
-   * return Array of keys containing the final result of createMessageList.
-   */
-  keyIntersection: function keyIntersection(keys, filter) {
-    // Always use keys[FILTER_TIMESTAMP] as base result set to be filtered.
-    // This ensures the result set is always sorted by timestamp.
-    let result = keys[FILTER_TIMESTAMP];
-    if (keys[FILTER_NUMBERS].length || filter.numbers) {
-      result = result.filter(function(i) {
-        return keys[FILTER_NUMBERS].indexOf(i) != -1;
-      });
+  upgradeSchema6: function upgradeSchema6(transaction) {
+    let objectStore = transaction.objectStore(STORE_NAME);
+
+    // Delete "delivery" index.
+    if (objectStore.indexNames.contains("delivery")) {
+      objectStore.deleteIndex("delivery");
+    }
+    // Delete "sender" index.
+    if (objectStore.indexNames.contains("sender")) {
+      objectStore.deleteIndex("sender");
+    }
+    // Delete "receiver" index.
+    if (objectStore.indexNames.contains("receiver")) {
+      objectStore.deleteIndex("receiver");
+    }
+    // Delete "read" index.
+    if (objectStore.indexNames.contains("read")) {
+      objectStore.deleteIndex("read");
     }
-    if (keys[FILTER_DELIVERY].length || filter.delivery) {
-      result = result.filter(function(i) {
-        return keys[FILTER_DELIVERY].indexOf(i) != -1;
-      });
+
+    // Create new "delivery", "number" and "read" indexes.
+    objectStore.createIndex("delivery", "deliveryIndex");
+    objectStore.createIndex("number", "numberIndex", { multiEntry: true });
+    objectStore.createIndex("read", "readIndex");
+
+    // Populate new "deliverIndex", "numberIndex" and "readIndex" attributes.
+    objectStore.openCursor().onsuccess = function(event) {
+      let cursor = event.target.result;
+      if (!cursor) {
+        return;
+      }
+
+      let message = cursor.value;
+      let timestamp = message.timestamp;
+      message.deliveryIndex = [message.delivery, timestamp];
+      message.numberIndex = [
+        [message.sender, timestamp],
+        [message.receiver, timestamp]
+      ];
+      message.readIndex = [message.read, timestamp];
+      cursor.update(message);
+      cursor.continue();
     }
-    if (keys[FILTER_READ].length || filter.read) {
-      result = result.filter(function(i) {
-        return keys[FILTER_READ].indexOf(i) != -1;
-      });
-    }
-    return result;
+  },
+
+  createMessageFromRecord: function createMessageFromRecord(record) {
+    if (DEBUG) debug("createMessageFromRecord: " + JSON.stringify(record));
+    return gSmsService.createSmsMessage(record.id,
+                                        record.delivery,
+                                        record.deliveryStatus,
+                                        record.sender,
+                                        record.receiver,
+                                        record.body,
+                                        record.messageClass,
+                                        record.timestamp,
+                                        record.read);
   },
 
   /**
-   * Helper function called after createMessageList gets the final result array
-   * containing the list of primary keys of records that matches the provided
-   * search criteria. This function retrieves from the store the message with
-   * the primary key matching the first one in the message list array and keeps
-   * the rest of this array in memory. It also notifies via nsISmsRequest.
-   *
-   * @param messageList
-   *        Array of primary keys retrieved within createMessageList.
-   * @param request
-   *        A nsISmsRequest object.
+   * Queue up passed message id, reply if necessary. 'aMessageId' = 0 for no
+   * more messages, negtive for errors and valid otherwise.
    */
-  onMessageListCreated: function onMessageListCreated(messageList, aRequest) {
-    if (DEBUG) debug("Message list created: " + messageList);
+  onNextMessageInListGot: function onNextMessageInListGot(
+      aObjectStore, aMessageList, aMessageId) {
+
+    if (DEBUG) {
+      debug("onNextMessageInListGot - listId: "
+            + aMessageList.listId + ", messageId: " + aMessageId);
+    }
+    if (aMessageId) {
+      // Queue up any id but '0' and replies later accordingly.
+      aMessageList.results.push(aMessageId);
+    }
+    if (aMessageId <= 0) {
+      // No more processing on '0' or negative values passed.
+      aMessageList.processing = false;
+    }
+
+    if (!aMessageList.requestWaiting) {
+      if (DEBUG) debug("Cursor.continue() not called yet");
+      return;
+    }
+
+    // We assume there is only one request waiting throughout the message list
+    // retrieving process. So we don't bother continuing to process further
+    // waiting requests here. This assumption comes from SmsCursor::Continue()
+    // implementation.
+    let smsRequest = aMessageList.requestWaiting;
+    aMessageList.requestWaiting = null;
+
+    if (!aMessageList.results.length) {
+      // No fetched results yet.
+      if (!aMessageList.processing) {
+        if (DEBUG) debug("No messages matching the filter criteria");
+        smsRequest.notifyNoMessageInList();
+      }
+      // No fetched results yet and still processing. Let's wait a bit more.
+      return;
+    }
+
+    if (aMessageList.results[0] < 0) {
+      // An previous error found. Keep the answer in results so that we can
+      // reply INTERNAL_ERROR for further requests.
+      if (DEBUG) debug("An previous error found");
+      smsRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
+      return;
+    }
+
+    let firstMessageId = aMessageList.results.shift();
+    if (DEBUG) debug ("Fetching message " + firstMessageId);
+
+    let getRequest = aObjectStore.get(firstMessageId);
     let self = this;
-    self.newTxn(READ_ONLY, function (error, txn, store) {
-      if (error) {
-        aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
-        return;
+    getRequest.onsuccess = function onsuccess(event) {
+      let sms = self.createMessageFromRecord(event.target.result);
+      if (aMessageList.listId >= 0) {
+        if (DEBUG) {
+          debug("notifyNextMessageInListGot - listId: "
+                + aMessageList.listId + ", messageId: " + firstMessageId);
+        }
+        smsRequest.notifyNextMessageInListGot(sms);
+      } else {
+        self.lastMessageListId += 1;
+        aMessageList.listId = self.lastMessageListId;
+        self.messageLists[self.lastMessageListId] = aMessageList;
+        if (DEBUG) {
+          debug("notifyMessageListCreated - listId: "
+                + aMessageList.listId + ", messageId: " + firstMessageId);
+        }
+        smsRequest.notifyMessageListCreated(aMessageList.listId, sms);
+      }
+    }
+    getRequest.onerror = function onerror(event) {
+      if (DEBUG) {
+        debug("notifyReadMessageListFailed - listId: "
+              + aMessageList.listId + ", messageId: " + firstMessageId);
+      }
+      smsRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
+    }
+  },
+
+  /**
+   * Queue up {aMessageId, aTimestamp} pairs, find out intersections and report
+   * to onNextMessageInListGot. Return true if it is still possible to have
+   * another match.
+   */
+  onNextMessageInMultiFiltersGot: function onNextMessageInMultiFiltersGot(
+      aObjectStore, aMessageList, aContextIndex, aMessageId, aTimestamp) {
+
+    if (DEBUG) {
+      debug("onNextMessageInMultiFiltersGot: "
+            + aContextIndex + ", " + aMessageId + ", " + aTimestamp);
+    }
+    let contexts = aMessageList.contexts;
+
+    if (!aMessageId) {
+      contexts[aContextIndex].processing = false;
+      for (let i = 0; i < contexts.length; i++) {
+        if (contexts[i].processing) {
+          return false;
+        }
       }
 
-      let messageId = messageList.shift();
-      if (DEBUG) debug ("Fetching message " + messageId);
-      let request = store.get(messageId);
-      let message;
-      request.onsuccess = function (event) {
-        message = request.result;
-      };
+      this.onNextMessageInListGot(aObjectStore, aMessageList, 0);
+      return false;
+    }
+
+    // Search id in other existing results. If no other results has it,
+    // and A) the last timestamp is smaller-equal to current timestamp,
+    // we wait for further results; either B) record timestamp is larger
+    // then current timestamp or C) no more processing for a filter, then we
+    // drop this id because there can't be a match anymore.
+    for (let i = 0; i < contexts.length; i++) {
+      if (i == aContextIndex) {
+        continue;
+      }
 
-      txn.oncomplete = function oncomplete(event) {
-        if (DEBUG) debug("Transaction " + txn + " completed.");
-        if (!message) {
-          aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
-          return;
+      let ctx = contexts[i];
+      let results = ctx.results;
+      let found = false;
+      for (let j = 0; j < results.length; j++) {
+        let result = results[j];
+        if (result.id == aMessageId) {
+          found = true;
+          break;
+        }
+        if ((!aMessageList.reverse && (result.timestamp > aTimestamp)) ||
+            (aMessageList.reverse && (result.timestamp < aTimestamp))) {
+          // B) Cannot find a match anymore. Drop.
+          return true;
+        }
+      }
+
+      if (!found) {
+        if (!ctx.processing) {
+          // C) Cannot find a match anymore. Drop.
+          if (results.length) {
+            let lastResult = results[results.length - 1];
+            if ((!aMessageList.reverse && (lastResult.timestamp >= aTimestamp)) ||
+                (aMessageList.reverse && (lastResult.timestamp <= aTimestamp))) {
+              // Still have a chance to get another match. Return true.
+              return true;
+            }
+          }
+
+          // Impossible to find another match because all results in ctx have
+          // timestamps smaller than aTimestamp.
+          return this.onNextMessageInMultiFiltersGot(aObjectStore, aMessageList,
+                                                     aContextIndex, 0, 0);
         }
-        self.lastMessageListId += 1;
-        self.messageLists[self.lastMessageListId] = messageList;
-        let sms = gSmsService.createSmsMessage(message.id,
-                                               message.delivery,
-                                               message.deliveryStatus,
-                                               message.sender,
-                                               message.receiver,
-                                               message.body,
-                                               message.messageClass,
-                                               message.timestamp,
-                                               message.read);
-        aRequest.notifyMessageListCreated(self.lastMessageListId, sms);
-      };
+
+        // A) Pending.
+        contexts[aContextIndex].results.push({
+          id: aMessageId,
+          timestamp: aTimestamp
+        });
+        return true;
+      }
+    }
+
+    // Now id is found in all other results. Report it.
+    this.onNextMessageInListGot(aObjectStore, aMessageList, aMessageId);
+    return true;
+  },
+
+  onNextMessageInMultiNumbersGot: function onNextMessageInMultiNumbersGot(
+      aObjectStore, aMessageList, aContextIndex,
+      aQueueIndex, aMessageId, aTimestamp) {
+
+    if (DEBUG) {
+      debug("onNextMessageInMultiNumbersGot: "
+            + aQueueIndex + ", " + aMessageId + ", " + aTimestamp);
+    }
+    let queues = aMessageList.numberQueues;
+    let q = queues[aQueueIndex];
+    if (aMessageId) {
+      if (!aQueueIndex) {
+        // Timestamp.
+        q.results.push({
+          id: aMessageId,
+          timestamp: aTimestamp
+        });
+      } else {
+        // Numbers.
+        q.results.push(aMessageId);
+      }
+      return true;
+    }
+
+    q.processing -= 1;
+    if (queues[0].processing || queues[1].processing) {
+      // At least one queue is still processing, but we got here because
+      // current cursor gives 0 as aMessageId meaning no more messages are
+      // available. Return false here to stop further cursor.continue() calls.
+      return false;
+    }
+
+    let tres = queues[0].results;
+    let qres = queues[1].results;
+    tres = tres.filter(function (element) {
+      return qres.indexOf(element.id) != -1;
     });
+    if (aContextIndex < 0) {
+      for (let i = 0; i < tres.length; i++) {
+        this.onNextMessageInListGot(aObjectStore, aMessageList, tres[i].id);
+      }
+      this.onNextMessageInListGot(aObjectStore, aMessageList, 0);
+    } else {
+      for (let i = 0; i < tres.length; i++) {
+        this.onNextMessageInMultiFiltersGot(aObjectStore, aMessageList,
+                                            aContextIndex,
+                                            tres[i].id, tres[i].timestamp);
+      }
+      this.onNextMessageInMultiFiltersGot(aObjectStore, aMessageList,
+                                          aContextIndex, 0, 0);
+    }
+    return false;
   },
 
   saveMessage: function saveMessage(message) {
     this.lastKey += 1;
     message.id = this.lastKey;
     if (DEBUG) debug("Going to store " + JSON.stringify(message));
     this.newTxn(READ_WRITE, function(error, txn, stores) {
       if (error) {
@@ -522,24 +717,30 @@ SmsDatabaseService.prototype = {
     let sender = aSender;
     if (sender) {
       let parsedNumber = PhoneNumberUtils.parse(sender);
       sender = (parsedNumber && parsedNumber.internationalNumber)
                ? parsedNumber.internationalNumber
                : sender;
     }
 
-    let message = {delivery:       DELIVERY_RECEIVED,
-                   deliveryStatus: DELIVERY_STATUS_SUCCESS,
-                   sender:         sender,
-                   receiver:       receiver,
-                   body:           aBody,
-                   messageClass:   aMessageClass,
-                   timestamp:      aDate,
-                   read:           FILTER_READ_UNREAD};
+    let message = {
+      deliveryIndex:  [DELIVERY_RECEIVED, aDate],
+      numberIndex:    [[sender, aDate], [receiver, aDate]],
+      readIndex:      [FILTER_READ_UNREAD, aDate],
+
+      delivery:       DELIVERY_RECEIVED,
+      deliveryStatus: DELIVERY_STATUS_SUCCESS,
+      sender:         sender,
+      receiver:       receiver,
+      body:           aBody,
+      messageClass:   aMessageClass,
+      timestamp:      aDate,
+      read:           FILTER_READ_UNREAD
+    };
     return this.saveMessage(message);
   },
 
   saveSendingMessage: function saveSendingMessage(aReceiver, aBody, aDate) {
     let sender = this.mRIL.rilContext.icc ? this.mRIL.rilContext.icc.msisdn : null;
 
     // Workaround an xpconnect issue with undefined string objects.
     // See bug 808220
@@ -557,24 +758,30 @@ SmsDatabaseService.prototype = {
 
     if (sender) {
       let parsedNumber = PhoneNumberUtils.parse(sender.toString());
       sender = (parsedNumber && parsedNumber.internationalNumber)
                ? parsedNumber.internationalNumber
                : sender;
     }
 
-    let message = {delivery:       DELIVERY_SENDING,
-                   deliveryStatus: DELIVERY_STATUS_PENDING,
-                   sender:         sender,
-                   receiver:       receiver,
-                   body:           aBody,
-                   messageClass:   MESSAGE_CLASS_NORMAL,
-                   timestamp:      aDate,
-                   read:           FILTER_READ_READ};
+    let message = {
+      deliveryIndex:  [DELIVERY_SENDING, aDate],
+      numberIndex:    [[sender, aDate], [receiver, aDate]],
+      readIndex:      [FILTER_READ_READ, aDate],
+
+      delivery:       DELIVERY_SENDING,
+      deliveryStatus: DELIVERY_STATUS_PENDING,
+      sender:         sender,
+      receiver:       receiver,
+      body:           aBody,
+      messageClass:   MESSAGE_CLASS_NORMAL,
+      timestamp:      aDate,
+      read:           FILTER_READ_READ
+    };
     return this.saveMessage(message);
   },
 
   setMessageDelivery: function setMessageDelivery(messageId, delivery, deliveryStatus) {
     if (DEBUG) {
       debug("Setting message " + messageId + " delivery to " + delivery
             + ", and deliveryStatus to " + deliveryStatus);
     }
@@ -603,32 +810,34 @@ SmsDatabaseService.prototype = {
             && (message.deliveryStatus == deliveryStatus)) {
           if (DEBUG) {
             debug("The values of attribute delivery and deliveryStatus are the"
                   + " the same with given parameters.");
           }
           return;
         }
         message.delivery = delivery;
+        message.deliveryIndex = [delivery, message.timestamp];
         message.deliveryStatus = deliveryStatus;
         if (DEBUG) {
           debug("Message.delivery set to: " + delivery
                 + ", and Message.deliveryStatus set to: " + deliveryStatus);
         }
         store.put(message);
       };
     });
   },
 
   /**
    * nsISmsDatabaseService API
    */
 
   getMessage: function getMessage(messageId, aRequest) {
     if (DEBUG) debug("Retrieving message with ID " + messageId);
+    let self = this;
     this.newTxn(READ_ONLY, function (error, txn, store) {
       if (error) {
         if (DEBUG) debug(error);
         aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
         return;
       }
       let request = store.mozGetAll(messageId);
 
@@ -648,26 +857,18 @@ SmsDatabaseService.prototype = {
         if (data.id != messageId) {
           if (DEBUG) {
             debug("Requested message ID (" + messageId + ") is " +
                   "different from the one we got");
           }
           aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.UNKNOWN_ERROR);
           return;
         }
-        let message = gSmsService.createSmsMessage(data.id,
-                                                   data.delivery,
-                                                   data.deliveryStatus,
-                                                   data.sender,
-                                                   data.receiver,
-                                                   data.body,
-                                                   data.messageClass,
-                                                   data.timestamp,
-                                                   data.read);
-        aRequest.notifyMessageGot(message);
+        let sms = self.createMessageFromRecord(data);
+        aRequest.notifyMessageGot(sms);
       };
 
       txn.onerror = function onerror(event) {
         if (DEBUG) {
           if (event.target)
             debug("Caught error on transaction", event.target.errorCode);
         }
         //TODO look at event.target.errorCode, pick appropriate error constant
@@ -716,54 +917,39 @@ SmsDatabaseService.prototype = {
               // This must exist.
               let mostRecentEntry = event.target.result;
 
               if (!message.read) {
                 mostRecentEntry.unreadCount--;
               }
 
               if (mostRecentEntry.id == messageId) {
-                // This sucks, we have to find a new most-recent message.
-                message = null;
-
-                // Check most recent sender.
-                smsStore.index("sender").openCursor(number, "prev").onsuccess = function(event) {
-                  let cursor = event.target.result;
-                  if (cursor) {
-                    message = cursor.value;
-                  }
-                };
-
-                // Check most recent receiver.
-                smsStore.index("receiver").openCursor(number, "prev").onsuccess = function(event) {
+                // Check most recent sender/receiver.
+                let numberRange = IDBKeyRange.bound([number, 0], [number, ""]);
+                let numberRequest = smsStore.index("number")
+                                            .openCursor(numberRange, PREV);
+                numberRequest.onsuccess = function(event) {
                   let cursor = event.target.result;
-                  if (cursor) {
-                    if (!message || cursor.value.timeStamp > message.timestamp) {
-                      message = cursor.value;
-                    }
-                  }
-
-                  // If we found a new message then we need to update the data
-                  // in the most-recent store. Otherwise we can delete it.
-                  if (message) {
-                    mostRecentEntry.id = message.id;
-                    mostRecentEntry.timestamp = message.timestamp;
-                    mostRecentEntry.body = message.body;
-                    if (DEBUG) {
-                      debug("Updating mru entry: " +
-                            JSON.stringify(mostRecentEntry));
-                    }
-                    mruStore.put(mostRecentEntry);
-                  }
-                  else {
+                  if (!cursor) {
                     if (DEBUG) {
                       debug("Deleting mru entry for number '" + number + "'");
                     }
                     mruStore.delete(number);
+                    return;
                   }
+
+                  let nextMsg = cursor.value;
+                  mostRecentEntry.id = nextMsg.id;
+                  mostRecentEntry.timestamp = nextMsg.timestamp;
+                  mostRecentEntry.body = nextMsg.body;
+                  if (DEBUG) {
+                    debug("Updating mru entry: " +
+                          JSON.stringify(mostRecentEntry));
+                  }
+                  mruStore.put(mostRecentEntry);
                 };
               } else if (!message.read) {
                 // Shortcut, just update the unread count.
                 if (DEBUG) {
                   debug("Updating unread count for number '" + number + "': " +
                         (mostRecentEntry.unreadCount + 1) + " -> " +
                         mostRecentEntry.unreadCount);
                 }
@@ -783,200 +969,338 @@ SmsDatabaseService.prototype = {
       debug("Creating a message list. Filters:" +
             " startDate: " + filter.startDate +
             " endDate: " + filter.endDate +
             " delivery: " + filter.delivery +
             " numbers: " + filter.numbers +
             " read: " + filter.read +
             " reverse: " + reverse);
     }
-    // This object keeps the lists of keys retrieved by the search specific to
-    // each nsIMozSmsFilter. Once all the keys have been retrieved from the
-    // store, the final intersection of this arrays will contain all the
-    // keys for the message list that we are creating.
-    let filteredKeys = {};
-    filteredKeys[FILTER_TIMESTAMP] = [];
-    filteredKeys[FILTER_NUMBERS] = [];
-    filteredKeys[FILTER_DELIVERY] = [];
-    filteredKeys[FILTER_READ] = [];
-
-    // Callback function to iterate through request results via IDBCursor.
-    let successCb = function onsuccess(result, filter) {
-      // Once the cursor has retrieved all keys that matches its key range,
-      // the filter search is done.
-      if (!result) {
-        if (DEBUG) {
-          debug("These messages match the " + filter + " filter: " +
-                filteredKeys[filter]);
-        }
-        return;
-      }
-      // The cursor primaryKey is stored in its corresponding partial array
-      // according to the filter parameter.
-      let primaryKey = result.primaryKey;
-      filteredKeys[filter].push(primaryKey);
-      result.continue();
-    };
-
-    let errorCb = function onerror(event) {
-      //TODO look at event.target.errorCode, pick appropriate error constant.
-      if (DEBUG) debug("IDBRequest error " + event.target.errorCode);
-      aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
-      return;
-    };
 
     let self = this;
     this.newTxn(READ_ONLY, function (error, txn, store) {
       if (error) {
-        errorCb(error);
+        //TODO look at event.target.errorCode, pick appropriate error constant.
+        if (DEBUG) debug("IDBRequest error " + error.target.errorCode);
+        aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
         return;
       }
 
-      // In first place, we retrieve the keys that match the filter.startDate
-      // and filter.endDate search criteria.
-      let timeKeyRange = null;
-      if (filter.startDate != null && filter.endDate != null) {
-        timeKeyRange = IDBKeyRange.bound(filter.startDate.getTime(),
-                                         filter.endDate.getTime());
-      } else if (filter.startDate != null) {
-        timeKeyRange = IDBKeyRange.lowerBound(filter.startDate.getTime());
-      } else if (filter.endDate != null) {
-        timeKeyRange = IDBKeyRange.upperBound(filter.endDate.getTime());
-      }
-      let direction = reverse ? PREV : NEXT;
-      let timeRequest = store.index("timestamp").openKeyCursor(timeKeyRange,
-                                                               direction);
+      let messageList = {
+        listId: -1,
+        reverse: reverse,
+        processing: true,
+        stop: false,
+        // Local contexts for multiple filter targets' case.
+        contexts: null,
+        // Result queues for multiple numbers filter's case.
+        numberQueues: null,
+        // Pending createMessageList or getNextMessageInList SmsRequest.
+        requestWaiting: aRequest,
+        results: []
+      };
 
-      timeRequest.onsuccess = function onsuccess(event) {
-        successCb(event.target.result, FILTER_TIMESTAMP);
-      };
-      timeRequest.onerror = errorCb;
-
-      // Retrieve the keys from the 'delivery' index that matches the
-      // value of filter.delivery.
-      if (filter.delivery) {
-        let deliveryKeyRange = IDBKeyRange.only(filter.delivery);
-        let deliveryRequest = store.index("delivery")
-                                   .openKeyCursor(deliveryKeyRange);
-        deliveryRequest.onsuccess = function onsuccess(event) {
-          successCb(event.target.result, FILTER_DELIVERY);
-        };
-        deliveryRequest.onerror = errorCb;
-      }
+      let onNextMessageInListGotCb =
+        self.onNextMessageInListGot.bind(self, store, messageList);
 
-      // Retrieve the keys from the 'sender' and 'receiver' indexes that
-      // match the values of filter.numbers
-      if (filter.numbers) {
-        for (let i = 0; i < filter.numbers.length; i++) {
-          let numberKeyRange = IDBKeyRange.only(filter.numbers[i]);
-          let senderRequest = store.index("sender")
-                                   .openKeyCursor(numb