Merge mozilla-central to UX
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 26 Jul 2013 00:27:14 -0700
changeset 155601 61678b88455bbf662b5e4d9c70ebc8ca5c1171bd
parent 155600 91c9b0675e2b76d8be6b57545e1007a2428dd57a (current diff)
parent 140003 46d73e889cb47c1497c9a967d055ab5123e05514 (diff)
child 155602 c1657630b126cdf179776259bdf09e3b35da669f
push id25666
push userjwein@mozilla.com
push dateMon, 18 Nov 2013 15:56:58 +0000
treeherdermozilla-central@f2adb62d07eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.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 mozilla-central to UX
addon-sdk/source/doc/module-source/sdk/test/utils.md
addon-sdk/source/test/addons/main/main.js
addon-sdk/source/test/addons/main/package.json
addon-sdk/source/test/addons/simple-prefs/lib/main.js
addon-sdk/source/test/addons/simple-prefs/package.json
browser/app/profile/firefox.js
browser/base/content/browser.js
gfx/layers/GonkIOSurfaceImage.cpp
gfx/layers/GonkIOSurfaceImage.h
mobile/android/base/ProfileMigrator.java
mobile/android/base/sync/repositories/android/FennecControlHelper.java
mobile/android/base/tests/assets/places.sqlite.zip
mobile/android/base/tests/testMigration.java.in
toolkit/modules/WindowDraggingUtils.jsm
widget/windows/nsWindow.cpp
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -927,23 +927,29 @@ HyperTextAccessible::GetTextBeforeOffset
     case BOUNDARY_LINE_START:
       if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
         offset = AdjustCaretOffset(offset);
 
       *aStartOffset = FindLineBoundary(offset, ePrevLineBegin);
       *aEndOffset = FindLineBoundary(offset, eThisLineBegin);
       return GetText(*aStartOffset, *aEndOffset, aText);
 
-    case BOUNDARY_LINE_END:
+    case BOUNDARY_LINE_END: {
       if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
         offset = AdjustCaretOffset(offset);
 
       *aEndOffset = FindLineBoundary(offset, ePrevLineEnd);
-      *aStartOffset = FindLineBoundary(*aEndOffset, ePrevLineEnd);
+      int32_t tmpOffset = *aEndOffset;
+      // Adjust offset if line is wrapped.
+      if (*aEndOffset != 0 && !IsLineEndCharAt(*aEndOffset))
+        tmpOffset--;
+
+      *aStartOffset = FindLineBoundary(tmpOffset, ePrevLineEnd);
       return GetText(*aStartOffset, *aEndOffset, aText);
+    }
 
     default:
       return NS_ERROR_INVALID_ARG;
   }
 }
 
 NS_IMETHODIMP
 HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
@@ -2146,23 +2152,19 @@ HyperTextAccessible::RenderedToContentOf
 bool
 HyperTextAccessible::GetCharAt(int32_t aOffset, EGetTextType aShift,
                                nsAString& aChar, int32_t* aStartOffset,
                                int32_t* aEndOffset)
 {
   aChar.Truncate();
 
   int32_t offset = ConvertMagicOffset(aOffset) + static_cast<int32_t>(aShift);
-  int32_t childIdx = GetChildIndexAtOffset(offset);
-  if (childIdx == -1)
+  if (!CharAt(offset, aChar))
     return false;
 
-  Accessible* child = GetChildAt(childIdx);
-  child->AppendTextTo(aChar, offset - GetChildOffset(childIdx), 1);
-
   if (aStartOffset)
     *aStartOffset = offset;
   if (aEndOffset)
     *aEndOffset = aChar.IsEmpty() ? offset : offset + 1;
 
   return true;
 }
 
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -160,16 +160,46 @@ public:
    * Return character count within the hypertext accessible.
    */
   uint32_t CharacterCount()
   {
     return GetChildOffset(ChildCount());
   }
 
   /**
+   * Get a character at the given offset (don't support magic offsets).
+   */
+  bool CharAt(int32_t aOffset, nsAString& aChar)
+  {
+    int32_t childIdx = GetChildIndexAtOffset(aOffset);
+    if (childIdx == -1)
+      return false;
+
+    Accessible* child = GetChildAt(childIdx);
+    child->AppendTextTo(aChar, aOffset - GetChildOffset(childIdx), 1);
+    return true;
+  }
+
+  /**
+   * Return true if char at the given offset equals to given char.
+   */
+  bool IsCharAt(int32_t aOffset, char aChar)
+  {
+    nsAutoString charAtOffset;
+    CharAt(aOffset, charAtOffset);
+    return charAtOffset.CharAt(0) == aChar;
+  }
+
+  /**
+   * Return true if terminal char is at the given offset.
+   */
+  bool IsLineEndCharAt(int32_t aOffset)
+    { return IsCharAt(aOffset, '\n'); }
+
+  /**
    * Get a character before/at/after the given offset.
    *
    * @param aOffset       [in] the given offset
    * @param aShift        [in] specifies whether to get a char before/at/after
    *                        offset
    * @param aChar         [out] the character
    * @param aStartOffset  [out, optional] the start offset of the character
    * @param aEndOffset    [out, optional] the end offset of the character
@@ -283,22 +313,18 @@ protected:
     return aOffset;
   }
 
   /**
    * Return true if the given offset points to terminal empty line if any.
    */
   bool IsEmptyLastLineOffset(int32_t aOffset)
   {
-    if (aOffset != static_cast<int32_t>(CharacterCount()))
-      return false;
-
-    nsAutoString lastChar;
-    GetText(aOffset -1, -1, lastChar);
-    return lastChar.EqualsLiteral("\n");
+    return aOffset == static_cast<int32_t>(CharacterCount()) &&
+      IsLineEndCharAt(aOffset - 1);
   }
 
   /**
    * Return an offset of the found word boundary.
    */
   int32_t FindWordBoundary(int32_t aOffset, nsDirection aDirection,
                            EWordMovementType aWordMovementType)
   {
--- a/accessible/tests/mochitest/text/test_atcaretoffset.html
+++ b/accessible/tests/mochitest/text/test_atcaretoffset.html
@@ -51,17 +51,17 @@
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
                              [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
-                             "textarea", kTodo, kTodo, kOk);
+                             [ "textarea" ]);
       }
 
       this.getID = function moveToLastLineEnd_getID()
       {
         return "move to last line end";
       }
     }
 
@@ -82,17 +82,17 @@
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
                              [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
-                             "textarea", kTodo, kTodo, kOk);
+                             [ "textarea" ]);
       }
 
       this.getID = function moveToLastLineStart_getID()
       {
         return "move to last line start";
       }
     }
 
@@ -176,17 +176,17 @@
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "aword", 0, 5,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "", 0, 0,
                              [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_END, "", 0, 0,
-                             "textarea", kOk, kOk, kOk);
+                             [ "textarea" ]);
       }
 
       this.getID = function moveToFirstLineStart_getID()
       {
         return "move to first line start";
       }
     }
 
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -215,16 +215,18 @@ function startup(data, reasonCode) {
       loadReason: reason,
 
       prefixURI: prefixURI,
       // Add-on URI.
       rootURI: rootURI,
       // options used by system module.
       // File to write 'OK' or 'FAIL' (exit code emulation).
       resultFile: options.resultFile,
+      // File to write stdout.
+      logFile: options.logFile,
       // Arguments passed as --static-args
       staticArgs: options.staticArgs,
 
       // Arguments related to test runner.
       modules: {
         '@test/options': {
           allTestModules: options.allTestModules,
           iterations: options.iterations,
--- a/addon-sdk/source/app-extension/install.rdf
+++ b/addon-sdk/source/app-extension/install.rdf
@@ -12,18 +12,18 @@
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
 
     <!-- Firefox -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>21.0</em:minVersion>
-        <em:maxVersion>25.0a1</em:maxVersion>
+        <em:minVersion>19.0</em:minVersion>
+        <em:maxVersion>22.0a1</em:maxVersion>
       </Description>
     </em:targetApplication>
 
     <!-- Front End MetaData -->
     <em:name>Test App</em:name>
     <em:description>Harness for tests.</em:description>
     <em:creator>Mozilla Corporation</em:creator>
     <em:homepageURL></em:homepageURL>
--- a/addon-sdk/source/doc/dev-guide-source/cfx-tool.md
+++ b/addon-sdk/source/doc/dev-guide-source/cfx-tool.md
@@ -635,24 +635,25 @@ of `updateURL`.
 
 Note that as the [add-on documentation](https://developer.mozilla.org/en/extension_versioning,_update_and_compatibility#Securing_Updates)
 explains, you should make sure the update procedure for your add-on is secure,
 and this usually involves using HTTPS for the links.
 
 So if we run the following command:
 
 <pre>
-  cfx xpi --update-link https://example.com/addon/latest/pluginName.xpi --update-url https://example.com/addon/update_rdf/pluginName.update.rdf
+  cfx xpi --update-link https://example.com/addon/latest
+          --update-url https://example.com/addon/update_rdf
 </pre>
 
 `cfx` will create two files:
 
 * an XPI file which embeds
-`https://example.com/addon/update_rdf/pluginName.update.rdf` as the value of `updateURL`
-* an RDF file which embeds `https://example.com/addon/latest/pluginName.xpi` as the value of
+`https://example.com/addon/update_rdf` as the value of `updateURL`
+* an RDF file which embeds `https://example.com/addon/latest` as the value of
 `updateLink`.
 
 ### Supported Options ###
 
 As with `cfx run` you can point `cfx` at a different `package.json` file using
 the `--pkgdir` option. You can also embed arguments in the XPI using the
 `--static-args` option: if you do this the arguments will be passed to your
 add-on whenever it is run.
--- a/addon-sdk/source/doc/module-source/sdk/self.md
+++ b/addon-sdk/source/doc/module-source/sdk/self.md
@@ -8,23 +8,16 @@ The `self` module provides access to dat
 as a whole. It also provides access to the
 [Program ID](dev-guide/guides/program-id.html), a value which is
 unique for each add-on.
 
 Note that the `self` module is completely different from the global `self`
 object accessible to content scripts, which is used by a content script to
 [communicate with the add-on code](dev-guide/guides/content-scripts/using-port.html).
 
-<api name="uri">
-@property {string}
-This property represents an add-on associated unique URI string.
-This URI can be used for APIs which require a valid URI string, such as the
-[passwords](modules/sdk/passwords.html) module.
-</api>
-
 <api name="id">
 @property {string}
 This property is a printable string that is unique for each add-on. It comes
 from the `id` property set in the `package.json` file in the main package
 (i.e. the package in which you run `cfx xpi`). While not generally of use to
 add-on code directly, it can be used by internal API code to index local
 storage and other resources that are associated with a particular add-on.
 Eventually, this ID will be unspoofable (see
deleted file mode 100644
--- a/addon-sdk/source/doc/module-source/sdk/test/utils.md
+++ /dev/null
@@ -1,90 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-The `test/utils` module provides additional helper methods to be used in
-the CommonJS Unit Testing test suite.
-
-## Before and After
-
-Helper functions `before()` and `after()` are available for running a function
-before or after each test in a suite. They're useful when you need to
-guarantee a particular state before running a test, and to clean up
-after your test.
-
-    let { before, after } = require('sdk/test/utils');
-    let { search } = require('sdk/places/bookmarks');
-
-    exports.testCountBookmarks = function (assert, done) {
-      search().on('end', function (results) {
-        assert.equal(results, 0, 'should be no bookmarks');
-        done();
-      });
-    };
-
-    before(exports, function (name, assert) {
-      removeAllBookmarks();
-    });
-
-    require('sdk/test').run(exports);
-
-Both `before` and `after` may be asynchronous. To make them asynchronous,
-pass a third argument `done`, which is a function to call when you have
-finished:
-
-    let { before, after } = require('sdk/test/utils');
-    let { search } = require('sdk/places/bookmarks');
-
-    exports.testCountBookmarks = function (assert, done) {
-      search().on('end', function (results) {
-        assert.equal(results, 0, 'should be no bookmarks');
-        done();
-      });
-    };
-
-    before(exports, function (name, assert, done) {
-      removeAllBookmarksAsync(function () {
-        done();
-      });
-    });
-
-    require('sdk/test').run(exports);
-
-<api name="before">
-@function
-  Runs `beforeFn` before each test in the file. May be asynchronous
-  if `beforeFn` accepts a third argument, which is a callback.
-
- @param exports {Object}
-    A test file's `exports` object
- @param beforeFn {Function}
-    The function to be called before each test. It has two arguments,
-    or three if it is asynchronous:
-
-   * the first argument is the test's name as a `String`.
-   * the second argument is the `assert` object for the test.
-   * the third, optional, argument is a callback. If the callback is
-    defined, then the `beforeFn` is considered asynchronous, and the
-    callback must be invoked before the test runs.
-
-</api>
-
-<api name="after">
-@function
-  Runs `afterFn` after each test in the file. May be asynchronous
-  if `afterFn` accepts a third argument, which is a callback.
-
- @param exports {Object}
-    A test file's `exports` object
- @param afterFn {Function}
-    The function to be called after each test. It has two arguments,
-    or three if it is asynchronous:
-
-   * the first argument is the test's name as a `String`.
-   * the second argument is the `assert` object for the test.
-   * the third, optional, argument is a callback. If the callback is
-    defined, then the `afterFn` is considered asynchronous, and the
-    callback must be invoked before the next test runs.
-
-</api>
-
--- a/addon-sdk/source/doc/module-source/sdk/util/array.md
+++ b/addon-sdk/source/doc/module-source/sdk/util/array.md
@@ -1,13 +1,13 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
-The `util/array` module provides simple helper functions for working with
+The `util/array` module provides simple helper functions for working with 
 arrays.
 
 <api name="has">
 @function
 Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains the element or `false` otherwise.
 A simplified version of `array.indexOf(element) >= 0`.
 
     let { has } = require('sdk/util/array');
@@ -24,17 +24,17 @@ A simplified version of `array.indexOf(e
   The element to search for in the array.
 
 @returns {boolean}
   A boolean indicating whether or not the element was found in the array.
 </api>
 
 <api name="hasAny">
 @function
-Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains any of the elements in the
+Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains any of the elements in the 
 `elements` array, or `false` otherwise.
 
     let { hasAny } = require('sdk/util/array');
     let a = ['rock', 'roll', 100];
 
     hasAny(a, ['rock', 'bright', 'light']); // true
     hasAny(a, ['rush', 'coil', 'jet']); // false
 
@@ -79,17 +79,17 @@ does not add the element and returns `fa
 If the given [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains the given element, this function
 removes the element from the array and returns `true`. Otherwise, this function
 does not alter the array and returns `false`.
 
     let { remove } = require('sdk/util/array');
     let a = ['alice', 'bob', 'carol'];
 
     remove(a, 'dave'); // false
-    remove(a, 'bob'); // true
+    remove(a, 'bob'); // true 
     remove(a, 'bob'); // false
 
     console.log(a); // ['alice', 'carol']
 
 @param array {array}
   The array to remove the element from.
 
 @param element {*}
@@ -149,29 +149,8 @@ Iterates over an [iterator](https://deve
 
 @param iterator {iterator}
   The [`Iterator`](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators#Iterators) object over which to iterate and place results into an array.
 
 @returns {array}
   The iterator's results in an array.
 </api>
 
-<api name="find">
-@function
-Iterates over given `array` and applies given `predicate` function until
-`predicate(element)` is `true`. If such element is found it's retured back
-otherwise third optional `fallback` argument is returned back. If fallback
-is not provided returns `undefined`.
-
-    let { find } = require('sdk/util/array');
-    let isOdd = (x) => x % 2;
-    find([2, 4, 5, 7, 8, 9], isOdd);   // => 5
-    find([2, 4, 6, 8], isOdd);         // => undefiend
-    find([2, 4, 6, 8], isOdd, null);   // => null
-
-    fromIterator(i) // ['otoro', 'unagi', 'keon']
-
-@param iterator {iterator}
-  The [`Iterator`](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators#Iterators) object over which to iterate and place results into an array.
-
-@returns {array}
-  The iterator's results in an array.
-</api>
--- a/addon-sdk/source/lib/sdk/console/plain-text.js
+++ b/addon-sdk/source/lib/sdk/console/plain-text.js
@@ -3,77 +3,134 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { Cc, Ci, Cu, Cr } = require("chrome");
+const { Cc, Ci } = require("chrome");
 const self = require("../self");
+const traceback = require("./traceback")
 const prefs = require("../preferences/service");
 const { merge } = require("../util/object");
-const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
+const { partial } = require("../lang/functional");
 
+const LEVELS = {
+  "all": Number.MIN_VALUE,
+  "debug": 20000,
+  "info": 30000,
+  "warn": 40000,
+  "error": 50000,
+  "off": Number.MAX_VALUE,
+};
 const DEFAULT_LOG_LEVEL = "error";
 const ADDON_LOG_LEVEL_PREF = "extensions." + self.id + ".sdk.console.logLevel";
 const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel";
 
-let logLevel = DEFAULT_LOG_LEVEL;
+let logLevel;
+
 function setLogLevel() {
-  logLevel = prefs.get(ADDON_LOG_LEVEL_PREF, 
-                           prefs.get(SDK_LOG_LEVEL_PREF,
-                                     DEFAULT_LOG_LEVEL));
+  logLevel = prefs.get(ADDON_LOG_LEVEL_PREF, prefs.get(SDK_LOG_LEVEL_PREF,
+                                                       DEFAULT_LOG_LEVEL));
 }
 setLogLevel();
 
 let logLevelObserver = {
-  QueryInterface: function(iid) {
-    if (!iid.equals(Ci.nsIObserver) &&
-        !iid.equals(Ci.nsISupportsWeakReference) &&
-        !iid.equals(Ci.nsISupports))
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    return this;
-  },
   observe: function(subject, topic, data) {
     setLogLevel();
   }
 };
 let branch = Cc["@mozilla.org/preferences-service;1"].
              getService(Ci.nsIPrefService).
              getBranch(null);
-branch.addObserver(ADDON_LOG_LEVEL_PREF, logLevelObserver, true);
-branch.addObserver(SDK_LOG_LEVEL_PREF, logLevelObserver, true);
+branch.addObserver(ADDON_LOG_LEVEL_PREF, logLevelObserver, false);
+branch.addObserver(SDK_LOG_LEVEL_PREF, logLevelObserver, false);
+
+function stringify(arg) {
+  try {
+    return String(arg);
+  }
+  catch(ex) {
+    return "<toString() error>";
+  }
+}
+
+function stringifyArgs(args) {
+  return Array.map(args, stringify).join(" ");
+}
+
+function message(print, level) {
+  if (LEVELS[level] < LEVELS[logLevel])
+    return;
+
+  let args = Array.slice(arguments, 2);
+
+  print(level + ": " + self.name + ": " + stringifyArgs(args) + "\n", level);
+}
+
+function errorMessage(print, e) {
+  // Some platform exception doesn't have name nor message but
+  // can be stringified to a meaningfull message
+  var fullString = ("An exception occurred.\n" +
+                   (e.name ? e.name + ": " : "") +
+                   (e.message ? e.message : e.toString()) + "\n" +
+                   (e.fileName ? traceback.sourceURI(e.fileName) + " " +
+                                 e.lineNumber + "\n"
+                               : "") +
+                   traceback.format(e));
+
+  message(print, "error", fullString);
+}
+
+function traceMessage(print) {
+  var stack = traceback.get();
+  stack.splice(-1, 1);
+
+  message(print, "info", traceback.format(stack));
+}
 
 function PlainTextConsole(print) {
+  if (!print)
+    print = dump;
 
-  let consoleOptions = {
-    prefix: self.name + ": ",
-    maxLogLevel: logLevel,
-    dump: print
-  };
-  let console = new ConsoleAPI(consoleOptions);
+  if (print === dump) {
+    // If we're just using dump(), auto-enable preferences so
+    // that the developer actually sees the console output.
+    var prefs = Cc["@mozilla.org/preferences-service;1"]
+                .getService(Ci.nsIPrefBranch);
+    prefs.setBoolPref("browser.dom.window.dump.enabled", true);
+  }
 
-  // As we freeze the console object, we can't modify this property afterward
-  Object.defineProperty(console, "maxLogLevel", {
-    get: function() {
-      return logLevel;
-    }
+  merge(this, {
+    log: partial(message, print, "info"),
+    info: partial(message, print, "info"),
+    warn: partial(message, print, "warn"),
+    error: partial(message, print, "error"),
+    debug: partial(message, print, "debug"),
+    exception: partial(errorMessage, print),
+    trace: partial(traceMessage, print),
+
+    dir: function dir() {},
+    group: function group() {},
+    groupCollapsed: function groupCollapsed() {},
+    groupEnd: function groupEnd() {},
+    time: function time() {},
+    timeEnd: function timeEnd() {}
   });
 
   // We defined the `__exposedProps__` in our console chrome object.
   // Although it seems redundant, because we use `createObjectIn` too, in
   // worker.js, we are following what `ConsoleAPI` does. See:
   // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#132
   //
   // Meanwhile we're investigating with the platform team if `__exposedProps__`
   // are needed, or are just a left-over.
 
-  console.__exposedProps__ = Object.keys(ConsoleAPI.prototype).reduce(function(exposed, prop) {
+  this.__exposedProps__ = Object.keys(this).reduce(function(exposed, prop) {
     exposed[prop] = "r";
     return exposed;
   }, {});
 
-  Object.freeze(console);
-  return console;
+  Object.freeze(this);
 };
 exports.PlainTextConsole = PlainTextConsole;
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -4,18 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
-const { Cu } = require("chrome");
-const memory = require("./memory");
+const memory = require('./memory');
 var timer = require("../timers");
 var cfxArgs = require("@test/options");
 
 exports.findAndRunTests = function findAndRunTests(options) {
   var TestFinder = require("./unit-test-finder").TestFinder;
   var finder = new TestFinder({
     filter: options.filter,
     testInProcess: options.testInProcess,
@@ -439,28 +438,27 @@ TestRunner.prototype = {
     // We may already have registered a timeout callback
     if (this.waitTimeout)
       timer.clearTimeout(this.waitTimeout);
 
     this.waitTimeout = timer.setTimeout(tiredOfWaiting, ms);
   },
 
   startMany: function startMany(options) {
-    let runNextTest = (self) => Cu.schedulePreciseGC(_ => {
+    function runNextTest(self) {
       var test = options.tests.shift();
       if (options.stopOnError && self.test && self.test.failed) {
         self.console.error("aborted: test failed and --stop-on-error was specified");
         options.onDone(self);
       } else if (test) {
         self.start({test: test, onDone: runNextTest});
       } else {
         options.onDone(self);
       }
-    });
-
+    }
     runNextTest(this);
   },
 
   start: function start(options) {
     this.test = options.test;
     this.test.passed = 0;
     this.test.failed = 0;
     this.test.errors = {};
--- a/addon-sdk/source/lib/sdk/io/buffer.js
+++ b/addon-sdk/source/lib/sdk/io/buffer.js
@@ -4,257 +4,79 @@
  */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 
-const { Cu } = require("chrome");
-const { TextEncoder, TextDecoder } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
-
-exports.TextEncoder = TextEncoder;
-exports.TextDecoder = TextDecoder;
-
+const { Cc, Ci, CC } = require("chrome");
+const { Class } = require("../core/heritage");
 
-function Buffer(subject, encoding) {
-    var type = typeof(subject);
-    switch (type) {
-      case "number":
-        // Create typed array of the given size if number.
-        return Uint8Array(subject > 0 ? Math.floor(subject) : 0);
-      case "string":
-        // If string encode it and use buffer for the returned Uint8Array
-        // to create a local patched version that acts like node buffer.
-        encoding = encoding || "utf8";
-        return Uint8Array(TextEncoder(encoding).encode(subject).buffer);
-      case "object":
-        // If array or alike just make a copy with a local patched prototype.
-        return Uint8Array(subject);
-     default:
-        throw new TypeError("must start with number, buffer, array or string");
-    }
-}
-exports.Buffer = Buffer;
-
-// Tests if `value` is a Buffer.
-Buffer.isBuffer = value => value instanceof Buffer
-
-// Returns true if the encoding is a valid encoding argument & false otherwise
-Buffer.isEncoding = encoding => !!ENCODINGS[String(encoding).toLowerCase()]
+const Transcoder = CC("@mozilla.org/intl/scriptableunicodeconverter",
+                      "nsIScriptableUnicodeConverter");
 
-// Gives the actual byte length of a string. encoding defaults to 'utf8'.
-// This is not the same as String.prototype.length since that returns the
-// number of characters in a string.
-Buffer.byteLength = (value, encoding = "utf8") =>
-  TextEncoder(encoding).encode(value).byteLength
-
-// Direct copy of the nodejs's buffer implementation:
-// https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177
-Buffer.concat = function(list, length) {
-  if (!Array.isArray(list))
-    throw new TypeError('Usage: Buffer.concat(list[, length])');
-
-  if (typeof length === 'undefined') {
-    length = 0;
-    for (var i = 0; i < list.length; i++)
-      length += list[i].length;
-  } else {
-    length = ~~length;
-  }
-
-  if (length < 0)
-    length = 0;
-
-  if (list.length === 0)
-    return new Buffer(0);
-  else if (list.length === 1)
-    return list[0];
-
-  if (length < 0)
-    throw new RangeError('length is not a positive number');
-
-  var buffer = new Buffer(length);
-  var pos = 0;
-  for (var i = 0; i < list.length; i++) {
-    var buf = list[i];
-    buf.copy(buffer, pos);
-    pos += buf.length;
-  }
-
-  return buffer;
-};
+var Buffer = Class({
+  initialize: function initialize(subject, encoding) {
+    subject = subject ? subject.valueOf() : 0;
+    let length = typeof subject === "number" ? subject : 0;
+    this.encoding = encoding || "utf-8";
+    this.valueOf(Array.isArray(subject) ? subject : new Array(length));
 
-// Node buffer is very much like Uint8Array although it has bunch of methods
-// that typically can be used in combination with `DataView` while preserving
-// access by index. Since in SDK echo module has it's own set of bult-ins it
-// ok to patch ours to make it nodejs Buffer compatible.
-Buffer.prototype = Uint8Array.prototype;
-Object.defineProperties(Buffer.prototype, {
-  view: {
-    get: function() this._view || (this._view = DataView(this.buffer))
+    if (typeof subject === "string") this.write(subject);
+  },
+  get length() {
+    return this.valueOf().length;
+  },
+  get: function get(index) {
+    return this.valueOf()[index];
+  },
+  set: function set(index, value) {
+    return this.valueOf()[index] = value;
   },
-  toString: {
-    value: function(encoding, start, end) {
-      encoding = !!encoding ? (encoding + '').toLowerCase() : "utf8";
-      start = Math.max(0, ~~start);
-      end = Math.min(this.length, end === void(0) ? this.length : ~~end);
-      return TextDecoder(encoding).decode(this.subarray(start, end));
-    }
-  },
-  toJSON: {
-    value: function() ({ type: "Buffer", data: Array.slice(this, 0) })
+  valueOf: function valueOf(value) {
+    Object.defineProperty(this, "valueOf", {
+      value: Array.prototype.valueOf.bind(value),
+      configurable: false,
+      writable: false,
+      enumerable: false
+    });
   },
-  get: {
-    value: function(offset) this[offset]
-  },
-  set: {
-    value: function(offset, value) this[offset] = value
-  },
-  copy: {
-    value: function(target, offset, start, end)
-      Uint8Array.set(target, this.subarray(start, end), offset)
-  },
-  slice: {
-    value: Buffer.prototype.subarray
+  toString: function toString(encoding, start, end) {
+    let bytes = this.valueOf().slice(start || 0, end || this.length);
+    let transcoder = Transcoder();
+    transcoder.charset = String(encoding || this.encoding).toUpperCase();
+    return transcoder.convertFromByteArray(bytes, this.length);
   },
-  write: {
-    value: function(string, offset, length, encoding = "utf8") {
-      if (typeof(offset) === "string")
-        ([offset, length, encoding]) = [0, null, offset];
-      else if (typeof(length) === "string")
-        ([length, encoding]) = [null, length];
-
-      offset = ~~offset;
-      length = length || this.length - offset;
-      let buffer = TextEncoder(encoding).encode(string);
-      let result = Math.min(buffer.length, length);
-      if (buffer.length !== length)
-        buffer = buffer.subarray(0, length);
-      Uint8Array.set(this, buffer, offset);
-      return result;
-    }
+  toJSON: function toJSON() {
+    return this.toString()
   },
-  fill: {
-    value: function fill(value, start, end) {
-      value = value || 0;
-      start = start || 0;
-      end = end || this.length;
-
-      if (typeof(value) === "string")
-        value = value.charCodeAt(0);
-      if (typeof(value) !== "number" || isNaN(value))
-        throw TypeError("value is not a number");
-      if (end < start)
-        throw new RangeError("end < start");
-
-      // Fill 0 bytes; we're done
-      if (end === start)
-        return 0;
-      if (this.length == 0)
-        return 0;
-
-      if (start < 0 || start >= this.length)
-        throw RangeError("start out of bounds");
-
-      if (end < 0 || end > this.length)
-        throw RangeError("end out of bounds");
-
-      let index = start;
-      while (index < end) this[index++] = value;
-    }
+  write: function write(string, offset, encoding) {
+    offset = Math.max(offset || 0, 0);
+    let value = this.valueOf();
+    let transcoder = Transcoder();
+    transcoder.charset = String(encoding || this.encoding).toUpperCase();
+    let bytes = transcoder.convertToByteArray(string, {});
+    value.splice.apply(value, [
+      offset,
+      Math.min(value.length - offset, bytes.length, bytes)
+    ].concat(bytes));
+    return bytes;
+  },
+  slice: function slice(start, end) {
+    return new Buffer(this.valueOf().slice(start, end));
+  },
+  copy: function copy(target, offset, start, end) {
+    offset = Math.max(offset || 0, 0);
+    target = target.valueOf();
+    let bytes = this.valueOf();
+    bytes.slice(Math.max(start || 0, 0), end);
+    target.splice.apply(target, [
+      offset,
+      Math.min(target.length - offset, bytes.length),
+    ].concat(bytes));
   }
 });
-
-// Define nodejs Buffer's getter and setter functions that just proxy
-// to internal DataView's equivalent methods.
-[["readUInt16LE", "getUint16", true],
- ["readUInt16BE", "getUint16", false],
- ["readInt16LE", "getInt16", true],
- ["readInt16BE", "getInt16", false],
- ["readUInt32LE", "getInt32", true],
- ["readUInt32BE", "getInt32", false],
- ["readInt32LE", "getInt32", true],
- ["readInt32BE", "getInt32", false],
- ["readFloatLE", "getFloat32", true],
- ["readFloatBE", "getFloat32", false],
- ["readDoubleLE", "getFloat64", true],
- ["readDoubleBE", "getFloat64", false],
- ["readUInt8", "getUint8"],
- ["readInt8", "getInt8"]].forEach(([alias, name, littleEndian]) => {
-  Object.defineProperty(Buffer.prototype, alias, {
-    value: function(offset) this.view[name](offset, littleEndian)
-  });
-});
-
-[["writeUInt16LE", "setUint16", true],
- ["writeUInt16BE", "setUint16", false],
- ["writeInt16LE", "setInt16", true],
- ["writeInt16BE", "setInt16", false],
- ["writeUInt32LE", "setUint32", true],
- ["writeUInt32BE", "setUint32", false],
- ["writeInt32LE", "setInt32", true],
- ["writeInt32BE", "setInt32", false],
- ["writeFloatLE", "setFloat32", true],
- ["writeFloatBE", "setFloat32", false],
- ["writeDoubleLE", "setFloat64", true],
- ["writeDoubleBE", "setFloat64", false],
- ["writeUInt8", "setUint8"],
- ["writeInt8", "setInt8"]].forEach(([alias, name, littleEndian]) => {
-  Object.defineProperty(Buffer.prototype, alias, {
-    value: function(value, offset) this.view[name](offset, value, littleEndian)
-  });
-});
-
-
-// List of supported encodings taken from:
-// http://mxr.mozilla.org/mozilla-central/source/dom/encoding/labelsencodings.properties
-const ENCODINGS = { "unicode-1-1-utf-8": 1, "utf-8": 1, "utf8": 1,
-  "866": 1, "cp866": 1, "csibm866": 1, "ibm866": 1, "csisolatin2": 1,
-  "iso-8859-2": 1, "iso-ir-101": 1, "iso8859-2": 1, "iso88592": 1,
-  "iso_8859-2": 1, "iso_8859-2:1987": 1, "l2": 1, "latin2": 1, "csisolatin3": 1,
-  "iso-8859-3": 1, "iso-ir-109": 1, "iso8859-3": 1, "iso88593": 1,
-  "iso_8859-3": 1, "iso_8859-3:1988": 1, "l3": 1, "latin3": 1, "csisolatin4": 1,
-  "iso-8859-4": 1, "iso-ir-110": 1, "iso8859-4": 1, "iso88594": 1,
-  "iso_8859-4": 1, "iso_8859-4:1988": 1, "l4": 1, "latin4": 1,
-  "csisolatincyrillic": 1, "cyrillic": 1, "iso-8859-5": 1, "iso-ir-144": 1,
-  "iso8859-5": 1, "iso88595": 1, "iso_8859-5": 1, "iso_8859-5:1988": 1,
-  "arabic": 1, "asmo-708": 1, "csiso88596e": 1, "csiso88596i": 1,
-  "csisolatinarabic": 1, "ecma-114": 1, "iso-8859-6": 1, "iso-8859-6-e": 1,
-  "iso-8859-6-i": 1, "iso-ir-127": 1, "iso8859-6": 1, "iso88596": 1,
-  "iso_8859-6": 1, "iso_8859-6:1987": 1, "csisolatingreek": 1, "ecma-118": 1,
-  "elot_928": 1, "greek": 1, "greek8": 1, "iso-8859-7": 1, "iso-ir-126": 1,
-  "iso8859-7": 1, "iso88597": 1, "iso_8859-7": 1, "iso_8859-7:1987": 1,
-  "sun_eu_greek": 1, "csiso88598e": 1, "csisolatinhebrew": 1, "hebrew": 1,
-  "iso-8859-8": 1, "iso-8859-8-e": 1, "iso-ir-138": 1, "iso8859-8": 1,
-  "iso88598": 1, "iso_8859-8": 1, "iso_8859-8:1988": 1, "visual": 1,
-  "csiso88598i": 1, "iso-8859-8-i": 1, "logical": 1, "csisolatin6": 1,
-  "iso-8859-10": 1, "iso-ir-157": 1, "iso8859-10": 1, "iso885910": 1,
-  "l6": 1, "latin6": 1, "iso-8859-13": 1, "iso8859-13": 1, "iso885913": 1,
-  "iso-8859-14": 1, "iso8859-14": 1, "iso885914": 1, "csisolatin9": 1,
-  "iso-8859-15": 1, "iso8859-15": 1, "iso885915": 1, "iso_8859-15": 1,
-  "l9": 1, "iso-8859-16": 1, "cskoi8r": 1, "koi": 1, "koi8": 1, "koi8-r": 1,
-  "koi8_r": 1, "koi8-u": 1, "csmacintosh": 1, "mac": 1, "macintosh": 1,
-  "x-mac-roman": 1, "dos-874": 1, "iso-8859-11": 1, "iso8859-11": 1,
-  "iso885911": 1, "tis-620": 1, "windows-874": 1, "cp1250": 1,
-  "windows-1250": 1, "x-cp1250": 1, "cp1251": 1, "windows-1251": 1,
-  "x-cp1251": 1, "ansi_x3.4-1968": 1, "ascii": 1, "cp1252": 1, "cp819": 1,
-  "csisolatin1": 1, "ibm819": 1, "iso-8859-1": 1, "iso-ir-100": 1,
-  "iso8859-1": 1, "iso88591": 1, "iso_8859-1": 1, "iso_8859-1:1987": 1,
-  "l1": 1, "latin1": 1, "us-ascii": 1, "windows-1252": 1, "x-cp1252": 1,
-  "cp1253": 1, "windows-1253": 1, "x-cp1253": 1, "cp1254": 1, "csisolatin5": 1,
-  "iso-8859-9": 1, "iso-ir-148": 1, "iso8859-9": 1, "iso88599": 1,
-  "iso_8859-9": 1, "iso_8859-9:1989": 1, "l5": 1, "latin5": 1,
-  "windows-1254": 1, "x-cp1254": 1, "cp1255": 1, "windows-1255": 1,
-  "x-cp1255": 1, "cp1256": 1, "windows-1256": 1, "x-cp1256": 1, "cp1257": 1,
-  "windows-1257": 1, "x-cp1257": 1, "cp1258": 1, "windows-1258": 1,
-  "x-cp1258": 1, "x-mac-cyrillic": 1, "x-mac-ukrainian": 1, "chinese": 1,
-  "csgb2312": 1, "csiso58gb231280": 1, "gb2312": 1, "gb_2312": 1,
-  "gb_2312-80": 1, "gbk": 1, "iso-ir-58": 1, "x-gbk": 1, "gb18030": 1,
-  "hz-gb-2312": 1, "big5": 1, "big5-hkscs": 1, "cn-big5": 1, "csbig5": 1,
-  "x-x-big5": 1, "cseucpkdfmtjapanese": 1, "euc-jp": 1, "x-euc-jp": 1,
-  "csiso2022jp": 1, "iso-2022-jp": 1, "csshiftjis": 1, "ms_kanji": 1,
-  "shift-jis": 1, "shift_jis": 1, "sjis": 1, "windows-31j": 1, "x-sjis": 1,
-  "cseuckr": 1, "csksc56011987": 1, "euc-kr": 1, "iso-ir-149": 1, "korean": 1,
-  "ks_c_5601-1987": 1, "ks_c_5601-1989": 1, "ksc5601": 1, "ksc_5601": 1,
-  "windows-949": 1, "csiso2022kr": 1, "iso-2022-kr": 1, "utf-16": 1,
-  "utf-16le": 1, "utf-16be": 1, "x-user-defined": 1 };
+Buffer.isBuffer = function isBuffer(buffer) {
+  return buffer instanceof Buffer
+};
+exports.Buffer = Buffer;
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -7,22 +7,20 @@
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, CC } = require("chrome");
 
 const { setTimeout } = require("../timers");
 const { Stream, InputStream, OutputStream } = require("./stream");
-const { emit, on } = require("../event/core");
 const { Buffer } = require("./buffer");
 const { ns } = require("../core/namespace");
 const { Class } = require("../core/heritage");
 
-
 const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
                         "initWithPath");
 const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1",
                             "nsIFileOutputStream", "init");
 const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
                            "nsIFileInputStream", "init");
 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
                              "nsIBinaryInputStream", "setInputStream");
@@ -30,18 +28,16 @@ const BinaryOutputStream = CC("@mozilla.
                               "nsIBinaryOutputStream", "setOutputStream");
 const StreamPump = CC("@mozilla.org/network/input-stream-pump;1",
                       "nsIInputStreamPump", "init");
 
 const { createOutputTransport, createInputTransport } =
   Cc["@mozilla.org/network/stream-transport-service;1"].
   getService(Ci.nsIStreamTransportService);
 
-const { OPEN_UNBUFFERED } = Ci.nsITransport;
-
 
 const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream;
 const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile;
 const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream;
 
 const FILE_PERMISSION = parseInt("0666", 8);
 const PR_UINT32_MAX = 0xfffffff;
 // Values taken from:
@@ -156,26 +152,24 @@ const ReadStream = Class({
       input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
     // We use `nsIStreamTransportService` service to transform blocking
     // file input stream into a fully asynchronous stream that can be written
     // without blocking the main thread.
     let transport = createInputTransport(input, position, length, false);
     // Open an input stream on a transport. We don"t pass flags to guarantee
     // non-blocking stream semantics. Also we use defaults for segment size &
     // count.
-    InputStream.prototype.initialize.call(this, {
-      asyncInputStream: transport.openInputStream(null, 0, 0)
-    });
+    let asyncInputStream = transport.openInputStream(null, 0, 0);
+    let binaryInputStream = BinaryInputStream(asyncInputStream);
+    nsIBinaryInputStream(fd, binaryInputStream);
+    let pump = StreamPump(asyncInputStream, position, length, 0, 0, false);
 
-    // Close file descriptor on end and destroy the stream.
-    on(this, "end", _ => {
-      this.destroy();
-      emit(this, "close");
+    InputStream.prototype.initialize.call(this, {
+      input: binaryInputStream, pump: pump
     });
-
     this.read();
   },
   destroy: function() {
     closeSync(this.fd);
     InputStream.prototype.destroy.call(this);
   }
 });
 exports.ReadStream = ReadStream;
@@ -212,30 +206,31 @@ const WriteStream = Class({
       output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
     // We use `nsIStreamTransportService` service to transform blocking
     // file output stream into a fully asynchronous stream that can be written
     // without blocking the main thread.
     let transport = createOutputTransport(output, position, -1, false);
     // Open an output stream on a transport. We don"t pass flags to guarantee
     // non-blocking stream semantics. Also we use defaults for segment size &
     // count.
-    OutputStream.prototype.initialize.call(this, {
-      asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0),
-      output: output
-    });
+    let asyncOutputStream = transport.openOutputStream(null, 0, 0);
+    // Finally we create a non-blocking binary output stream. This will allows
+    // us to write buffers as byte arrays without any further transcoding.
+    let binaryOutputStream = BinaryOutputStream(asyncOutputStream);
+    nsIBinaryOutputStream(fd, binaryOutputStream);
 
-    // For write streams "finish" basically means close.
-    on(this, "finish", _ => {
-       this.destroy();
-       emit(this, "close");
+    // Storing output stream so that it can beaccessed later.
+    OutputStream.prototype.initialize.call(this, {
+      output: binaryOutputStream,
+      asyncOutputStream: asyncOutputStream
     });
   },
   destroy: function() {
+    closeSync(this.fd);
     OutputStream.prototype.destroy.call(this);
-    closeSync(this.fd);
   }
 });
 exports.WriteStream = WriteStream;
 exports.createWriteStream = function createWriteStream(path, options) {
   return new WriteStream(path, options);
 };
 
 const Stats = Class({
@@ -365,17 +360,17 @@ exports.truncate = truncate;
 
 function ftruncate(fd, length, callback) {
   write(fd, new Buffer(length), 0, length, 0, function(error) {
     callback(error);
   });
 }
 exports.ftruncate = ftruncate;
 
-function ftruncateSync(fd, length = 0) {
+function ftruncateSync(fd, length) {
   writeSync(fd, new Buffer(length), 0, length, 0);
 }
 exports.ftruncateSync = ftruncateSync;
 
 function chownSync(path, uid, gid) {
   throw Error("Not implemented yet!!");
 }
 exports.chownSync = chownSync;
@@ -634,18 +629,16 @@ exports.close = close;
 
 /**
  * Synchronous open(2).
  */
 function openSync(path, flags, mode) {
   let [ fd, flags, mode, file ] =
       [ { path: path }, Flags(flags), Mode(mode), nsILocalFile(path) ];
 
-  nsIFile(fd, file);
-
   // If trying to open file for just read that does not exists
   // need to throw exception as node does.
   if (!file.exists() && !isWritable(flags))
     throw FSError("open", "ENOENT", 34, path);
 
   // If we want to open file in read mode we initialize input stream.
   if (isReadable(flags)) {
     let input = FileInputStream(file, flags, mode, DEFER_OPEN);
@@ -677,19 +670,17 @@ function writeSync(fd, buffer, offset, l
   if (length + offset > buffer.length) {
     throw Error("Length is extends beyond buffer");
   }
   else if (length + offset !== buffer.length) {
     buffer = buffer.slice(offset, offset + length);
   }
   let writeStream = new WriteStream(fd, { position: position,
                                           length: length });
-
-  let output = BinaryOutputStream(nsIFileOutputStream(fd));
-  nsIBinaryOutputStream(fd, output);
+  let output = nsIBinaryOutputStream(fd);
   // We write content as a byte array as this will avoid any transcoding
   // if content was a buffer.
   output.writeByteArray(buffer.valueOf(), buffer.length);
   output.flush();
 };
 exports.writeSync = writeSync;
 
 /**
@@ -740,24 +731,20 @@ function readSync(fd, buffer, offset, le
   // Setting a stream position, unless it"s `-1` which means current position.
   if (position >= 0)
     input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
   // We use `nsIStreamTransportService` service to transform blocking
   // file input stream into a fully asynchronous stream that can be written
   // without blocking the main thread.
   let binaryInputStream = BinaryInputStream(input);
   let count = length === ALL ? binaryInputStream.available() : length;
-  if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer);
-  else {
-    let chunk = new Buffer(count);
-    binaryInputStream.readArrayBuffer(count, chunk.buffer);
-    chunk.copy(buffer, offset);
-  }
+  var bytes = binaryInputStream.readByteArray(count);
+  buffer.copy.call(bytes, buffer, offset);
 
-  return buffer.slice(offset, offset + count);
+  return bytes;
 };
 exports.readSync = readSync;
 
 /**
  * Read data from the file specified by `fd`.
  *
  * `buffer` is the buffer that the data will be written to.
  * `offset` is offset within the buffer where writing will start.
@@ -767,19 +754,19 @@ exports.readSync = readSync;
  * `position` is an integer specifying where to begin reading from in the file.
  * If `position` is `null`, data will be read from the current file position.
  *
  * The callback is given the three arguments, `(error, bytesRead, buffer)`.
  */
 function read(fd, buffer, offset, length, position, callback) {
   let bytesRead = 0;
   let readStream = new ReadStream(fd, { position: position, length: length });
-  readStream.on("data", function onData(data) {
-      data.copy(buffer, offset + bytesRead);
-      bytesRead += data.length;
+  readStream.on("data", function onData(chunck) {
+      chunck.copy(buffer, offset + bytesRead);
+      bytesRead += chunck.length;
   });
   readStream.on("end", function onEnd() {
     callback(null, bytesRead, buffer);
     readStream.destroy();
   });
 };
 exports.read = read;
 
@@ -789,52 +776,44 @@ exports.read = read;
  * contents of the file.
  */
 function readFile(path, encoding, callback) {
   if (isFunction(encoding)) {
     callback = encoding
     encoding = null
   }
 
-  let buffer = null;
+  let buffer = new Buffer();
   try {
     let readStream = new ReadStream(path);
-    readStream.on("data", function(data) {
-      if (!buffer) buffer = data;
-      else {
-        let bufferred = buffer
-        buffer = new Buffer(buffer.length + data.length);
-        bufferred.copy(buffer, 0);
-        data.copy(buffer, bufferred.length);
-      }
+    readStream.on("data", function(chunck) {
+      chunck.copy(buffer, buffer.length);
     });
     readStream.on("error", function onError(error) {
       callback(error);
+      readStream.destroy();
     });
     readStream.on("end", function onEnd() {
-      // Note: Need to destroy before invoking a callback
-      // so that file descriptor is released.
+      callback(null, buffer);
       readStream.destroy();
-      callback(null, buffer);
     });
   } catch (error) {
     setTimeout(callback, 0, error);
   }
 };
 exports.readFile = readFile;
 
 /**
  * Synchronous version of `fs.readFile`. Returns the contents of the path.
  * If encoding is specified then this function returns a string.
  * Otherwise it returns a buffer.
  */
 function readFileSync(path, encoding) {
+  let buffer = new Buffer();
   let fd = openSync(path, "r");
-  let size = fstatSync(fd).size;
-  let buffer = new Buffer(size);
   try {
     readSync(fd, buffer, 0, ALL, 0);
   }
   finally {
     closeSync(fd);
   }
   return buffer;
 };
@@ -849,26 +828,23 @@ function writeFile(path, content, encodi
     if (isFunction(encoding)) {
       callback = encoding
       encoding = null
     }
     if (isString(content))
       content = new Buffer(content, encoding);
 
     let writeStream = new WriteStream(path);
-    let error = null;
-
-    writeStream.end(content, function() {
-      writeStream.destroy();
+    writeStream.on("error", function onError(error) {
       callback(error);
+      writeStream.destroy();
     });
-
-    writeStream.on("error", function onError(reason) {
-      error = reason;
+    writeStream.write(content, function onDrain() {
       writeStream.destroy();
+      callback(null);
     });
   } catch (error) {
     callback(error);
   }
 };
 exports.writeFile = writeFile;
 
 /**
--- a/addon-sdk/source/lib/sdk/io/stream.js
+++ b/addon-sdk/source/lib/sdk/io/stream.js
@@ -3,55 +3,109 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { CC, Cc, Ci, Cu, Cr, components } = require("chrome");
 const { EventTarget } = require("../event/target");
 const { emit } = require("../event/core");
 const { Buffer } = require("./buffer");
 const { Class } = require("../core/heritage");
 const { setTimeout } = require("../timers");
-
-
-const MultiplexInputStream = CC("@mozilla.org/io/multiplex-input-stream;1",
-                                "nsIMultiplexInputStream");
-const AsyncStreamCopier = CC("@mozilla.org/network/async-stream-copier;1",
-                             "nsIAsyncStreamCopier", "init");
-const StringInputStream = CC("@mozilla.org/io/string-input-stream;1",
-                             "nsIStringInputStream");
-const ArrayBufferInputStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
-                                  "nsIArrayBufferInputStream");
+const { ns } = require("../core/namespace");
 
-const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
-                             "nsIBinaryInputStream", "setInputStream");
-const InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1",
-                           "nsIInputStreamPump", "init");
-
-const threadManager = Cc["@mozilla.org/thread-manager;1"].
-                      getService(Ci.nsIThreadManager);
-
-const eventTarget = Cc["@mozilla.org/network/socket-transport-service;1"].
-                    getService(Ci.nsIEventTarget);
-
-let isFunction = value => typeof(value) === "function"
+function isFunction(value) typeof value === "function"
 
 function accessor() {
   let map = new WeakMap();
-  return function(target, value) {
-    if (value)
-      map.set(target, value);
-    return map.get(target);
+  return function(fd, value) {
+    if (value === null) map.delete(fd);
+    if (value !== undefined) map.set(fd, value);
+    return map.get(fd);
   }
 }
 
+let nsIInputStreamPump = accessor();
+let nsIAsyncOutputStream = accessor();
+let nsIInputStream = accessor();
+let nsIOutputStream = accessor();
+
+
+/**
+ * Utility function / hack that we use to figure if output stream is closed.
+ */
+function isClosed(stream) {
+  // We assume that stream is not closed.
+  let isClosed = false;
+  stream.asyncWait({
+    // If `onClose` callback is called before outer function returns
+    // (synchronously) `isClosed` will be set to `true` identifying
+    // that stream is closed.
+    onOutputStreamReady: function onClose() isClosed = true
+
+  // `WAIT_CLOSURE_ONLY` flag overrides the default behavior, causing the
+  // `onOutputStreamReady` notification to be suppressed until the stream
+  // becomes closed.
+  }, stream.WAIT_CLOSURE_ONLY, 0, null);
+  return isClosed;
+}
+/**
+ * Utility function takes output `stream`, `onDrain`, `onClose` callbacks and
+ * calls one of this callbacks depending on stream state. It is guaranteed
+ * that only one called will be called and it will be called asynchronously.
+ * @param {nsIAsyncOutputStream} stream
+ * @param {Function} onDrain
+ *    callback that is called when stream becomes writable.
+ * @param {Function} onClose
+ *    callback that is called when stream becomes closed.
+ */
+function onStateChange(stream, target) {
+  let isAsync = false;
+  stream.asyncWait({
+    onOutputStreamReady: function onOutputStreamReady() {
+      // If `isAsync` was not yet set to `true` by the last line we know that
+      // `onOutputStreamReady` was called synchronously. In such case we just
+      // defer execution until next turn of event loop.
+      if (!isAsync)
+        return setTimeout(onOutputStreamReady, 0);
+
+      // As it"s not clear what is a state of the stream (TODO: Is there really
+      // no better way ?) we employ hack (see details in `isClosed`) to verify
+      // if stream is closed.
+      emit(target, isClosed(stream) ? "close" : "drain");
+    }
+  }, 0, 0, null);
+  isAsync = true;
+}
+
+function pump(stream) {
+  let input = nsIInputStream(stream);
+  nsIInputStreamPump(stream).asyncRead({
+    onStartRequest: function onStartRequest() {
+      emit(stream, "start");
+    },
+    onDataAvailable: function onDataAvailable(req, c, is, offset, count) {
+      try {
+        let bytes = input.readByteArray(count);
+        emit(stream, "data", new Buffer(bytes, stream.encoding));
+      } catch (error) {
+        emit(stream, "error", error);
+        stream.readable = false;
+      }
+    },
+    onStopRequest: function onStopRequest() {
+      stream.readable = false;
+      emit(stream, "end");
+    }
+  }, null);
+}
+
 const Stream = Class({
   extends: EventTarget,
   initialize: function() {
     this.readable = false;
     this.writable = false;
     this.encoding = null;
   },
   setEncoding: function setEncoding(encoding) {
@@ -61,18 +115,17 @@ const Stream = Class({
     let source = this;
     function onData(chunk) {
       if (target.writable) {
         if (false === target.write(chunk))
           source.pause();
       }
     }
     function onDrain() {
-      if (source.readable)
-        source.resume();
+      if (source.readable) source.resume();
     }
     function onEnd() {
       target.end();
     }
     function onPause() {
       source.pause();
     }
     function onResume() {
@@ -118,320 +171,154 @@ const Stream = Class({
     emit(this, "resume");
   },
   destroySoon: function destroySoon() {
     this.destroy();
   }
 });
 exports.Stream = Stream;
 
-
-let nsIStreamListener = accessor();
-let nsIInputStreamPump = accessor();
-let nsIAsyncInputStream = accessor();
-let nsIBinaryInputStream = accessor();
-
-const StreamListener = Class({
-  initialize: function(stream) {
-    this.stream = stream;
-  },
-
-  // Next three methods are part of `nsIStreamListener` interface and are
-  // invoked by `nsIInputStreamPump.asyncRead`.
-  onDataAvailable: function(request, context, input, offset, count) {
-    let stream = this.stream;
-    let buffer = new ArrayBuffer(count);
-    nsIBinaryInputStream(stream).readArrayBuffer(count, buffer);
-    emit(stream, "data", new Buffer(buffer, stream.encoding));
-  },
-
-  // Next two methods implement `nsIRequestObserver` interface and are invoked
-  // by `nsIInputStreamPump.asyncRead`.
-  onStartRequest: function() {},
-  // Called to signify the end of an asynchronous request. We only care to
-  // discover errors.
-  onStopRequest: function(request, context, status) {
-    let stream = this.stream;
-    stream.readable = false;
-    if (!components.isSuccessCode(status))
-      emit(stream, "error", status);
-    else
-      emit(stream, "end");
-  }
-});
-
-
 const InputStream = Class({
   extends: Stream,
-  readable: false,
-  paused: false,
   initialize: function initialize(options) {
-    let { asyncInputStream } = options;
+    let { input, pump } = options;
 
     this.readable = true;
-
-    let binaryInputStream = new BinaryInputStream(asyncInputStream);
-    let inputStreamPump = new InputStreamPump(asyncInputStream,
-                                              -1, -1, 0, 0, false);
-    let streamListener = new StreamListener(this);
-
-    nsIAsyncInputStream(this, asyncInputStream);
-    nsIInputStreamPump(this, inputStreamPump);
-    nsIBinaryInputStream(this, binaryInputStream);
-    nsIStreamListener(this, streamListener);
-
-    this.asyncInputStream = asyncInputStream;
-    this.inputStreamPump = inputStreamPump;
-    this.binaryInputStream = binaryInputStream;
+    this.paused = false;
+    nsIInputStream(this, input);
+    nsIInputStreamPump(this, pump);
   },
   get status() nsIInputStreamPump(this).status,
-  read: function() {
-    nsIInputStreamPump(this).asyncRead(nsIStreamListener(this), null);
-  },
+  read: function() pump(this),
   pause: function pause() {
     this.paused = true;
     nsIInputStreamPump(this).suspend();
     emit(this, "paused");
   },
   resume: function resume() {
     this.paused = false;
     nsIInputStreamPump(this).resume();
     emit(this, "resume");
   },
-  close: function close() {
+  destroy: function destroy() {
     this.readable = false;
-    nsIInputStreamPump(this).cancel(Cr.NS_OK);
-    nsIBinaryInputStream(this).close();
-    nsIAsyncInputStream(this).close();
-  },
-  destroy: function destroy() {
-    this.close();
+    try {
+      emit(this, "close", null);
+      nsIInputStreamPump(this).cancel(null);
+      nsIInputStreamPump(this, null);
 
-    nsIInputStreamPump(this);
-    nsIAsyncInputStream(this);
-    nsIBinaryInputStream(this);
-    nsIStreamListener(this);
+      nsIInputStream(this).close();
+      nsIInputStream(this, null);
+    } catch (error) {
+      emit(this, "error", error);
+    }
   }
 });
 exports.InputStream = InputStream;
 
-
-
-let nsIRequestObserver = accessor();
-let nsIAsyncOutputStream = accessor();
-let nsIAsyncStreamCopier = accessor();
-let nsIMultiplexInputStream = accessor();
-
-const RequestObserver = Class({
-  initialize: function(stream) {
-    this.stream = stream;
-  },
-  // Method is part of `nsIRequestObserver` interface that is
-  // invoked by `nsIAsyncStreamCopier.asyncCopy`.
-  onStartRequest: function() {},
-  // Method is part of `nsIRequestObserver` interface that is
-  // invoked by `nsIAsyncStreamCopier.asyncCopy`.
-  onStopRequest: function(request, context, status) {
-    let stream = this.stream;
-    stream.drained = true;
-
-    // Remove copied chunk.
-    let multiplexInputStream = nsIMultiplexInputStream(stream);
-    multiplexInputStream.removeStream(0);
-
-    // If there was an error report.
-    if (!components.isSuccessCode(status))
-      emit(stream, "error", status);
-
-    // If there more chunks in queue then flush them.
-    else if (multiplexInputStream.count)
-      stream.flush();
-
-    // If stream is still writable notify that queue has drained.
-    else if (stream.writable)
-      emit(stream, "drain");
-
-    // If stream is no longer writable close it.
-    else {
-      nsIAsyncStreamCopier(stream).cancel(Cr.NS_OK);
-      nsIMultiplexInputStream(stream).close();
-      nsIAsyncOutputStream(stream).close();
-      nsIAsyncOutputStream(stream).flush();
-    }
-  }
-});
-
-const OutputStreamCallback = Class({
-  initialize: function(stream) {
-    this.stream = stream;
-  },
-  // Method is part of `nsIOutputStreamCallback` interface that
-  // is invoked by `nsIAsyncOutputStream.asyncWait`. It is registered
-  // with `WAIT_CLOSURE_ONLY` flag that overrides the default behavior,
-  // causing the `onOutputStreamReady` notification to be suppressed until
-  // the stream becomes closed.
-  onOutputStreamReady: function(nsIAsyncOutputStream) {
-    emit(this.stream, "finish");
-  }
-});
-
 const OutputStream = Class({
   extends: Stream,
-  writable: false,
-  drained: true,
-  get bufferSize() {
-    let multiplexInputStream = nsIMultiplexInputStream(this);
-    return multiplexInputStream && multiplexInputStream.available();
-  },
   initialize: function initialize(options) {
-    let { asyncOutputStream, output } = options;
-    this.writable = true;
-
-    // Ensure that `nsIAsyncOutputStream` was provided.
-    asyncOutputStream.QueryInterface(Ci.nsIAsyncOutputStream);
+    let { output, asyncOutputStream } = options;
 
-    // Create a `nsIMultiplexInputStream` and `nsIAsyncStreamCopier`. Former
-    // is used to queue written data chunks that `asyncStreamCopier` will
-    // asynchronously drain into `asyncOutputStream`.
-    let multiplexInputStream = MultiplexInputStream();
-    let asyncStreamCopier = AsyncStreamCopier(multiplexInputStream,
-                                              output || asyncOutputStream,
-                                              eventTarget,
-                                              // nsIMultiplexInputStream
-                                              // implemnts .readSegments()
-                                              true,
-                                              // nsIOutputStream may or
-                                              // may not implemnet
-                                              // .writeSegments().
-                                              false,
-                                              // Use default buffer size.
-                                              null,
-                                              // Should not close an input.
-                                              false,
-                                              // Should not close an output.
-                                              false);
-
-    // Create `requestObserver` implementing `nsIRequestObserver` interface
-    // in the constructor that's gonna be reused across several flushes.
-    let requestObserver = RequestObserver(this);
-
-
-    // Create observer that implements `nsIOutputStreamCallback` and register
-    // using `WAIT_CLOSURE_ONLY` flag. That way it will be notfied once
-    // `nsIAsyncOutputStream` is closed.
-    asyncOutputStream.asyncWait(OutputStreamCallback(this),
-                                asyncOutputStream.WAIT_CLOSURE_ONLY,
-                                0,
-                                threadManager.currentThread);
-
-    nsIRequestObserver(this, requestObserver);
+    this.writable = true;
+    nsIOutputStream(this, output);
     nsIAsyncOutputStream(this, asyncOutputStream);
-    nsIMultiplexInputStream(this, multiplexInputStream);
-    nsIAsyncStreamCopier(this, asyncStreamCopier);
-
-    this.asyncOutputStream = asyncOutputStream;
-    this.multiplexInputStream = multiplexInputStream;
-    this.asyncStreamCopier = asyncStreamCopier;
   },
   write: function write(content, encoding, callback) {
+    let output = nsIOutputStream(this);
+    let asyncOutputStream = nsIAsyncOutputStream(this);
+
     if (isFunction(encoding)) {
       callback = encoding;
       encoding = callback;
     }
 
+    // Flag indicating whether or not content has been flushed to the kernel
+    // buffer.
+    let isWritten = false;
     // If stream is not writable we throw an error.
-    if (!this.writable) throw Error("stream is not writable");
-
-    let chunk = null;
+    if (!this.writable)
+      throw Error("stream not writable");
 
-    // If content is not a buffer then we create one out of it.
-    if (Buffer.isBuffer(content)) {
-      chunk = new ArrayBufferInputStream();
-      chunk.setData(content.buffer, 0, content.length);
-    }
-    else {
-      chunk = new StringInputStream();
-      chunk.setData(content, content.length);
-    }
+    try {
+      // If content is not a buffer then we create one out of it.
+      if (!Buffer.isBuffer(content))
+        content = new Buffer(content, encoding);
 
-    if (callback)
-      this.once("drain", callback);
-
-    // Queue up chunk to be copied to output sync.
-    nsIMultiplexInputStream(this).appendStream(chunk);
-    this.flush();
+      // We write content as a byte array as this will avoid any transcoding
+      // if content was a buffer.
+      output.writeByteArray(content.valueOf(), content.length);
+      output.flush();
 
-    return this.drained;
+      if (callback) this.once("drain", callback);
+      onStateChange(asyncOutputStream, this);
+      return true;
+    } catch (error) {
+      // If errors occur we emit appropriate event.
+      emit(this, "error", error);
+    }
   },
-  flush: function() {
-    if (this.drained) {
-      this.drained = false;
-      nsIAsyncStreamCopier(this).asyncCopy(nsIRequestObserver(this), null);
-    }
+  flush: function flush() {
+    nsIOutputStream(this).flush();
   },
   end: function end(content, encoding, callback) {
     if (isFunction(content)) {
       callback = content
       content = callback
     }
     if (isFunction(encoding)) {
       callback = encoding
       encoding = callback
     }
 
-    // Setting a listener to "finish" event if passed.
+    // Setting a listener to "close" event if passed.
     if (isFunction(callback))
-      this.once("finish", callback);
-
-
-    if (content)
-      this.write(content, encoding);
-    this.writable = false;
+      this.once("close", callback);
 
-    // Close `asyncOutputStream` only if output has drained. If it's
-    // not drained than `asyncStreamCopier` is busy writing, so let
-    // it finish. Note that since `this.writable` is false copier will
-    // close `asyncOutputStream` once output drains.
-    if (this.drained)
-      nsIAsyncOutputStream(this).close();
+    // If content is passed then we defer closing until we finish with writing.
+    if (content)
+      this.write(content, encoding, end.bind(this));
+    // If we don"t write anything, then we close an outputStream.
+    else
+      nsIOutputStream(this).close();
   },
-  destroy: function destroy() {
-    nsIAsyncOutputStream(this).close();
-    nsIAsyncOutputStream(this);
-    nsIMultiplexInputStream(this);
-    nsIAsyncStreamCopier(this);
-    nsIRequestObserver(this);
+  destroy: function destroy(callback) {
+    try {
+      this.end(callback);
+      nsIOutputStream(this, null);
+      nsIAsyncOutputStream(this, null);
+    } catch (error) {
+      emit(this, "error", error);
+    }
   }
 });
 exports.OutputStream = OutputStream;
 
 const DuplexStream = Class({
   extends: Stream,
-  implements: [InputStream, OutputStream],
-  allowHalfOpen: true,
   initialize: function initialize(options) {
-    options = options || {};
-    let { readable, writable, allowHalfOpen } = options;
+    let { input, output, pump } = options;
 
-    InputStream.prototype.initialize.call(this, options);
-    OutputStream.prototype.initialize.call(this, options);
-
-    if (readable === false)
-      this.readable = false;
+    this.writable = true;
+    this.readable = true;
+    this.encoding = null;
 
-    if (writable === false)
-      this.writable = false;
-
-    if (allowHalfOpen === false)
-      this.allowHalfOpen = false;
+    nsIInputStream(this, input);
+    nsIOutputStream(this, output);
+    nsIInputStreamPump(this, pump);
+  },
+  read: InputStream.prototype.read,
+  pause: InputStream.prototype.pause,
+  resume: InputStream.prototype.resume,
 
-    // If in a half open state and it's disabled enforce end.
-    this.once("end", () => {
-      if (!this.allowHalfOpen && (!this.readable || !this.writable))
-        this.end();
-    });
-  },
+  write: OutputStream.prototype.write,
+  flush: OutputStream.prototype.flush,
+  end: OutputStream.prototype.end,
+
   destroy: function destroy(error) {
+    if (error)
+      emit(this, "error", error);
     InputStream.prototype.destroy.call(this);
     OutputStream.prototype.destroy.call(this);
   }
 });
 exports.DuplexStream = DuplexStream;
--- a/addon-sdk/source/lib/sdk/lang/functional.js
+++ b/addon-sdk/source/lib/sdk/lang/functional.js
@@ -7,17 +7,17 @@
 // those goes to him.
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { setImmediate, setTimeout } = require("../timers");
+const { setTimeout } = require("../timers");
 const { deprecateFunction } = require("../util/deprecate");
 
 /**
  * Takes `lambda` function and returns a method. When returned method is
  * invoked it calls wrapped `lambda` and passes `this` as a first argument
  * and given argument as rest.
  */
 function method(lambda) {
@@ -25,22 +25,23 @@ function method(lambda) {
     return lambda.apply(null, [this].concat(Array.slice(arguments)));
   }
 }
 exports.method = method;
 
 /**
  * Takes a function and returns a wrapped one instead, calling which will call
  * original function in the next turn of event loop. This is basically utility
- * to do `setImmediate(function() { ... })`, with a difference that returned
+ * to do `setTimeout(function() { ... }, 0)`, with a difference that returned
  * function is reused, instead of creating a new one each time. This also allows
  * to use this functions as event listeners.
  */
 function defer(f) {
-  return function deferred() setImmediate(invoke, f, arguments, this);
+  return function deferred()
+    setTimeout(invoke, 0, f, arguments, this);
 }
 exports.defer = defer;
 // Exporting `remit` alias as `defer` may conflict with promises.
 exports.remit = defer;
 
 /*
  * Takes a funtion and returns a wrapped function that returns `this`
  */
--- a/addon-sdk/source/lib/sdk/system.js
+++ b/addon-sdk/source/lib/sdk/system.js
@@ -63,21 +63,29 @@ exports.exit = function exit(code) {
     stream.write(status, status.length);
     stream.flush();
     stream.close();
   }
 
   appStartup.quit(code ? E_ATTEMPT : E_FORCE);
 };
 
-// Adapter for nodejs's stdout & stderr:
-// http://nodejs.org/api/process.html#process_process_stdout
-let stdout = Object.freeze({ write: dump, end: dump });
-exports.stdout = stdout;
-exports.stderr = stdout;
+exports.stdout = new function() {
+  let write = dump
+  if ('logFile' in options && options.logFile) {
+    let mode = PR_WRONLY | PR_CREATE_FILE | PR_APPEND;
+    let stream = openFile(options.logFile, mode);
+    write = function write(data) {
+      let text = String(data);
+      stream.write(text, text.length);
+      stream.flush();
+    }
+  }
+  return Object.freeze({ write: write });
+};
 
 /**
  * Returns a path of the system's or application's special directory / file
  * associated with a given `id`. For list of possible `id`s please see:
  * https://developer.mozilla.org/en-US/docs/Code_snippets/File_I_O#Getting_files_in_special_directories
  * http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsAppDirectoryServiceDefs.h
  * @example
  *
@@ -121,17 +129,17 @@ exports.build = appInfo.appBuildID;
  * The XUL application's UUID.
  * This has traditionally been in the form
  * `{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}` but for some applications it may
  * be: "appname@vendor.tld".
  */
 exports.id = appInfo.ID;
 
 /**
- * The name of the application.
+ * The name of the application. 
  */
 exports.name = appInfo.name;
 
 /**
  * The XUL application's version, for example "0.8.0+" or "3.7a1pre".
  */
 exports.version = appInfo.version;
 
--- a/addon-sdk/source/lib/sdk/system/globals.js
+++ b/addon-sdk/source/lib/sdk/system/globals.js
@@ -17,17 +17,30 @@ let consoleService = Cc['@mozilla.org/co
                      QueryInterface(Ci.nsIConsoleService);
 
 // On windows dump does not writes into stdout so cfx can't read thous dumps.
 // To workaround this issue we write to a special file from which cfx will
 // read and print to the console.
 // For more details see: bug-673383
 exports.dump = stdout.write;
 
-exports.console = new PlainTextConsole();
+// Bug 718230: We need to send console messages to stdout and JS Console
+function forsakenConsoleDump(msg, level) {
+  stdout.write(msg);
+
+  if (level === 'error') {
+    let error = ScriptError();
+    msg = msg.replace(/^error: /, '');
+    error.init(msg, null, null, 0, 0, 0, 'Add-on SDK');
+    consoleService.logMessage(error);
+  }
+  else
+    consoleService.logStringMessage(msg);
+};
+exports.console = new PlainTextConsole(forsakenConsoleDump);
 
 // Provide CommonJS `define` to allow authoring modules in a format that can be
 // loaded both into jetpack and into browser via AMD loaders.
 Object.defineProperty(exports, 'define', {
   // `define` is provided as a lazy getter that binds below defined `define`
   // function to the module scope, so that require, exports and module
   // variables remain accessible.
   configurable: true,
--- a/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
@@ -21,19 +21,20 @@ function newTabWindow(options) {
 }
 
 Object.defineProperties(tabs, {
   open: { value: function open(options) {
     if (options.inNewWindow) {
         newTabWindow(options);
         return undefined;
     }
+    // Open in active window if new window was not required.
 
     let activeWindow = windows.activeWindow;
-    let privateState = (supportPrivateTabs && (options.isPrivate || isPrivate(activeWindow))) || false;
+    let privateState = !!options.isPrivate;
 
     // if the active window is in the state that we need then use it
     if (activeWindow && (!supportPrivateTabs || privateState === isPrivate(activeWindow))) {
       activeWindow.tabs.open(options);
     }
     else {
       // find a window in the state that we need
       let window = getWindow(privateState);
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { Cc, Ci, Cu } = require("chrome");
+const { Cc,Ci } = require("chrome");
 const { Loader } = require('./loader');
 const { serializeStack, parseStack  } = require("toolkit/loader");
 const { setTimeout } = require('../timers');
 const memory = require('../deprecated/memory');
 const { PlainTextConsole } = require("../console/plain-text");
 const { when: unload } = require("../system/unload");
 const { format, fromException }  = require("../console/traceback");
 const system = require("../system");
@@ -142,21 +142,19 @@ function dictDiff(last, curr) {
   return diff;
 }
 
 function reportMemoryUsage() {
   memory.gc();
 
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
-
   var reporters = mgr.enumerateReporters();
   if (reporters.hasMoreElements())
     print("\n");
-
   while (reporters.hasMoreElements()) {
     var reporter = reporters.getNext();
     reporter.QueryInterface(Ci.nsIMemoryReporter);
     print(reporter.description + ": " + reporter.memoryUsed + "\n");
   }
 
   var weakrefs = [info.weakref.get()
                   for each (info in memory.getObjects())];
@@ -164,34 +162,36 @@ function reportMemoryUsage() {
   print("Tracked memory objects in testing sandbox: " +
         weakrefs.length + "\n");
 }
 
 var gWeakrefInfo;
 
 function checkMemory() {
   memory.gc();
-  Cu.schedulePreciseGC(function () {
-    let leaks = getPotentialLeaks();
-
-    let compartmentURLs = Object.keys(leaks.compartments).filter(function(url) {
-      return !(url in startLeaks.compartments);
-    });
+  setTimeout(function () {
+    memory.gc();
+    setTimeout(function () {
+      let leaks = getPotentialLeaks();
+      let compartmentURLs = Object.keys(leaks.compartments).filter(function(url) {
+        return !(url in startLeaks.compartments);
+      });
 
-    let windowURLs = Object.keys(leaks.windows).filter(function(url) {
-      return !(url in startLeaks.windows);
-    });
+      let windowURLs = Object.keys(leaks.windows).filter(function(url) {
+        return !(url in startLeaks.windows);
+      });
 
-    for (let url of compartmentURLs)
-      console.warn("LEAKED", leaks.compartments[url]);
+      for (let url of compartmentURLs)
+        console.warn("LEAKED", leaks.compartments[url]);
 
-    for (let url of windowURLs)
-      console.warn("LEAKED", leaks.windows[url]);
+      for (let url of windowURLs)
+        console.warn("LEAKED", leaks.windows[url]);
 
-    showResults();
+      showResults();
+    });
   });
 }
 
 function showResults() {
   if (gWeakrefInfo) {
     gWeakrefInfo.forEach(
       function(info) {
         var ref = info.weakref.get();
@@ -293,17 +293,16 @@ function getPotentialLeaks() {
   let uri = ioService.newURI("chrome://global/content/", "UTF-8", null);
   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
                   getService(Ci.nsIChromeRegistry);
   uri = chromeReg.convertChromeURL(uri);
   let spec = uri.spec;
   let pos = spec.indexOf("!/");
   WHITELIST_BASE_URLS.push(spec.substring(0, pos + 2));
 
-  let zoneRegExp = new RegExp("^explicit/js-non-window/zones/zone[^/]+/compartment\\((.+)\\)");
   let compartmentRegexp = new RegExp("^explicit/js-non-window/compartments/non-window-global/compartment\\((.+)\\)/");
   let compartmentDetails = new RegExp("^([^,]+)(?:, (.+?))?(?: \\(from: (.*)\\))?$");
   let windowRegexp = new RegExp("^explicit/window-objects/top\\((.*)\\)/active");
   let windowDetails = new RegExp("^(.*), id=.*$");
 
   function isPossibleLeak(item) {
     if (!item.location)
       return false;
@@ -314,19 +313,18 @@ function getPotentialLeaks() {
     }
 
     return true;
   }
 
   let compartments = {};
   let windows = {};
   function logReporter(process, path, kind, units, amount, description) {
-    let matches;
-
-    if ((matches = compartmentRegexp.exec(path)) || (matches = zoneRegExp.exec(path))) {
+    let matches = compartmentRegexp.exec(path);
+    if (matches) {
       if (matches[1] in compartments)
         return;
 
       let details = compartmentDetails.exec(matches[1]);
       if (!details) {
         console.error("Unable to parse compartment detail " + matches[1]);
         return;
       }
@@ -573,17 +571,17 @@ var runTests = exports.runTests = functi
     print("Running tests on " + system.name + " " + system.version +
           "/Gecko " + system.platformVersion + " (" +
           system.id + ") under " +
           system.platform + "/" + system.architecture + ".\n");
 
     if (options.parseable)
       testConsole = new TestRunnerTinderboxConsole(options);
     else
-      testConsole = new TestRunnerConsole(new PlainTextConsole(), options);
+      testConsole = new TestRunnerConsole(new PlainTextConsole(print), options);
 
     loader = Loader(module, {
       console: testConsole,
       global: {} // useful for storing things like coverage testing.
     });
 
     // Load these before getting initial leak stats as they will still be in
     // memory when we check later
--- a/addon-sdk/source/lib/sdk/timers.js
+++ b/addon-sdk/source/lib/sdk/timers.js
@@ -2,104 +2,52 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "stable"
 };
 
-const { CC, Cc, Ci } = require("chrome");
-const { when: unload } = require("./system/unload");
+const { CC, Ci } = require('chrome');
+const { when: unload } = require('./system/unload');
 
 const { TYPE_ONE_SHOT, TYPE_REPEATING_SLACK } = Ci.nsITimer;
-const Timer = CC("@mozilla.org/timer;1", "nsITimer");
+const Timer = CC('@mozilla.org/timer;1', 'nsITimer');
 const timers = Object.create(null);
-const threadManager = Cc["@mozilla.org/thread-manager;1"].
-                      getService(Ci.nsIThreadManager);
-const prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                    getService(Ci.nsIPrefService).
-                    QueryInterface(Ci.nsIPrefBranch);
-
-let MIN_DELAY = 4;
-// Try to get min timeout delay used by browser.
-try { MIN_DELAY = prefBranch.getIntPref("dom.min_timeout_value"); } finally {}
-
 
 // Last timer id.
 let lastID = 0;
 
 // Sets typer either by timeout or by interval
 // depending on a given type.
-function setTimer(type, callback, delay, ...args) {
+function setTimer(type, callback, delay) {
   let id = ++ lastID;
   let timer = timers[id] = Timer();
+  let args = Array.slice(arguments, 3);
   timer.initWithCallback({
     notify: function notify() {
       try {
         if (type === TYPE_ONE_SHOT)
           delete timers[id];
         callback.apply(null, args);
       }
       catch(error) {
         console.exception(error);
       }
     }
-  }, Math.max(delay || MIN_DELAY), type);
+  }, delay || 0, type);
   return id;
 }
 
 function unsetTimer(id) {
   let timer = timers[id];
   delete timers[id];
-  if (timer) timer.cancel();
+  if (timer)
+    timer.cancel();
 }
 
-let immediates = new Map();
-
-let dispatcher = _ => {
-  // Allow scheduling of a new dispatch loop.
-  dispatcher.scheduled = false;
-  // Take a snapshot of timer `id`'s that have being present before
-  // starting a dispatch loop, in order to ignore timers registered
-  // in side effect to dispatch while also skipping immediates that
-  // were removed in side effect.
-  let ids = [id for ([id] of immediates)];
-  for (let id of ids) {
-    let immediate = immediates.get(id);
-    if (immediate) {
-      immediates.delete(id);
-      try { immediate(); }
-      catch (error) { console.exception(error); }
-    }
-  }
-}
-
-function setImmediate(callback, ...params) {
-  let id = ++ lastID;
-  // register new immediate timer with curried params.
-  immediates.set(id, _ => callback.apply(callback, params));
-  // if dispatch loop is not scheduled schedule one. Own scheduler
-  if (!dispatcher.scheduled) {
-    dispatcher.scheduled = true;
-    threadManager.currentThread.dispatch(dispatcher,
-                                         Ci.nsIThread.DISPATCH_NORMAL);
-  }
-  return id;
-}
-
-function clearImmediate(id) {
-  immediates.delete(id);
-}
-
-// Bind timers so that toString-ing them looks same as on native timers.
-exports.setImmediate = setImmediate.bind(null);
-exports.clearImmediate = clearImmediate.bind(null);
 exports.setTimeout = setTimer.bind(null, TYPE_ONE_SHOT);
 exports.setInterval = setTimer.bind(null, TYPE_REPEATING_SLACK);
 exports.clearTimeout = unsetTimer.bind(null);
 exports.clearInterval = unsetTimer.bind(null);
 
-// all timers are cleared out on unload.
-unload(function() {
-  immediates.clear();
-  Object.keys(timers).forEach(unsetTimer)
-});
+unload(function() { Object.keys(timers).forEach(unsetTimer) });
--- a/addon-sdk/source/lib/sdk/util/array.js
+++ b/addon-sdk/source/lib/sdk/util/array.js
@@ -96,19 +96,18 @@ function fromIterator(iterator) {
   else {
     for (let item of iterator)
       array.push(item);
   }
   return array;
 }
 exports.fromIterator = fromIterator;
 
-function find(array, predicate, fallback) {
+function find(array, predicate) {
   var index = 0;
   var count = array.length;
   while (index < count) {
     var value = array[index];
     if (predicate(value)) return value;
     else index = index + 1;
   }
-  return fallback;
 }
 exports.find = find;
--- a/addon-sdk/source/lib/sdk/window/utils.js
+++ b/addon-sdk/source/lib/sdk/window/utils.js
@@ -13,18 +13,16 @@ const observers = require('../deprecated
 const { defer } = require('sdk/core/promise');
 
 const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                        getService(Ci.nsIWindowWatcher);
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
            getService(Ci.nsIWindowMediator);
-const io = Cc['@mozilla.org/network/io-service;1'].
-           getService(Ci.nsIIOService);
 
 const BROWSER = 'navigator:browser',
       URI_BROWSER = 'chrome://browser/content/browser.xul',
       NAME = '_blank',
       FEATURES = 'chrome,all,dialog=no,non-private';
 
 function isWindowPrivate(win) {
   if (!win)
@@ -181,31 +179,28 @@ function serializeFeatures(options) {
  * @params {nsIDOMWindow} options.parent
  *    Used as parent for the created window.
  * @params {String} options.name
  *    Optional name that is assigned to the window.
  * @params {Object} options.features
  *    Map of key, values like: `{ width: 10, height: 15, chrome: true, private: true }`.
  */
 function open(uri, options) {
-  uri = uri || URI_BROWSER;
-  options = options || {}
-
-  if (['chrome', 'resource', 'data'].indexOf(io.newURI(uri, null, null).scheme) < 0)
-    throw new Error('only chrome, resource and data uris are allowed');
-
+  options = options || {};
   let newWindow = windowWatcher.
     openWindow(options.parent || null,
-               uri,
+               uri || URI_BROWSER,
                options.name || null,
                serializeFeatures(options.features || {}),
                options.args || null);
 
   return newWindow;
 }
+
+
 exports.open = open;
 
 function onFocus(window) {
   let { resolve, promise } = defer();
 
   if (isFocused(window)) {
     resolve(window);
   }
--- a/addon-sdk/source/python-lib/cuddlefish/options_xul.py
+++ b/addon-sdk/source/python-lib/cuddlefish/options_xul.py
@@ -47,19 +47,16 @@ def validate_prefs(options):
 
 def parse_options(options, jetpack_id):
     doc = Document()
     root = doc.createElement("vbox")
     root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
     doc.appendChild(root)
 
     for pref in options:
-        if ("hidden" in pref and pref["hidden"] == True):
-            continue;
-
         setting = doc.createElement("setting")
         setting.setAttribute("pref-name", pref["name"])
         setting.setAttribute("data-jetpack-id", jetpack_id)
         setting.setAttribute("pref", "extensions." + jetpack_id + "." + pref["name"])
         setting.setAttribute("type", pref["type"])
         setting.setAttribute("title", pref["title"])
 
         if ("description" in pref):
--- a/addon-sdk/source/python-lib/cuddlefish/runner.py
+++ b/addon-sdk/source/python-lib/cuddlefish/runner.py
@@ -25,17 +25,17 @@ FILTER_ONLY_CONSOLE_FROM_ADB = re.compil
 
 # Used to detect the currently running test
 PARSEABLE_TEST_NAME = re.compile(r'TEST-START \| ([^\n]+)\n')
 
 # Maximum time we'll wait for tests to finish, in seconds.
 # The purpose of this timeout is to recover from infinite loops.  It should be
 # longer than the amount of time any test run takes, including those on slow
 # machines running slow (debug) versions of Firefox.
-RUN_TIMEOUT = 1.5 * 60 * 60 # 1.5 Hour
+RUN_TIMEOUT = 45 * 60 # 45 minutes
 
 # Maximum time we'll wait for tests to emit output, in seconds.
 # The purpose of this timeout is to recover from hangs.  It should be longer
 # than the amount of time any test takes to report results.
 OUTPUT_TIMEOUT = 60 # one minute
 
 def follow_file(filename):
     """
@@ -491,16 +491,19 @@ def run_app(harness_root_dir, manifest_r
         fileno,logfile = tempfile.mkstemp(prefix="harness-log-")
         os.close(fileno)
     logfile_tail = follow_file(logfile)
     atexit.register(maybe_remove_logfile)
 
     logfile = os.path.abspath(os.path.expanduser(logfile))
     maybe_remove_logfile()
 
+    if app_type != "fennec-on-device":
+        harness_options['logFile'] = logfile
+
     env = {}
     env.update(os.environ)
     env['MOZ_NO_REMOTE'] = '1'
     env['XPCOM_DEBUG_BREAK'] = 'stack'
     env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
     env.update(extra_environment)
     if norun:
         cmdargs.append("-no-remote")
--- a/addon-sdk/source/python-lib/mozrunner/winprocess.py
+++ b/addon-sdk/source/python-lib/mozrunner/winprocess.py
@@ -325,19 +325,23 @@ GetExitCodeProcessProto = WINFUNCTYPE(BO
 GetExitCodeProcessFlags = ((1, "hProcess"),
                            (2, "lpExitCode"))
 GetExitCodeProcess = GetExitCodeProcessProto(
     ("GetExitCodeProcess", windll.kernel32),
     GetExitCodeProcessFlags)
 GetExitCodeProcess.errcheck = ErrCheckBool
 
 def CanCreateJobObject():
-    # Running firefox in a job (from cfx) hangs on sites using flash plugin
-    # so job creation is turned off for now. (see Bug 768651).
-    return False
+    currentProc = GetCurrentProcess()
+    if IsProcessInJob(currentProc):
+        jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitInformation')
+        limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
+        return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitflags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)
+    else:
+        return True
 
 ### testing functions
 
 def parent():
     print 'Starting parent'
     currentProc = GetCurrentProcess()
     if IsProcessInJob(currentProc):
         print >> sys.stderr, "You should not be in a job object to test"
--- a/addon-sdk/source/test/addons/content-permissions/main.js
+++ b/addon-sdk/source/test/addons/content-permissions/main.js
@@ -1,21 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+const xulApp = require("sdk/system/xul-app");
 const { PageMod } = require("sdk/page-mod");
 const tabs = require("sdk/tabs");
-const { startServerAsync } = require("sdk/test/httpd");
-
-const serverPort = 8099;
 
 exports.testCrossDomainIframe = function(assert, done) {
-  let server = startServerAsync(serverPort);
+  let serverPort = 8099;
+  let server = require("sdk/test/httpd").startServerAsync(serverPort);
   server.registerPathHandler("/iframe", function handle(request, response) {
     response.write("<html><body>foo</body></html>");
   });
 
   let pageMod = PageMod({
     include: "about:*",
     contentScript: "new " + function ContentScriptScope() {
       self.on("message", function (url) {
@@ -27,33 +26,29 @@ exports.testCrossDomainIframe = function
         iframe.setAttribute("src", url);
         document.documentElement.appendChild(iframe);
       });
     },
     onAttach: function(w) {
       w.on("message", function (body) {
         assert.equal(body, "foo", "received iframe html content");
         pageMod.destroy();
-        w.tab.close(function() {
-          server.stop(done);
-        });
+        w.tab.close();
+        server.stop(done);
       });
-
       w.postMessage("http://localhost:8099/iframe");
     }
   });
 
-  tabs.open({
-    url: "about:home",
-    inBackground: true
-  });
+  tabs.open("about:credits");
 };
 
 exports.testCrossDomainXHR = function(assert, done) {
-  let server = startServerAsync(serverPort);
+  let serverPort = 8099;
+  let server = require("sdk/test/httpd").startServerAsync(serverPort);
   server.registerPathHandler("/xhr", function handle(request, response) {
     response.write("foo");
   });
 
   let pageMod = PageMod({
     include: "about:*",
     contentScript: "new " + function ContentScriptScope() {
       self.on("message", function (url) {
@@ -65,24 +60,27 @@ exports.testCrossDomainXHR = function(as
         };
         request.send(null);
       });
     },
     onAttach: function(w) {
       w.on("message", function (body) {
         assert.equal(body, "foo", "received XHR content");
         pageMod.destroy();
-        w.tab.close(function() {
-          server.stop(done);
-        });
+        w.tab.close();
+        server.stop(done);
       });
-
       w.postMessage("http://localhost:8099/xhr");
     }
   });
 
-  tabs.open({
-    url: "about:home",
-    inBackground: true
-  });
+  tabs.open("about:credits");
 };
 
+if (!xulApp.versionInRange(xulApp.platformVersion, "17.0a2", "*")) {
+  module.exports = {
+    "test Unsupported Application": function Unsupported (assert) {
+      assert.pass("This firefox version doesn't support cross-domain-content permission.");
+    }
+  };
+}
+
 require("sdk/test/runner").runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/main/main.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-const { setTimeout } = require('sdk/timers');
-
-let mainStarted = false;
-
-exports.main = function main(options, callbacks) {
-  mainStarted = true;
-
-  let tests = {};
-
-  tests.testMainArguments = function(assert) {
-  	assert.ok(!!options, 'options argument provided to main');
-    assert.ok('loadReason' in options, 'loadReason is in options provided by main');
-    assert.equal(typeof callbacks.print, 'function', 'callbacks.print is a function');
-    assert.equal(typeof callbacks.quit, 'function', 'callbacks.quit is a function');
-    assert.equal(options.loadReason, 'install', 'options.loadReason is install');
-  }
-
-  require('sdk/test/runner').runTestsFromModule({exports: tests});
-}
-
-// this causes a fail if main does not start
-setTimeout(function() {
-  if (mainStarted)
-  	return;
-
-  // main didn't start, fail..
-  require("sdk/test/runner").runTestsFromModule({exports: {
-  	testFail: function(assert) assert.fail('Main did not start..')
-  }});
-}, 500);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/main/package.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "id": "test-main"
-}
--- a/addon-sdk/source/test/addons/private-browsing-supported/main.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/main.js
@@ -1,24 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { merge } = require('sdk/util/object');
-const app = require('sdk/system/xul-app');
+const app = require("sdk/system/xul-app");
 const { isGlobalPBSupported } = require('sdk/private-browsing/utils');
 
 merge(module.exports,
   require('./test-tabs'),
   require('./test-page-mod'),
   require('./test-selection'),
   require('./test-panel'),
   require('./test-private-browsing'),
   isGlobalPBSupported ? require('./test-global-private-browsing') : {}
 );
 
 // Doesn't make sense to test window-utils and windows on fennec,
 // as there is only one window which is never private
-if (!app.is('Fennec'))
+if (!app.is("Fennec"))
   merge(module.exports, require('./test-windows'));
 
 require('sdk/test/runner').runTestsFromModule(module);
--- a/addon-sdk/source/test/addons/private-browsing-supported/test-tabs.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-tabs.js
@@ -1,17 +1,15 @@
 'use strict';
 
 const tabs = require('sdk/tabs');
 const { is } = require('sdk/system/xul-app');
 const { isPrivate } = require('sdk/private-browsing');
 const pbUtils = require('sdk/private-browsing/utils');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
-const { promise: windowPromise, close, focus } = require('sdk/window/helpers');
-const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 
 exports.testPrivateTabsAreListed = function (assert, done) {
   let originalTabCount = tabs.length;
 
   tabs.open({
     url: 'about:blank',
     isPrivate: true,
     onOpen: function(tab) {
@@ -23,91 +21,12 @@ exports.testPrivateTabsAreListed = funct
                      'New private window\'s tab are visible in tabs list');
       }
       else {
       // Global case, openDialog didn't opened a private window/tab
         assert.ok(!isPrivate(tab), "tab isn't private");
         assert.equal(tabs.length, originalTabCount + 1,
                      'New non-private window\'s tab is visible in tabs list');
       }
-
       tab.close(done);
     }
   });
 }
-
-exports.testOpenTabWithPrivateActiveWindowNoIsPrivateOption = function(assert, done) {
-  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: true });
-
-  windowPromise(window, 'load').then(focus).then(function (window) {
-    assert.ok(isPrivate(window), 'new window is private');
-
-    tabs.open({
-      url: 'about:blank',
-      onOpen: function(tab) {
-        assert.ok(isPrivate(tab), 'new tab is private');
-        assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
-        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
-
-        close(window).then(done, assert.fail);
-      }
-    })
-  }, assert.fail).then(null, assert.fail);
-}
-
-exports.testOpenTabWithNonPrivateActiveWindowNoIsPrivateOption = function(assert, done) {
-  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: false });
-
-  windowPromise(window, 'load').then(focus).then(function (window) {
-    assert.equal(isPrivate(window), false, 'new window is not private');
-
-    tabs.open({
-      url: 'about:blank',
-      onOpen: function(tab) {
-        assert.equal(isPrivate(tab), false, 'new tab is not private');
-        assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
-        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
-
-        close(window).then(done, assert.fail);
-      }
-    })
-  }, assert.fail).then(null, assert.fail);
-}
-
-exports.testOpenTabWithPrivateActiveWindowWithIsPrivateOptionTrue = function(assert, done) {
-  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: true });
-
-  windowPromise(window, 'load').then(focus).then(function (window) {
-    assert.ok(isPrivate(window), 'new window is private');
-
-    tabs.open({
-      url: 'about:blank',
-      isPrivate: true,
-      onOpen: function(tab) {
-        assert.ok(isPrivate(tab), 'new tab is private');
-        assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
-        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
-
-        close(window).then(done, assert.fail);
-      }
-    })
-  }, assert.fail).then(null, assert.fail);
-}
-
-exports.testOpenTabWithNonPrivateActiveWindowWithIsPrivateOptionFalse = function(assert, done) {
-  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: false });
-
-  windowPromise(window, 'load').then(focus).then(function (window) {
-    assert.equal(isPrivate(window), false, 'new window is not private');
-
-    tabs.open({
-      url: 'about:blank',
-      isPrivate: false,
-      onOpen: function(tab) {
-        assert.equal(isPrivate(tab), false, 'new tab is not private');
-        assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
-        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
-
-        close(window).then(done, assert.fail);
-      }
-    })
-  }, assert.fail).then(null, assert.fail);
-}
deleted file mode 100644
--- a/addon-sdk/source/test/addons/simple-prefs/lib/main.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-const { Cu } = require('chrome');
-const sp = require('sdk/simple-prefs');
-const app = require('sdk/system/xul-app');
-const self = require('sdk/self');
-const tabs = require('sdk/tabs');
-
-const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
-
-exports.testDefaultValues = function (assert) {
-  assert.equal(sp.prefs.myHiddenInt, 5, 'myHiddenInt default is 5');
-  assert.equal(sp.prefs.myInteger, 8, 'myInteger default is 8');
-  assert.equal(sp.prefs.somePreference, 'TEST', 'somePreference default is correct');
-}
-
-exports.testOptionsType = function(assert, done) {
-  AddonManager.getAddonByID(self.id, function(aAddon) {
-    assert.equal(aAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, 'options type is inline');
-    done();
-  });
-}
-
-if (app.is('Firefox')) {
-  exports.testAOM = function(assert, done) {
-      tabs.open({
-      	url: 'about:addons',
-      	onReady: function(tab) {
-          tab.attach({
-          	contentScript: 'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' +
-          		             'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' +
-          		               'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' +
-          		               'setTimeout(function() {\n' + // TODO: figure out why this is necessary..
-                                 'self.postMessage({\n' +
-                                   'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[title=\'some-title\']")),\n' +
-                                   'myInteger: getAttributes(unsafeWindow.document.querySelector("setting[title=\'my-int\']")),\n' +
-                                   'myHiddenInt: getAttributes(unsafeWindow.document.querySelector("setting[title=\'hidden-int\']"))\n' +
-                                 '});\n' +
-          		               '}, 250);\n' +
-          		             '}, false);\n' +
-                             'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' +
-                           '});\n' + 
-                           'function getAttributes(ele) {\n' +
-                             'if (!ele) return {};\n' +
-                             'return {\n' +
-                               'pref: ele.getAttribute("pref"),\n' +
-                               'type: ele.getAttribute("type"),\n' +
-                               'title: ele.getAttribute("title"),\n' +
-                               'desc: ele.getAttribute("desc")\n' +
-                             '}\n' +
-                           '}\n',
-            onMessage: function(msg) {
-              // test somePreference
-              assert.equal(msg.somePreference.type, 'string', 'some pref is a string');
-              assert.equal(msg.somePreference.pref, 'extensions.'+self.id+'.somePreference', 'somePreference path is correct');
-              assert.equal(msg.somePreference.title, 'some-title', 'somePreference title is correct');
-              assert.equal(msg.somePreference.desc, 'Some short description for the preference', 'somePreference description is correct');
-
-              // test myInteger
-              assert.equal(msg.myInteger.type, 'integer', 'myInteger is a int');
-              assert.equal(msg.myInteger.pref, 'extensions.'+self.id+'.myInteger', 'extensions.test-simple-prefs.myInteger');
-              assert.equal(msg.myInteger.title, 'my-int', 'myInteger title is correct');
-              assert.equal(msg.myInteger.desc, 'How many of them we have.', 'myInteger desc is correct');
-
-              // test myHiddenInt
-              assert.equal(msg.myHiddenInt.type, undefined, 'myHiddenInt was not displayed');
-              assert.equal(msg.myHiddenInt.pref, undefined, 'myHiddenInt was not displayed');
-              assert.equal(msg.myHiddenInt.title, undefined, 'myHiddenInt was not displayed');
-              assert.equal(msg.myHiddenInt.desc, undefined, 'myHiddenInt was not displayed');
-
-              tab.close(done);
-            }
-          });
-      	}
-      });
-  }
-}
-
-require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/simple-prefs/package.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "id": "test-simple-prefs",
-    "preferences": [{
-        "name": "somePreference",
-        "title": "some-title",
-        "description": "Some short description for the preference",
-        "type": "string",
-        "value": "TEST"
-    },
-    {
-        "description": "How many of them we have.",
-        "name": "myInteger",
-        "type": "integer",
-        "value": 8,
-        "title": "my-int"
-    }, {
-        "name": "myHiddenInt",
-        "type": "integer",
-        "hidden": true,
-        "value": 5,
-        "title": "hidden-int"
-    }]
-}
--- a/addon-sdk/source/test/places-helper.js
+++ b/addon-sdk/source/test/places-helper.js
@@ -14,17 +14,16 @@ const tagsrv = Cc['@mozilla.org/browser/
               getService(Ci.nsITaggingService);
 const asyncHistory = Cc['@mozilla.org/browser/history;1'].
               getService(Ci.mozIAsyncHistory);
 const { send } = require('sdk/addon/events');
 const { setTimeout } = require('sdk/timers');
 const { newURI } = require('sdk/url/utils');
 const { defer, all } = require('sdk/core/promise');
 const { once } = require('sdk/system/events');
-const { set } = require('sdk/preferences/service');
 const {
   Bookmark, Group, Separator,
   save, search,
   MENU, TOOLBAR, UNSORTED
 } = require('sdk/places/bookmarks');
 
 function invalidResolve (assert) {
   return function (e) {
@@ -41,35 +40,22 @@ function invalidReject (assert) {
 exports.invalidReject = invalidReject;
 
 // Removes all children of group
 function clearBookmarks (group) {
   group
    ? bmsrv.removeFolderChildren(group.id)
    : clearAllBookmarks();
 }
+exports.clearBookmarks = clearBookmarks;
 
 function clearAllBookmarks () {
   [MENU, TOOLBAR, UNSORTED].forEach(clearBookmarks);
 }
-
-function clearHistory (done) {
-  hsrv.removeAllPages();
-  once('places-expiration-finished', done);
-}
-
-// Cleans bookmarks and history and disables maintanance
-function resetPlaces (done) {
-  // Set last maintenance to current time to prevent
-  // Places DB maintenance occuring and locking DB
-  set('places.database.lastMaintenance', Math.floor(Date.now() / 1000));
-  clearAllBookmarks();
-  clearHistory(done);
-}
-exports.resetPlaces = resetPlaces;
+exports.clearAllBookmarks = clearAllBookmarks;
 
 function compareWithHost (assert, item) {
   let id = item.id;
   let type = item.type === 'group' ? bmsrv.TYPE_FOLDER : bmsrv['TYPE_' + item.type.toUpperCase()];
   let url = item.url && !item.url.endsWith('/') ? item.url + '/' : item.url;
 
   if (type === bmsrv.TYPE_BOOKMARK) {
     assert.equal(url, bmsrv.getBookmarkURI(id).spec.toString(), 'Matches host url');
@@ -114,16 +100,22 @@ function createVisit (url) {
   place.visits = [{
     transitionType: hsrv.TRANSITION_LINK,
     visitDate: +(new Date()) * 1000,
     referredURI: undefined
   }];
   return place;
 }
 
+function clearHistory (done) {
+  hsrv.removeAllPages();
+  once('places-expiration-finished', done);
+}
+exports.clearHistory = clearHistory;
+
 function createBookmark (data) {
   data = data || {};
   let item = {
     title: data.title || 'Moz',
     url: data.url || (!data.type || data.type === 'bookmark' ?
       'http://moz.com/' :
       undefined),
     tags: data.tags || (!data.type || data.type === 'bookmark' ?
--- a/addon-sdk/source/test/tabs/test-fennec-tabs.js
+++ b/addon-sdk/source/test/tabs/test-fennec-tabs.js
@@ -106,21 +106,22 @@ exports.testTabProperties = function(tes
   let tabsLen = tabs.length;
   tabs.open({
     url: url,
     onReady: function(tab) {
       test.assertEqual(tab.title, "foo", "title of the new tab matches");
       test.assertEqual(tab.url, url, "URL of the new tab matches");
       test.assert(tab.favicon, "favicon of the new tab is not empty");
       // TODO: remove need for this test by implementing the favicon feature
-      test.assertEqual(messages[0].msg,
-        "tab.favicon is deprecated, and " +
-        "currently favicon helpers are not yet supported " +
-        "by Fennec",
-        "favicon logs an error for now");
+      // Poors man deepEqual with JSON.stringify...
+      test.assertEqual(JSON.stringify(messages),
+                       JSON.stringify(['tab.favicon is deprecated, and ' +
+                          'currently favicon helpers are not yet supported ' +
+                          'by Fennec']),
+                       "favicon logs an error for now");
       test.assertEqual(tab.style, null, "style of the new tab matches");
       test.assertEqual(tab.index, tabsLen, "index of the new tab matches");
       test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
       test.assertNotEqual(tab.id, null, "a tab object always has an id property");
 
       tab.close(function() {
         loader.unload();
 
--- a/addon-sdk/source/test/test-array.js
+++ b/addon-sdk/source/test/test-array.js
@@ -80,16 +80,8 @@ exports.testUnique = function(test) {
 
   function compareArray (a, b) {
     test.assertEqual(a.length, b.length);
     for (let i = 0; i < a.length; i++) {
       test.assertEqual(a[i], b[i]);
     }
   }
 };
-
-exports.testFind = function(test) {
-  let isOdd = (x) => x % 2;
-  test.assertEqual(array.find([2, 4, 5, 7, 8, 9], isOdd), 5);
-  test.assertEqual(array.find([2, 4, 6, 8], isOdd), undefined);
-  test.assertEqual(array.find([2, 4, 6, 8], isOdd, null), null);
-};
-
--- a/addon-sdk/source/test/test-browser-events.js
+++ b/addon-sdk/source/test/test-browser-events.js
@@ -87,9 +87,19 @@ exports["test browser events ignore othe
       done();
     }
   });
 
   // Open window and close it to trigger observers.
   let window = open("data:text/html,not a browser");
 };
 
+if (require("sdk/system/xul-app").is("Fennec")) {
+  module.exports = {
+    "test Unsupported Test": function UnsupportedTest (assert) {
+        assert.pass(
+          "Skipping this test until Fennec support is implemented." +
+          "See bug 793071");
+    }
+  }
+}
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-content-script.js
+++ b/addon-sdk/source/test/test-content-script.js
@@ -1,71 +1,57 @@
 /* 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 hiddenFrames = require("sdk/frame/hidden-frame");
-const { create: makeFrame } = require("sdk/frame/utils");
-const { window } = require("sdk/addon/window");
+
 const { Loader } = require('sdk/test/loader');
-const { URL } = require("sdk/url");
-const testURI = require("sdk/self").data.url("test.html");
-const testHost = URL(testURI).scheme + '://' + URL(testURI).host;
 
 /*
  * Utility function that allow to easily run a proxy test with a clean
  * new HTML document. See first unit test for usage.
  */
 function createProxyTest(html, callback) {
   return function (assert, done) {
-    let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
-    let principalLoaded = false;
+    let url = 'data:text/html;charset=utf-8,' + encodeURI(html);
+
+    let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
+      onReady: function () {
 
-    let element = makeFrame(window.document, {
-      nodeName: "iframe",
-      type: "content",
-      allowJavascript: true,
-      allowPlugins: true,
-      allowAuth: true,
-      uri: testURI
-    });
+        function onDOMReady() {
+          hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady,
+                                                  false);
 
-    element.addEventListener("DOMContentLoaded", onDOMReady, false);
-
-    function onDOMReady() {
-      // Reload frame after getting principal from `testURI`
-      if (!principalLoaded) {
-        element.setAttribute("src", url);
-        principalLoaded = true;
-        return;
-      }
+          let xrayWindow = hiddenFrame.element.contentWindow;
+          let rawWindow = xrayWindow.wrappedJSObject;
 
-      assert.equal(element.getAttribute("src"), url, "correct URL loaded");
-      element.removeEventListener("DOMContentLoaded", onDOMReady,
-                                                  false);
-      let xrayWindow = element.contentWindow;
-      let rawWindow = xrayWindow.wrappedJSObject;
+          let isDone = false;
+          let helper = {
+            xrayWindow: xrayWindow,
+            rawWindow: rawWindow,
+            createWorker: function (contentScript) {
+              return createWorker(assert, xrayWindow, contentScript, helper.done);
+            },
+            done: function () {
+              if (isDone)
+                return;
+              isDone = true;
+              hiddenFrames.remove(hiddenFrame);
+              done();
+            }
+          }
+          callback(helper, assert);
+        }
 
-      let isDone = false;
-      let helper = {
-        xrayWindow: xrayWindow,
-        rawWindow: rawWindow,
-        createWorker: function (contentScript) {
-          return createWorker(assert, xrayWindow, contentScript, helper.done);
-        },
-        done: function () {
-          if (isDone)
-            return;
-          isDone = true;
-          element.parentNode.removeChild(element);
-          done();
-        }
-      };
-      callback(helper, assert);
-    }
+        hiddenFrame.element.addEventListener("DOMContentLoaded", onDOMReady, false);
+        hiddenFrame.element.setAttribute("src", url);
+
+      }
+    }));
   };
 }
 
 function createWorker(assert, xrayWindow, contentScript, done) {
   let loader = Loader(module);
   let Worker = loader.require("sdk/content/worker").Worker;
   let worker = Worker({
     window: xrayWindow,
@@ -174,19 +160,19 @@ exports["test postMessage"] = createProx
   // Listen without proxies, to check that it will work in regular case
   // simulate listening from a web document.
   ifWindow.addEventListener("message", function listener(event) {
     ifWindow.removeEventListener("message", listener, false);
     // As we are in system principal, event is an XrayWrapper
     // xrays use current compartments when calling postMessage method.
     // Whereas js proxies was using postMessage method compartment,
     // not the caller one.
-    assert.strictEqual(event.source, helper.xrayWindow,
-                      "event.source is the top window");
-    assert.equal(event.origin, testHost, "origin matches testHost");
+    assert.equal(event.source, helper.xrayWindow,
+                 "event.source is the top window");
+    assert.equal(event.origin, "null", "origin is null");
 
     assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
                      "message data is correct");
 
     helper.done();
   }, false);
 
   helper.createWorker(
@@ -225,40 +211,38 @@ exports["test Object Listener"] = create
     }
   );
 
 });
 
 exports["test Object Listener 2"] = createProxyTest("", function (helper) {
 
   helper.createWorker(
-    ('new ' + function ContentScriptScope() {
-      // variable replaced with `testHost`
-      let testHost = "TOKEN";
+    'new ' + function ContentScriptScope() {
       // Verify object as DOM event listener
       let myMessageListener = {
         called: false,
         handleEvent: function(event) {
           window.removeEventListener("message", myMessageListener, true);
 
           assert(this == myMessageListener, "`this` is the original object");
           assert(!this.called, "called only once");
           this.called = true;
           assert(event.target == document.defaultView, "event.target is the wrapped window");
           assert(event.source == document.defaultView, "event.source is the wrapped window");
-          assert(event.origin == testHost, "origin matches testHost");
+          assert(event.origin == "null", "origin is null");
           assert(event.data == "ok", "message data is correct");
           done();
         }
       };
 
       window.addEventListener("message", myMessageListener, true);
       document.defaultView.postMessage("ok", '*');
     }
-  ).replace("TOKEN", testHost));
+  );
 
 });
 
 let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
              '<input id="input2" type="checkbox" />';
 
 /* Disable test to keep tree green until Bug 756214 is fixed.
 exports.testStringOverload = createProxyTest(html, function (helper, test) {
--- a/addon-sdk/source/test/test-fs.js
+++ b/addon-sdk/source/test/test-fs.js
@@ -4,17 +4,16 @@
 
 "use strict";
 
 const { pathFor } = require("sdk/system");
 const fs = require("sdk/io/fs");
 const url = require("sdk/url");
 const path = require("sdk/fs/path");
 const { Buffer } = require("sdk/io/buffer");
-const { is } = require("sdk/system/xul-app");
 
 // Use profile directory to list / read / write files.
 const profilePath = pathFor("ProfD");
 const fileNameInProfile = "compatibility.ini";
 const dirNameInProfile = "extensions";
 const filePathInProfile = path.join(profilePath, fileNameInProfile);
 const dirPathInProfile = path.join(profilePath, dirNameInProfile);
 const mkdirPath = path.join(profilePath, "sdk-fixture-mkdir");
@@ -22,28 +21,29 @@ const writePath = path.join(profilePath,
 const unlinkPath = path.join(profilePath, "sdk-fixture-unlink");
 const truncatePath = path.join(profilePath, "sdk-fixture-truncate");
 const renameFromPath = path.join(profilePath, "sdk-fixture-rename-from");
 const renameToPath = path.join(profilePath, "sdk-fixture-rename-to");
 
 const profileEntries = [
   "compatibility.ini",
   "extensions",
+  "extensions.ini",
   "prefs.js"
   // There are likely to be a lot more files but we can't really
   // on consistent list so we limit to this.
 ];
 
-exports["test readdir"] = function(assert, end) {
+exports["test readir"] = function(assert, end) {
   var async = false;
   fs.readdir(profilePath, function(error, entries) {
     assert.ok(async, "readdir is async");
     assert.ok(!error, "there is no error when reading directory");
     assert.ok(profileEntries.length <= entries.length,
-              "got at least number of entries we expect");
+              "got et least number of entries we expect");
     assert.ok(profileEntries.every(function(entry) {
                 return entries.indexOf(entry) >= 0;
               }), "all profiles are present");
     end();
   });
 
   async = true;
 };
@@ -62,23 +62,23 @@ exports["test readdir error"] = function
 
   async = true;
 };
 
 exports["test readdirSync"] = function(assert) {
   var async = false;
   var entries = fs.readdirSync(profilePath);
   assert.ok(profileEntries.length <= entries.length,
-            "got at least number of entries we expect");
+            "got et least number of entries we expect");
   assert.ok(profileEntries.every(function(entry) {
     return entries.indexOf(entry) >= 0;
   }), "all profiles are present");
 };
 
-exports["test readdirSync error"] = function(assert) {
+exports["test readirSync error"] = function(assert) {
   var async = false;
   var path = profilePath + "-does-not-exists";
   try {
     fs.readdirSync(path);
     assert.fail(Error("No error was thrown"));
   } catch (error) {
     assert.equal(error.message, "ENOENT, readdir " + path);
     assert.equal(error.code, "ENOENT", "error has a code");
@@ -87,17 +87,16 @@ exports["test readdirSync error"] = func
   }
 };
 
 exports["test readFile"] = function(assert, end) {
   let async = false;
   fs.readFile(filePathInProfile, function(error, content) {
     assert.ok(async, "readFile is async");
     assert.ok(!error, "error is falsy");
-
     assert.ok(Buffer.isBuffer(content), "readFile returns buffer");
     assert.ok(typeof(content.length) === "number", "buffer has length");
     assert.ok(content.toString().indexOf("[Compatibility]") >= 0,
               "content contains expected data");
     end();
   });
   async = true;
 };
@@ -334,16 +333,17 @@ exports["test fs.truncateSync fs.unlinkS
 
 
 exports["test fs.truncate"] = function(assert, end) {
   let path = truncatePath;
   if (!fs.existsSync(path)) {
     let async = false;
     fs.truncate(path, 0, function(error) {
       assert.ok(async, "truncate is async");
+      console.log(error);
       assert.ok(!error, "no error");
       assert.equal(fs.existsSync(path), true, "file was created");
       fs.unlinkSync(path);
       assert.equal(fs.existsSync(path), false, "file was removed");
       end();
     })
     async = true;
   }
@@ -454,33 +454,9 @@ exports["test fs.writeFile"] = function(
     fs.unlinkSync(path);
     assert.ok(!fs.exists(path), "file was removed");
 
     end();
   });
   async = true;
 };
 
-exports["test fs.writeFile (with large files)"] = function(assert, end) {
-  let path = writePath;
-  let content = "";
-
-  for (var i = 0; i < 100000; i++) {
-    content += "buffer\n";
-  }
-
-  var async = false;
-  fs.writeFile(path, content, function(error) {
-    assert.ok(async, "fs write is async");
-    assert.ok(!error, "error is falsy");
-    assert.ok(fs.existsSync(path), "file was created");
-    assert.equal(fs.readFileSync(path).toString(),
-                 content,
-                 "contet was written");
-    fs.unlinkSync(path);
-    assert.ok(!fs.exists(path), "file was removed");
-
-    end();
-  });
-  async = true;
-};
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-observer-service.js
+++ b/addon-sdk/source/test/test-observer-service.js
@@ -17,23 +17,23 @@ exports.testUnloadAndErrorLogging = func
   var badCb = function(subject, data) {
     throw new Error("foo");
   };
   sbobsvc.add("blarg", cb);
   observers.notify("blarg", "yo yo");
   test.assertEqual(timesCalled, 1);
   sbobsvc.add("narg", badCb);
   observers.notify("narg", "yo yo");
-
-  test.assertEqual(messages[0], "console.error: " + require("sdk/self").name + ": \n");
-  var lines = messages[1].split("\n");
-  test.assertEqual(lines[0], "  Message: Error: foo");
-  test.assertEqual(lines[1], "  Stack:");
+  var lines = messages[0].split("\n");
+  test.assertEqual(lines[0], "error: " + require("sdk/self").name + ": An exception occurred.");
+  test.assertEqual(lines[0], "error: " + require("sdk/self").name + ": An exception occurred.");
+  test.assertEqual(lines[1], "Error: foo");
   // Keep in mind to update "18" to the line of "throw new Error("foo")"
-  test.assert(lines[2].indexOf(module.uri + ":18") != -1);
+  test.assertEqual(lines[2], module.uri + " 18");
+  test.assertEqual(lines[3], "Traceback (most recent call last):");
 
   loader.unload();
   observers.notify("blarg", "yo yo");
   test.assertEqual(timesCalled, 1);
 };
 
 exports.testObserverService = function(test) {
   var ios = Cc['@mozilla.org/network/io-service;1']
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -14,18 +14,16 @@ const windowUtils = require('sdk/depreca
 const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
 const xulApp = require("sdk/system/xul-app");
 const { data, isPrivateBrowsingSupported } = require('sdk/self');
 const { isPrivate } = require('sdk/private-browsing');
 const { openWebpage } = require('./private-browsing/helper');
 const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const promise = require("sdk/core/promise");
 const { pb } = require('./private-browsing/helper');
-const { URL } = require("sdk/url");
-const testPageURI = require("sdk/self").data.url("test.html");
 
 /* XXX This can be used to delay closing the test Firefox instance for interactive
  * testing or visual inspection. This test is registered first so that it runs
  * the last. */
 exports.delay = function(test) {
   if (false) {
     test.waitUntilDone(60000);
     timer.setTimeout(function() {test.done();}, 4000);
@@ -116,26 +114,26 @@ exports.testPageModIncludes = function(t
       // so we attach it on 'start'.
       contentScriptWhen: 'start',
       onAttach: function(worker) {
         worker.postMessage(this.include[0]);
       }
     };
   }
 
-  testPageMod(test, testPageURI, [
+  testPageMod(test, "about:buildconfig", [
       createPageModTest("*", false),
       createPageModTest("*.google.com", false),
-      createPageModTest("resource:*", true),
-      createPageModTest("resource:", false),
-      createPageModTest(testPageURI, true)
+      createPageModTest("about:*", true),
+      createPageModTest("about:", false),
+      createPageModTest("about:buildconfig", true)
     ],
     function (win, done) {
-      test.waitUntil(function () win.localStorage[testPageURI],
-                     testPageURI + " page-mod to be executed")
+      test.waitUntil(function () win.localStorage["about:buildconfig"],
+                     "about:buildconfig page-mod to be executed")
           .then(function () {
             asserts.forEach(function(fn) {
               fn(test, win);
             });
             done();
           });
     }
     );
--- a/addon-sdk/source/test/test-page-worker.js
+++ b/addon-sdk/source/test/test-page-worker.js
@@ -2,18 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Loader } = require('sdk/test/loader');
 const Pages = require("sdk/page-worker");
 const Page = Pages.Page;
-const { URL } = require("sdk/url");
-const testURI = require("sdk/self").data.url("test.html");
 
 const ERR_DESTROYED =
   "Couldn't find the worker to receive this message. " +
   "The script may not be initialized yet, or may already have been unloaded.";
 
 exports.testSimplePageCreation = function(assert, done) {
   let page = new Page({
     contentScript: "self.postMessage(window.location.href)",
@@ -153,33 +151,24 @@ exports.testValidateOptions = function(a
     "Validation correctly denied a non-function onMessage."
   );
 
   assert.pass("Options validation is working.");
 }
 
 exports.testContentAndAllowGettersAndSetters = function(assert, done) {
   let content = "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3;</script>";
-
-  // Load up the page with testURI initially for the resource:// principal,
-  // then load the actual data:* content, as data:* URIs no longer
-  // have localStorage
   let page = Page({
-    contentURL: testURI,
-    contentScript: "if (window.location.href==='"+testURI+"')" +
-      "  self.postMessage('reload');" +
-      "else " +
-      "  self.postMessage(window.localStorage.allowScript)",
+    contentURL: content,
+    contentScript: "self.postMessage(window.localStorage.allowScript)",
     contentScriptWhen: "end",
     onMessage: step0
   });
 
   function step0(message) {
-    if (message === 'reload')
-      return page.contentURL = content;
     assert.equal(message, "3",
                      "Correct value expected for allowScript - 3");
     assert.equal(page.contentURL, content,
                      "Correct content expected");
     page.removeListener('message', step0);
     page.on('message', step1);
     page.allow = { script: false };
     page.contentURL = content =
--- a/addon-sdk/source/test/test-places-bookmarks.js
+++ b/addon-sdk/source/test/test-places-bookmarks.js
@@ -20,19 +20,19 @@ const { defer: async } = require('sdk/la
 const { before, after } = require('sdk/test/utils');
 
 const {
   Bookmark, Group, Separator,
   save, search, remove,
   MENU, TOOLBAR, UNSORTED
 } = require('sdk/places/bookmarks');
 const {
-  invalidResolve, invalidReject, createTree,
-  compareWithHost, createBookmark, createBookmarkItem,
-  createBookmarkTree, addVisits, resetPlaces
+  invalidResolve, invalidReject, clearBookmarks, createTree,
+  compareWithHost, clearAllBookmarks, createBookmark, createBookmarkItem,
+  createBookmarkTree, addVisits
 } = require('./places-helper');
 const { promisedEmitter } = require('sdk/places/utils');
 const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
                     getService(Ci.nsINavBookmarksService);
 const tagsrv = Cc['@mozilla.org/browser/tagging-service;1'].
                     getService(Ci.nsITaggingService);
 
 exports.testDefaultFolders = function (assert) {
@@ -936,18 +936,23 @@ exports.testCheckSaveOrder = function (a
   saveP(bookmarks).then(results => {
     for (let i = 0; i < bookmarks.length; i++)
       assert.equal(results[i].url, bookmarks[i].url,
         'correct ordering of bookmark results');
     done();
   });
 };
 
-before(exports, (name, assert, done) => resetPlaces(done));
-after(exports, (name, assert, done) => resetPlaces(done));
+before(exports, name => {
+  clearAllBookmarks();
+});
+
+after(exports, name => {
+  clearAllBookmarks();
+});
 
 function saveP () {
   return promisedEmitter(save.apply(null, Array.slice(arguments)));
 }
 
 function searchP () {
   return promisedEmitter(search.apply(null, Array.slice(arguments)));
 }
--- a/addon-sdk/source/test/test-places-favicon.js
+++ b/addon-sdk/source/test/test-places-favicon.js
@@ -14,17 +14,17 @@ const { Cc, Ci, Cu } = require('chrome')
 const { getFavicon } = require('sdk/places/favicon');
 const tabs = require('sdk/tabs');
 const open = tabs.open;
 const port = 8099;
 const host = 'http://localhost:' + port;
 const { onFaviconChange, serve, binFavicon } = require('./favicon-helpers');
 const { once } = require('sdk/system/events');
 const { defer } = require('sdk/core/promise');
-const { resetPlaces } = require('./places-helper');
+const { clearHistory } = require('./places-helper');
 const faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                          getService(Ci.nsIFaviconService);
 
 exports.testStringGetFaviconCallbackSuccess = function (assert, done) {
   let name = 'callbacksuccess'
   let srv = makeServer(name);
   let url = host + '/' + name + '.html';
   let favicon = host + '/' + name + '.ico';
@@ -176,15 +176,15 @@ function waitAndExpire (url) {
     });
     faviconService.expireAllFavicons();
   });
   return deferred.promise;
 }
 
 function complete(tab, srv, done) {
   tab.close(function () {
-    resetPlaces(() => {
+    clearHistory(() => {
       srv.stop(done);
     });
   });
 }
 
 require("test").run(exports);
--- a/addon-sdk/source/test/test-places-history.js
+++ b/addon-sdk/source/test/test-places-history.js
@@ -9,25 +9,26 @@ module.metadata = {
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { defer, all } = require('sdk/core/promise');
 const { has } = require('sdk/util/array');
 const { setTimeout } = require('sdk/timers');
 const { before, after } = require('sdk/test/utils');
-const { set } = require('sdk/preferences/service');
 const {
   search 
 } = require('sdk/places/history');
 const {
-  invalidResolve, invalidReject, createTree,
-  compareWithHost, addVisits, resetPlaces
+  invalidResolve, invalidReject, clearBookmarks, createTree,
+  compareWithHost, clearAllBookmarks, addVisits, clearHistory
 } = require('./places-helper');
 const { promisedEmitter } = require('sdk/places/utils');
+const hsrv = Cc['@mozilla.org/browser/nav-history-service;1'].
+              getService(Ci.nsINavHistoryService);
 
 exports.testEmptyQuery = function (assert, done) {
   let within = toBeWithin();
   addVisits([
     'http://simplequery-1.com', 'http://simplequery-2.com'
   ]).then(searchP).then(results => {
     assert.equal(results.length, 2, 'Correct number of entries returned');
     assert.equal(results[0].url, 'http://simplequery-1.com/',
@@ -233,16 +234,21 @@ exports.testEmitters = function (assert,
 function toBeWithin (range) {
   range = range || 2000;
   var current = new Date() * 1000; // convert to microseconds
   return compared => { 
     return compared - current < range;
   };
 }
 
+function clear (done) {
+  clearAllBookmarks();
+  clearHistory(done);
+}
+
 function searchP () {
   return promisedEmitter(search.apply(null, Array.slice(arguments)));
 }
 
-before(exports, (name, assert, done) => resetPlaces(done));
-after(exports, (name, assert, done) => resetPlaces(done));
+before(exports, (name, assert, done) => clear(done));
+after(exports, (name, assert, done) => clear(done));
 
 require('test').run(exports);
--- a/addon-sdk/source/test/test-places-host.js
+++ b/addon-sdk/source/test/test-places-host.js
@@ -9,33 +9,32 @@ module.metadata = {
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { defer, all } = require('sdk/core/promise');
 const { setTimeout } = require('sdk/timers');
 const { newURI } = require('sdk/url/utils');
 const { send } = require('sdk/addon/events');
-const { set } = require('sdk/preferences/service');
-const { before, after } = require('sdk/test/utils');
 
 require('sdk/places/host/host-bookmarks');
 require('sdk/places/host/host-tags');
 require('sdk/places/host/host-query');
 const {
-  invalidResolve, invalidReject, createTree,
-  compareWithHost, createBookmark, createBookmarkTree, resetPlaces
+  invalidResolve, invalidReject, clearBookmarks, createTree,
+  compareWithHost, clearAllBookmarks, createBookmark, createBookmarkTree
 } = require('./places-helper');
 
 const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
                     getService(Ci.nsINavBookmarksService);
 const hsrv = Cc['@mozilla.org/browser/nav-history-service;1'].
               getService(Ci.nsINavHistoryService);
 const tagsrv = Cc['@mozilla.org/browser/tagging-service;1'].
               getService(Ci.nsITaggingService);
+clearAllBookmarks();
 
 exports.testBookmarksCreate = function (assert, done) {
   let items = [{
     title: 'my title',
     url: 'http://moz.com',
     tags: ['some', 'tags', 'yeah'],
     type: 'bookmark'
   }, {
@@ -47,16 +46,17 @@ exports.testBookmarksCreate = function (
     group: bmsrv.unfiledBookmarksFolder
   }];
   
   all(items.map(function (item) {
     return send('sdk-places-bookmarks-create', item).then(function (data) {
       compareWithHost(assert, data);
     }, invalidReject(assert));
   })).then(function () {
+    clearAllBookmarks();
     done();
   }, invalidReject(assert));
 };
 
 exports.testBookmarksCreateFail = function (assert, done) {
   let items = [{
     title: 'my title',
     url: 'not-a-url',
@@ -67,16 +67,17 @@ exports.testBookmarksCreateFail = functi
   }, {
     group: bmsrv.unfiledBookmarksFolder
   }];
   all(items.map(function (item) {
     return send('sdk-places-bookmarks-create', item).then(null, function (reason) {
       assert.ok(reason, 'bookmark create should fail');
     });
   })).then(function () {
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testBookmarkLastUpdated = function (assert, done) {
   let timestamp;
   let item;
   createBookmark().then(function (data) {
@@ -88,31 +89,33 @@ exports.testBookmarkLastUpdated = functi
     item.title = 'updated mozilla';
     return send('sdk-places-bookmarks-save', item).then(function (data) {
       let deferred = defer();
       setTimeout(function () deferred.resolve(data), 100);
       return deferred.promise;
     });
   }).then(function (data) {
     assert.ok(data.updated > timestamp, 'time has elapsed and updated the updated property');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testBookmarkRemove = function (assert, done) {
   let id;
   createBookmark().then(function (data) {
     id = data.id;
     compareWithHost(assert, data); // ensure bookmark exists
     bmsrv.getItemTitle(id); // does not throw an error
     return send('sdk-places-bookmarks-remove', data);
   }).then(function () {
     assert.throws(function () {
       bmsrv.getItemTitle(id);
     }, 'item should no longer exist');
+    clearAllBookmarks();
     done();
   }, console.error);
 };
 
 exports.testBookmarkGet = function (assert, done) {
   let bookmark;
   createBookmark().then(function (data) {
     bookmark = data;
@@ -125,16 +128,17 @@ exports.testBookmarkGet = function (asse
             'correctly fetched tag ' + tag);
         }
         assert.equal(bookmark.tags.length, data.tags.length,
           'same amount of tags');
       }
       else
         assert.equal(bookmark[prop], data[prop], 'correctly fetched ' + prop);
     });
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsTag = function (assert, done) {
   let url;
   createBookmark().then(function (data) {
     url = data.url;
@@ -142,16 +146,17 @@ exports.testTagsTag = function (assert, 
       url: data.url, tags: ['mozzerella', 'foxfire']
     });
   }).then(function () {
     let tags = tagsrv.getTagsForURI(newURI(url));
     assert.ok(~tags.indexOf('mozzerella'), 'first tag found');
     assert.ok(~tags.indexOf('foxfire'), 'second tag found');
     assert.ok(~tags.indexOf('firefox'), 'default tag found');
     assert.equal(tags.length, 3, 'no extra tags');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsUntag = function (assert, done) {
   let item;
   createBookmark({tags: ['tag1', 'tag2', 'tag3']}).then(function (data) {
     item = data;
@@ -161,46 +166,49 @@ exports.testTagsUntag = function (assert
     });
   }).then(function () {
     let tags = tagsrv.getTagsForURI(newURI(item.url));
     assert.ok(~tags.indexOf('tag1'), 'first tag persisted');
     assert.ok(~tags.indexOf('tag3'), 'second tag persisted');
     assert.ok(!~tags.indexOf('firefox'), 'first tag removed');
     assert.ok(!~tags.indexOf('tag2'), 'second tag removed');
     assert.equal(tags.length, 2, 'no extra tags');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsGetURLsByTag = function (assert, done) {
   let item;
   createBookmark().then(function (data) {
     item = data;
     return send('sdk-places-tags-get-urls-by-tag', {
       tag: 'firefox'
     });
   }).then(function(urls) {
     assert.equal(item.url, urls[0], 'returned correct url');
     assert.equal(urls.length, 1, 'returned only one url');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsGetTagsByURL = function (assert, done) {
   let item;
   createBookmark({ tags: ['firefox', 'mozilla', 'metal']}).then(function (data) {
     item = data;
     return send('sdk-places-tags-get-tags-by-url', {
       url: data.url,
     });
   }).then(function(tags) {
     assert.ok(~tags.indexOf('firefox'), 'returned first tag');
     assert.ok(~tags.indexOf('mozilla'), 'returned second tag');
     assert.ok(~tags.indexOf('metal'), 'returned third tag');
     assert.equal(tags.length, 3, 'returned all tags');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testHostQuery = function (assert, done) {
   all([
     createBookmark({ url: 'http://firefox.com', tags: ['firefox', 'mozilla'] }),
     createBookmark({ url: 'http://mozilla.com', tags: ['mozilla'] }),
@@ -215,16 +223,17 @@ exports.testHostQuery = function (assert
     assert.equal(results[0].url, 'http://mozilla.com/', 'is sorted by URI asc');
     return send('sdk-places-query', {
       queries: { tags: ['mozilla'] }, 
       options: { sortingMode: 5, queryType: 1 } // sort by URI descending, bookmarks only
     });
   }).then(results => {
     assert.equal(results.length, 2, 'should only return two');
     assert.equal(results[0].url, 'http://firefox.com/', 'is sorted by URI desc');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testHostMultiQuery = function (assert, done) {
   all([
     createBookmark({ url: 'http://firefox.com', tags: ['firefox', 'mozilla'] }),
     createBookmark({ url: 'http://mozilla.com', tags: ['mozilla'] }),
@@ -239,38 +248,37 @@ exports.testHostMultiQuery = function (a
     assert.equal(results[0].url, 'http://firefox.com/', 'should match URL or tag');
     assert.equal(results[1].url, 'http://thunderbird.com/', 'should match URL or tag');
     return send('sdk-places-query', {
       queries: [{ tags: ['firefox'], url: 'http://mozilla.com/' }],
       options: { sortingMode: 5, queryType: 1 } // sort by URI descending, bookmarks only
     });
   }).then(results => {
     assert.equal(results.length, 0, 'query props should be AND\'d');
+    clearAllBookmarks();
     done();
   });
 };
 
 exports.testGetAllBookmarks = function (assert, done) {
   createBookmarkTree().then(() => { 
     return send('sdk-places-bookmarks-get-all', {});
   }).then(res => {
     assert.equal(res.length, 8, 'all bookmarks returned');
+    clearAllBookmarks();
     done();
   }, console.error);
 };
 
 exports.testGetAllChildren = function (assert, done) {
   createBookmarkTree().then(results => {
     return send('sdk-places-bookmarks-get-children', {
       id: results.filter(({title}) => title === 'mozgroup')[0].id
     });
   }).then(results => {
     assert.equal(results.length, 5,
       'should return all children and folders at a single depth');
+    clearAllBookmarks();
     done();
   });
 };
 
-
-before(exports, (name, assert, done) => resetPlaces(done));
-after(exports, (name, assert, done) => resetPlaces(done));
-
 require('test').run(exports);
--- a/addon-sdk/source/test/test-plain-text-console.js
+++ b/addon-sdk/source/test/test-plain-text-console.js
@@ -31,134 +31,121 @@ exports.testPlainTextConsole = function(
   prefs.reset(ADDON_LOG_LEVEL_PREF);
 
   var Console = require("sdk/console/plain-text").PlainTextConsole;
   var con = new Console(print);
 
   test.pass("PlainTextConsole instantiates");
 
   con.log('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, 1, Array [2,3,4]\n",
+  test.assertEqual(lastPrint(), "info: " + name + ": testing 1 2,3,4\n",
                    "PlainTextConsole.log() must work.");
 
   con.info('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "console.info: " + name + ": testing, 1, Array [2,3,4]\n",
+  test.assertEqual(lastPrint(), "info: " + name + ": testing 1 2,3,4\n",
                    "PlainTextConsole.info() must work.");
 
   con.warn('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "console.warn: " + name + ": testing, 1, Array [2,3,4]\n",
+  test.assertEqual(lastPrint(), "warn: " + name + ": testing 1 2,3,4\n",
                    "PlainTextConsole.warn() must work.");
 
   con.error('testing', 1, [2, 3, 4]);
-  test.assertEqual(prints[0], "console.error: " + name + ": \n",
+  test.assertEqual(lastPrint(), "error: " + name + ": testing 1 2,3,4\n",
                    "PlainTextConsole.error() must work.");
-  test.assertEqual(prints[1], "  testing\n")
-  test.assertEqual(prints[2], "  1\n")
-  test.assertEqual(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
-  prints = [];
 
   con.debug('testing', 1, [2, 3, 4]);
-  test.assertEqual(prints[0], "console.debug: " + name + ": \n",
+  test.assertEqual(lastPrint(), "debug: " + name + ": testing 1 2,3,4\n",
                    "PlainTextConsole.debug() must work.");
-  test.assertEqual(prints[1], "  testing\n")
-  test.assertEqual(prints[2], "  1\n")
-  test.assertEqual(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
-  prints = [];
 
   con.log('testing', undefined);
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, undefined\n",
+  test.assertEqual(lastPrint(), "info: " + name + ": testing undefined\n",
                    "PlainTextConsole.log() must stringify undefined.");
 
   con.log('testing', null);
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, null\n",
+  test.assertEqual(lastPrint(), "info: " + name + ": testing null\n",
                    "PlainTextConsole.log() must stringify null.");
 
-  // TODO: Fix console.jsm to detect custom toString.
   con.log("testing", { toString: function() "obj.toString()" });
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, {}\n",
-                   "PlainTextConsole.log() doesn't printify custom toString.");
+  test.assertEqual(lastPrint(), "info: " + name + ": testing obj.toString()\n",
+                   "PlainTextConsole.log() must stringify custom toString.");
 
   con.log("testing", { toString: function() { throw "fail!"; } });
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, {}\n",
+  test.assertEqual(lastPrint(), "info: " + name + ": testing <toString() error>\n",
                    "PlainTextConsole.log() must stringify custom bad toString.");
 
-  
   con.exception(new Error("blah"));
 
-  
-  test.assertEqual(prints[0], "console.error: " + name + ": \n");
-  let tbLines = prints[1].split("\n");
-  test.assertEqual(tbLines[0], "  Message: Error: blah");
-  test.assertEqual(tbLines[1], "  Stack:");
-  test.assert(prints[1].indexOf(module.uri + ":84") !== -1);
-  prints = []
+  var tbLines = prints[0].split("\n");
+  test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred.");
+  test.assertEqual(tbLines[1], "Error: blah");
+  test.assertEqual(tbLines[2], module.uri + " 74");
+  test.assertEqual(tbLines[3], "Traceback (most recent call last):");
 
+  prints = [];
   try {
     loadSubScript("invalid-url", {});
     test.fail("successed in calling loadSubScript with invalid-url");
   }
   catch(e) {
     con.exception(e);
   }
-  test.assertEqual(prints[0], "console.error: " + name + ": \n");
-  test.assertEqual(prints[1], "  Error creating URI (invalid URL scheme?)\n");
+  var tbLines = prints[0].split("\n");
+  test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred.");
+  test.assertEqual(tbLines[1], "Error creating URI (invalid URL scheme?)");
+  test.assertEqual(tbLines[2], "Traceback (most recent call last):");
+
   prints = [];
-
   con.trace();
-  let tbLines = prints[0].split("\n");
-  test.assertEqual(tbLines[0], "console.trace: " + name + ": ");
-  test.assert(tbLines[1].indexOf("_ain-text-console.js 105") == 0);
-  prints = [];
+  tbLines = prints[0].split("\n");
+  test.assertEqual(tbLines[0], "info: " + name + ": Traceback (most recent call last):");
+  test.assertEqual(tbLines[tbLines.length - 4].trim(), "con.trace();");
 
   // Whether or not console methods should print at the various log levels,
   // structured as a hash of levels, each of which contains a hash of methods,
   // each of whose value is whether or not it should print, i.e.:
   // { [level]: { [method]: [prints?], ... }, ... }.
   let levels = {
     all:   { debug: true,  log: true,  info: true,  warn: true,  error: true  },
     debug: { debug: true,  log: true,  info: true,  warn: true,  error: true  },
     info:  { debug: false, log: true,  info: true,  warn: true,  error: true  },
     warn:  { debug: false, log: false, info: false, warn: true,  error: true  },
     error: { debug: false, log: false, info: false, warn: false, error: true  },
     off:   { debug: false, log: false, info: false, warn: false, error: false },
   };
 
   // The messages we use to test the various methods, as a hash of methods.
   let messages = {
-    debug: "console.debug: " + name + ": \n  \n",
-    log: "console.log: " + name + ": \n",
-    info: "console.info: " + name + ": \n",
-    warn: "console.warn: " + name + ": \n",
-    error: "console.error: " + name + ": \n  \n",
+    debug: "debug: " + name + ": \n",
+    log: "info: " + name + ": \n",
+    info: "info: " + name + ": \n",
+    warn: "warn: " + name + ": \n",
+    error: "error: " + name + ": \n",
   };
 
   for (let level in levels) {
     let methods = levels[level];
     for (let method in methods) {
       // We have to reset the log level pref each time we run the test
       // because the test runner relies on the console to print test output,
       // and test results would not get printed to the console for some
       // values of the pref.
       prefs.set(SDK_LOG_LEVEL_PREF, level);
       con[method]("");
       prefs.set(SDK_LOG_LEVEL_PREF, "all");
-      test.assertEqual(prints.join(""), 
-                       (methods[method] ? messages[method] : ""),
+      test.assertEqual(lastPrint(), (methods[method] ? messages[method] : null),
                        "at log level '" + level + "', " + method + "() " +
                        (methods[method] ? "prints" : "doesn't print"));
-      prints = [];
     }
   }
 
   prefs.set(SDK_LOG_LEVEL_PREF, "off");
   prefs.set(ADDON_LOG_LEVEL_PREF, "all");
   con.debug("");
-  test.assertEqual(prints.join(""), messages["debug"],
+  test.assertEqual(lastPrint(), messages["debug"],
                    "addon log level 'all' overrides SDK log level 'off'");
-  prints = [];
 
   prefs.set(SDK_LOG_LEVEL_PREF, "all");
   prefs.set(ADDON_LOG_LEVEL_PREF, "off");
   con.error("");
   prefs.reset(ADDON_LOG_LEVEL_PREF);
   test.assertEqual(lastPrint(), null,
                    "addon log level 'off' overrides SDK log level 'all'");
 
--- a/addon-sdk/source/test/test-system-events.js
+++ b/addon-sdk/source/test/test-system-events.js
@@ -5,18 +5,16 @@
 const events = require("sdk/system/events");
 const self = require("sdk/self");
 const { Cc, Ci, Cu } = require("chrome");
 const { setTimeout } = require("sdk/timers");
 const { Loader, LoaderWithHookedConsole2 } = require("sdk/test/loader");
 const nsIObserverService = Cc["@mozilla.org/observer-service;1"].
                            getService(Ci.nsIObserverService);
 
-let isConsoleEvent = (topic) =>
-  !!~["console-api-log-event", "console-storage-cache-event"].indexOf(topic)
 
 exports["test basic"] = function(assert) {
   let type = Date.now().toString(32);
 
   let timesCalled = 0;
   function handler(subject, data) { timesCalled++; };
 
   events.on(type, handler);
@@ -45,20 +43,20 @@ exports["test error reporting"] = functi
   let lineNumber;
   try { brokenHandler() } catch (error) { lineNumber = error.lineNumber }
 
   let errorType = Date.now().toString(32);
 
   events.on(errorType, brokenHandler);
   events.emit(errorType, { data: "yo yo" });
 
-  assert.equal(messages.length, 2, "Got an exception");
-  assert.equal(messages[0], "console.error: " + self.name + ": \n",
-               "error is logged");
-  let text = messages[1];
+  assert.equal(messages.length, 1, "Got an exception");
+  let text = messages[0];
+  assert.ok(text.indexOf(self.name + ": An exception occurred.") >= 0,
+            "error is logged");
   assert.ok(text.indexOf("Error: foo") >= 0, "error message is logged");
   assert.ok(text.indexOf(module.uri) >= 0, "module uri is logged");
   assert.ok(text.indexOf(lineNumber) >= 0, "error line is logged");
 
   events.off(errorType, brokenHandler);
 
   loader.unload();
 };
@@ -101,19 +99,16 @@ exports["test handle nsIObserverService 
 
   let type = Date.now().toString(32);
   let timesCalled = 0;
   let lastSubject = null;
   let lastData = null;
   let lastType = null;
 
   function handler({ subject, data, type }) {
-    // Ignores internal console events
-    if (isConsoleEvent(type))
-      return;
     timesCalled++;
     lastSubject = subject;
     lastData = data;
     lastType = type;
   };
 
   events.on(type, handler);
   nsIObserverService.notifyObservers(uri, type, "some data");
@@ -168,35 +163,33 @@ exports["test emit to nsIObserverService
   let lastTopic = null;
 
   var topic = Date.now().toString(32)
   let nsIObserver = {
     QueryInterface: function() {
       return nsIObserver;
     },
     observe: function(subject, topic, data) {
-      // Ignores internal console events
-      if (isConsoleEvent(topic))
-        return;
       timesCalled = timesCalled + 1;
       lastSubject = subject;
       lastData = data;
       lastTopic = topic;
     }
   };
 
   nsIObserverService.addObserver(nsIObserver, topic, false);
 
   events.emit(topic, { subject: uri, data: "some data" });
 
   assert.equal(timesCalled, 1, "emit notifies observers");
   assert.equal(lastTopic, topic, "event type is notification topic");
   assert.equal(lastSubject.wrappedJSObject.object, uri,
                "event.subject is notification subject");
   assert.equal(lastData, "some data", "event.data is notification data");
+
   function customSubject() {}
   function customData() {}
   events.emit(topic, { subject: customSubject, data: customData });
 
   assert.equal(timesCalled, 2, "emit notifies observers");
   assert.equal(lastTopic, topic, "event.type is notification");
   assert.equal(lastSubject.wrappedJSObject.object, customSubject,
                "event.subject is notification subject");
@@ -208,21 +201,20 @@ exports["test emit to nsIObserverService
 
   assert.equal(timesCalled, 2, "removed observers no longer invoked");
 
   nsIObserverService.addObserver(nsIObserver, "*", false);
 
   events.emit(topic, { data: "data again" });
 
   assert.equal(timesCalled, 3, "emit notifies * observers");
-
   assert.equal(lastTopic, topic, "event.type is notification");
   assert.equal(lastSubject, null,
                "event.subject is notification subject");
   assert.equal(lastData, "data again", "event.data is notification data");
 
   nsIObserverService.removeObserver(nsIObserver, "*");
-  
+
   events.emit(topic, { data: "last data" });
   assert.equal(timesCalled, 3, "removed observers no longer invoked");
 }
 
 require("test").run(exports);
--- a/addon-sdk/source/test/test-tab-utils.js
+++ b/addon-sdk/source/test/test-tab-utils.js
@@ -1,50 +1,57 @@
 'use strict';
 
 const { getTabs } = require('sdk/tabs/utils');
 const { isGlobalPBSupported, isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
 const { browserWindows } = require('sdk/windows');
 const tabs = require('sdk/tabs');
 const { pb } = require('./private-browsing/helper');
 const { isPrivate } = require('sdk/private-browsing');
-const { openTab, closeTab, getTabContentWindow, getOwnerWindow } = require('sdk/tabs/utils');
+const { openTab, closeTab, getTabContentWindow } = require('sdk/tabs/utils');
 const { open, close } = require('sdk/window/helpers');
 const { windows } = require('sdk/window/utils');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { fromIterator } = require('sdk/util/array');
 
-if (isWindowPBSupported) {
+if (isGlobalPBSupported) {
   exports.testGetTabs = function(assert, done) {
-    let tabCount = getTabs().length;
-    let windowCount = browserWindows.length;
-
+    pb.once('start', function() {
+      tabs.open({
+        url: 'about:blank',
+        inNewWindow: true,
+        onOpen: function(tab) {
+          assert.equal(getTabs().length, 2, 'there are two tabs');
+          assert.equal(browserWindows.length, 2, 'there are two windows');
+          pb.once('stop', function() {
+            done();
+          });
+          pb.deactivate();
+        }
+      });
+    });
+    pb.activate();
+  };
+}
+else if (isWindowPBSupported) {
+  exports.testGetTabs = function(assert, done) {
     open(null, {
         features: {
         private: true,
         toolbar: true,
         chrome: true
       }
     }).then(function(window) {
       assert.ok(isPrivate(window), 'new tab is private');
-
-      assert.equal(getTabs().length, tabCount, 'there are no new tabs found');
-      getTabs().forEach(function(tab) {
-        assert.equal(isPrivate(tab), false, 'all found tabs are not private');
-        assert.equal(isPrivate(getOwnerWindow(tab)), false, 'all found tabs are not private');
-        assert.equal(isPrivate(getTabContentWindow(tab)), false, 'all found tabs are not private');
+      assert.equal(getTabs().length, 1, 'there is one tab found');
+      assert.equal(browserWindows.length, 1, 'there is one window found');
+      fromIterator(browserWindows).forEach(function(window) {
+        assert.ok(!isPrivate(window), 'all found windows are not private');
       });
-
-      assert.equal(browserWindows.length, windowCount, 'there are no new windows found');
-      fromIterator(browserWindows).forEach(function(window) {
-        assert.equal(isPrivate(window), false, 'all found windows are not private');
-      });
-
       assert.equal(windows(null, {includePrivate: true}).length, 2, 'there are really two windows');
-
       close(window).then(done);
     });
   };
 }
 else if (isTabPBSupported) {
   exports.testGetTabs = function(assert, done) {
     let startTabCount = getTabs().length;
     let tab = openTab(getMostRecentBrowserWindow(), 'about:blank', {
@@ -59,9 +66,12 @@ else if (isTabPBSupported) {
                  'the last tab is the opened tab');
     assert.equal(browserWindows.length, 1, 'there is only one window');
     closeTab(tab);
 
     done();
   };
 }
 
+// Test disabled because of bug 855771
+module.exports = {};
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-timer.js
+++ b/addon-sdk/source/test/test-timer.js
@@ -1,177 +1,131 @@
 /* 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/. */
 
 var timer = require("sdk/timers");
 const { Loader } = require("sdk/test/loader");
 
-exports.testSetTimeout = function(assert, end) {
+exports.testSetTimeout = function(test) {
   timer.setTimeout(function() {
-    assert.pass("testSetTimeout passed");
-    end();
+    test.pass("testSetTimeout passed");
+    test.done();
   }, 1);
+  test.waitUntilDone();
 };
 
-exports.testParamedSetTimeout = function(assert, end) {
+exports.testParamedSetTimeout = function(test) {
   let params = [1, 'foo', { bar: 'test' }, null, undefined];
   timer.setTimeout.apply(null, [function() {
-    assert.equal(arguments.length, params.length);
+    test.assertEqual(arguments.length, params.length);
     for (let i = 0, ii = params.length; i < ii; i++)
-      assert.equal(params[i], arguments[i]);
-    end();
+      test.assertEqual(params[i], arguments[i]);
+    test.done();
   }, 1].concat(params));
+  test.waitUntilDone();
 };
 
-exports.testClearTimeout = function(assert, end) {
+exports.testClearTimeout = function(test) {
   var myFunc = function myFunc() {
-    assert.fail("myFunc() should not be called in testClearTimeout");
+    test.fail("myFunc() should not be called in testClearTimeout");
   };
   var id = timer.setTimeout(myFunc, 1);
   timer.setTimeout(function() {
-    assert.pass("testClearTimeout passed");
-    end();
+    test.pass("testClearTimeout passed");
+    test.done();
   }, 2);
   timer.clearTimeout(id);
+  test.waitUntilDone();
 };
 
-exports.testParamedClearTimeout = function(assert, end) {
+exports.testParamedClearTimeout = function(test) {
   let params = [1, 'foo', { bar: 'test' }, null, undefined];
   var myFunc = function myFunc() {
-    assert.fail("myFunc() should not be called in testClearTimeout");
+    test.fail("myFunc() should not be called in testClearTimeout");
   };
   var id = timer.setTimeout(myFunc, 1);
   timer.setTimeout.apply(null, [function() {
-    assert.equal(arguments.length, params.length);
+    test.assertEqual(arguments.length, params.length);
     for (let i = 0, ii = params.length; i < ii; i++)
-      assert.equal(params[i], arguments[i]);
-    end();
+      test.assertEqual(params[i], arguments[i]);
+    test.done();
   }, 1].concat(params));
   timer.clearTimeout(id);
+  test.waitUntilDone();
 };
 
-exports.testSetInterval = function (assert, end) {
+exports.testSetInterval = function (test) {
   var count = 0;
   var id = timer.setInterval(function () {
     count++;
     if (count >= 5) {
       timer.clearInterval(id);
-      assert.pass("testSetInterval passed");
-      end();
+      test.pass("testSetInterval passed");
+      test.done();
     }
   }, 1);
+  test.waitUntilDone();
 };
 
-exports.testParamedSetInerval = function(assert, end) {
+exports.testParamedSetInerval = function(test) {
   let params = [1, 'foo', { bar: 'test' }, null, undefined];
   let count = 0;
   let id = timer.setInterval.apply(null, [function() {
     count ++;
     if (count < 5) {
-      assert.equal(arguments.length, params.length);
+      test.assertEqual(arguments.length, params.length);
       for (let i = 0, ii = params.length; i < ii; i++)
-        assert.equal(params[i], arguments[i]);
+        test.assertEqual(params[i], arguments[i]);
     } else {
       timer.clearInterval(id);
-      end();
+      test.done();
     }
   }, 1].concat(params));
+  test.waitUntilDone();
 };
 
-exports.testClearInterval = function (assert, end) {
+exports.testClearInterval = function (test) {
   timer.clearInterval(timer.setInterval(function () {
-    assert.fail("setInterval callback should not be called");
+    test.fail("setInterval callback should not be called");
   }, 1));
   var id = timer.setInterval(function () {
     timer.clearInterval(id);
-    assert.pass("testClearInterval passed");
-    end();
+    test.pass("testClearInterval passed");
+    test.done();
   }, 2);
+  test.waitUntilDone();
 };
 
-exports.testParamedClearInterval = function(assert, end) {
+exports.testParamedClearInterval = function(test) {
   timer.clearInterval(timer.setInterval(function () {
-    assert.fail("setInterval callback should not be called");
+    test.fail("setInterval callback should not be called");
   }, 1, timer, {}, null));
 
   let id = timer.setInterval(function() {
     timer.clearInterval(id);
-    assert.equal(3, arguments.length);
-    end();
+    test.assertEqual(3, arguments.length);
+    test.done();
   }, 2, undefined, 'test', {});
+  test.waitUntilDone();
 };
 
 
-exports.testImmediate = function(assert, end) {
-  let actual = [];
-  let ticks = 0;
-  timer.setImmediate(function(...params) {
-    actual.push(params);
-    assert.equal(ticks, 1, "is a next tick");
-    assert.deepEqual(actual, [["start", "immediates"]]);
-  }, "start", "immediates");
-
-  timer.setImmediate(function(...params) {
-    actual.push(params);
-    assert.deepEqual(actual, [["start", "immediates"],
-                                  ["added"]]);
-    assert.equal(ticks, 1, "is a next tick");
-    timer.setImmediate(function(...params) {
-      actual.push(params);
-      assert.equal(ticks, 2, "is second tick");
-      assert.deepEqual(actual, [["start", "immediates"],
-                                    ["added"],
-                                    [],
-                                    ["last", "immediate", "handler"],
-                                    ["side-effect"]]);
-      end();
-    }, "side-effect");
-  }, "added");
-
-  timer.setImmediate(function(...params) {
-    actual.push(params);
-    assert.equal(ticks, 1, "is a next tick");
-    assert.deepEqual(actual, [["start", "immediates"],
-                              ["added"],
-                              []]);
-    timer.clearImmediate(removeID);
-  });
-
-  function removed() {
-    assert.fail("should be removed");
-  }
-  let removeID = timer.setImmediate(removed);
-
-  timer.setImmediate(function(...params) {
-    actual.push(params);
-    assert.equal(ticks, 1, "is a next tick");
-    assert.deepEqual(actual, [["start", "immediates"],
-                              ["added"],
-                              [],
-                              ["last", "immediate", "handler"]]);
-    ticks = ticks + 1;
-  }, "last", "immediate", "handler");
-
-
-  ticks = ticks + 1;
-};
-
-exports.testUnload = function(assert, end) {
+exports.testUnload = function(test) {
   var loader = Loader(module);
   var sbtimer = loader.require("sdk/timers");
 
   var myFunc = function myFunc() {
-    assert.fail("myFunc() should not be called in testUnload");
+    test.fail("myFunc() should not be called in testUnload");
   };
 
   sbtimer.setTimeout(myFunc, 1);
   sbtimer.setTimeout(myFunc, 1, 'foo', 4, {}, undefined);
   sbtimer.setInterval(myFunc, 1);
   sbtimer.setInterval(myFunc, 1, {}, null, 'bar', undefined, 87);
   loader.unload();
   timer.setTimeout(function() {
-    assert.pass("timer testUnload passed");
-    end();
+    test.pass("timer testUnload passed");
+    test.done();
   }, 2);
+  test.waitUntilDone();
 };
 
-require("test").run(exports);
\ No newline at end of file
--- a/addon-sdk/source/test/test-window-events.js
+++ b/addon-sdk/source/test/test-window-events.js
@@ -1,11 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 "use strict";
 
 const { Loader } = require("sdk/test/loader");
 const { open, getMostRecentBrowserWindow, getOuterId } = require("sdk/window/utils");
 
 exports["test browser events"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/window/events");
@@ -47,9 +48,9 @@ if (require("sdk/system/xul-app").is("Fe
     "test Unsupported Test": function UnsupportedTest (assert) {
         assert.pass(
           "Skipping this test until Fennec support is implemented." +
           "See bug 793071");
     }
   }
 }
 
-require("sdk/test").run(exports);
+require("test").run(exports);
--- a/addon-sdk/source/test/test-window-utils2.js
+++ b/addon-sdk/source/test/test-window-utils2.js
@@ -56,44 +56,16 @@ exports['test new top window with option
   assert.equal(window.innerHeight, 100, 'height is set');
   assert.equal(window.innerWidth, 200, 'height is set');
   assert.equal(window.toolbar.visible, true, 'toolbar was set');
 
   // Wait for the window unload before ending test
   close(window).then(done);
 };
 
-exports['test new top window with various URIs'] = function(assert, done) {
-  let msg = 'only chrome, resource and data uris are allowed';
-  assert.throws(function () {
-    open('foo');
-  }, msg);
-  assert.throws(function () {
-    open('http://foo');
-  }, msg);
-  assert.throws(function () {
-    open('https://foo');
-  }, msg); 
-  assert.throws(function () {
-    open('ftp://foo');
-  }, msg);
-  assert.throws(function () {
-    open('//foo');
-  }, msg);
-
-  let chromeWindow = open('chrome://foo/content/');
-  assert.ok(~windows().indexOf(chromeWindow), 'chrome URI works');
-  
-  let resourceWindow = open('resource://foo');
-  assert.ok(~windows().indexOf(resourceWindow), 'resource URI works');
-
-  // Wait for the window unload before ending test
-  close(chromeWindow).then(close.bind(null, resourceWindow)).then(done);
-};
-
 exports.testBackgroundify = function(assert, done) {
   let window = open('data:text/html;charset=utf-8,backgroundy');
   assert.ok(~windows().indexOf(window),
             'window is in the list of windows');
   let backgroundy = backgroundify(window);
   assert.equal(backgroundy, window, 'backgroundify returs give window back');
   assert.ok(!~windows().indexOf(window),
             'backgroundifyied window is in the list of windows');
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -172,16 +172,18 @@ pref("geo.enabled", true);
 // see https://bugzilla.mozilla.org/show_bug.cgi?id=481566#c9
 pref("content.sink.enable_perf_mode",  2); // 0 - switch, 1 - interactive, 2 - perf
 pref("content.sink.pending_event_mode", 0);
 pref("content.sink.perf_deflect_count", 1000000);
 pref("content.sink.perf_parse_time", 50000000);
 
 // Maximum scripts runtime before showing an alert
 pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
+// Disable the watchdog thread for B2G. See bug 870043 comment 31.
+pref("dom.use_watchdog", false);
 
 // plugins
 pref("plugin.disable", true);
 pref("dom.ipc.plugins.enabled", true);
 
 // product URLs
 // The breakpad report server to link to in about:crashes
 pref("breakpad.reportURL", "https://crash-stats.mozilla.com/report/index/");
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -551,17 +551,18 @@ var shell = {
   openAppForSystemMessage: function shell_openAppForSystemMessage(msg) {
     let origin = Services.io.newURI(msg.manifest, null, null).prePath;
     this.sendChromeEvent({
       type: 'open-app',
       url: msg.uri,
       manifestURL: msg.manifest,
       isActivity: (msg.type == 'activity'),
       target: msg.target,
-      expectingSystemMessage: true
+      expectingSystemMessage: true,
+      extra: msg.extra
     });
   },
 
   receiveMessage: function shell_receiveMessage(message) {
     var activities = { 'content-handler': { name: 'view', response: null },
                        'dial-handler':    { name: 'dial', response: null },
                        'mail-handler':    { name: 'new',  response: null },
                        'sms-handler':     { name: 'new',  response: null },
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "755ad2936b1b80db70ecb45cf6798e9ab91a11dd", 
+    "revision": "72f1b7c657c65389c843dcd032e5bb787f4afafc", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -466,16 +466,20 @@ pref("general.warnOnAboutConfig",       
 pref("dom.disable_window_open_feature.location",  true);
 // prevent JS from setting status messages
 pref("dom.disable_window_status_change",          true);
 // allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize",            false);
 // prevent JS from monkeying with window focus, etc
 pref("dom.disable_window_flip",                   true);
 
+// Disable touch events on Desktop Firefox by default until they are properly
+// supported (bug 736048)
+pref("dom.w3c_touch_events.enabled",        0);
+
 // popups.policy 1=allow,2=reject
 pref("privacy.popups.policy",               1);
 pref("privacy.popups.usecustom",            true);
 pref("privacy.popups.showBrowserMessage",   true);
 
 pref("privacy.item.cookies",                false);
 
 pref("privacy.clearOnShutdown.history",     true);
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -289,31 +289,59 @@ function ensureSnippetsMapThen(aCallback
 }
 
 function onSearchSubmit(aEvent)
 {
   let searchTerms = document.getElementById("searchText").value;
   let searchURL = document.documentElement.getAttribute("searchEngineURL");
 
   if (searchURL && searchTerms.length > 0) {
-    const SEARCH_TOKENS = {
-      "_searchTerms_": encodeURIComponent(searchTerms)
-    }
-    for (let key in SEARCH_TOKENS) {
-      searchURL = searchURL.replace(key, SEARCH_TOKENS[key]);
-    }
-
     // Send an event that a search was performed. This was originally
     // added so Firefox Health Report could record that a search from
     // about:home had occurred.
     let engineName = document.documentElement.getAttribute("searchEngineName");
     let event = new CustomEvent("AboutHomeSearchEvent", {detail: engineName});
     document.dispatchEvent(event);
 
-    window.location.href = searchURL;
+    const SEARCH_TOKEN = "_searchTerms_";
+    let searchPostData = document.documentElement.getAttribute("searchEnginePostData");
+    if (searchPostData) {
+      // Check if a post form already exists. If so, remove it.
+      const POST_FORM_NAME = "searchFormPost";
+      let form = document.forms[POST_FORM_NAME];
+      if (form) {
+        form.parentNode.removeChild(form);
+      }
+
+      // Create a new post form.
+      form = document.body.appendChild(document.createElement("form"));
+      form.setAttribute("name", POST_FORM_NAME);
+      // Set the URL to submit the form to.
+      form.setAttribute("action", searchURL.replace(SEARCH_TOKEN, searchTerms));
+      form.setAttribute("method", "post");
+
+      // Create new <input type=hidden> elements for search param.
+      searchPostData = searchPostData.split("&");
+      for (let postVar of searchPostData) {
+        let [name, value] = postVar.split("=");
+        if (value == SEARCH_TOKEN) {
+          value = searchTerms;
+        }
+        let input = document.createElement("input");
+        input.setAttribute("type", "hidden");
+        input.setAttribute("name", name);
+        input.setAttribute("value", value);
+        form.appendChild(input);
+      }
+      // Submit the form.
+      form.submit();
+   } else {
+      searchURL = searchURL.replace(SEARCH_TOKEN, encodeURIComponent(searchTerms));
+      window.location.href = searchURL;
+    }
   }
 
   aEvent.preventDefault();
 }
 
 
 function setupSearchEngine()
 {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1035,17 +1035,16 @@ var gBrowserInit = {
     OfflineApps.init();
     IndexedDBPromptHelper.init();
     gFormSubmitObserver.init();
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
     PanelUI.init();
-    SocialUI.init();
     LightweightThemeListener.init();
     WebrtcIndicator.init();
 
     // Ensure login manager is up and running.
     Services.logins;
 
     if (mustLoadSidebar) {
       let sidebar = document.getElementById("sidebar");
@@ -1091,22 +1090,17 @@ var gBrowserInit = {
     if (!gMultiProcessBrowser) {
       let NP = {};
       Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
       NP.trackBrowserWindow(window);
     }
 
     // initialize the session-restore service (in case it's not already running)
     let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
-    ss.init(window);
-
-    // Enable the Restore Last Session command if needed
-    if (ss.canRestoreLastSession &&
-        !PrivateBrowsingUtils.isWindowPrivate(window))
-      goSetCommandEnabled("Browser:RestoreLastSession", true);
+    let ssPromise = ss.init(window);
 
     PlacesToolbarHelper.init();
 
     ctrlTab.readPref();
     gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
@@ -1159,17 +1153,16 @@ var gBrowserInit = {
     gSyncUI.init();
 #endif
 
 #ifdef MOZ_DATA_REPORTING
     gDataNotificationInfoBar.init();
 #endif
 
     gBrowserThumbnails.init();
-    TabView.init();
 
     setUrlAndSearchBarWidthForConditionalForwardButton();
     window.addEventListener("resize", function resizeHandler(event) {
       if (event.target == window)
         setUrlAndSearchBarWidthForConditionalForwardButton();
     });
 
     // Enable developer toolbar?
@@ -1251,18 +1244,29 @@ var gBrowserInit = {
 #ifdef MOZ_METRO
     gMetroPrefs.prefDomain.forEach(function(prefName) {
       gMetroPrefs.pushDesktopControlledPrefToMetro(prefName);
       Services.prefs.addObserver(prefName, gMetroPrefs, false);
     }, this);
 #endif
 #endif
 
+    ssPromise.then(() =>{
+      // Enable the Restore Last Session command if needed
+      if (ss.canRestoreLastSession &&
+          !PrivateBrowsingUtils.isWindowPrivate(window))
+        goSetCommandEnabled("Browser:RestoreLastSession", true);
+
+      TabView.init();
+      SocialUI.init();
+
+      setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
+    });
+
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
-    setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
   onUnload: function() {
     // In certain scenarios it's possible for unload to be fired before onload,
     // (e.g. if the window is being closed after browser.js loads but before the
     // load completes). In that case, there's nothing to do here.
     if (!this._loadHandled)
@@ -2273,16 +2277,19 @@ function BrowserOnAboutPageLoad(doc) {
       let currentVersion = Services.prefs.getIntPref("browser.rights.version");
       Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
     }
     docElt.setAttribute("snippetsVersion", AboutHomeUtils.snippetsVersion);
 
     let updateSearchEngine = function() {
       let engine = AboutHomeUtils.defaultSearchEngine;
       docElt.setAttribute("searchEngineName", engine.name);
+      docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
+      // Again, keep the searchEngineURL as the last attribute, because the
+      // mutation observer in aboutHome.js is counting on that.
       docElt.setAttribute("searchEngineURL", engine.searchURL);
     };
     updateSearchEngine();
 
     // Listen for the event that's triggered when the user changes search engine.
     // At this point we simply reload about:home to reflect the change.
     Services.obs.addObserver(updateSearchEngine, "browser-search-engine-modified", false);
 
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -237,28 +237,82 @@ let gTests = [
     }
     // Do a sanity check that all attributes are correctly set to begin with
     checkSearchUI(currEngine);
 
     let deferred = Promise.defer();
     promiseBrowserAttributes(gBrowser.selectedTab).then(function() {
       // Test if the update propagated
       checkSearchUI(unusedEngines[0]);
+      searchbar.currentEngine = currEngine;
       deferred.resolve();
     });
 
-    // The following cleanup function will set currentEngine back to the previous engine
+    // The following cleanup function will set currentEngine back to the previous
+    // engine if we fail to do so above.
     registerCleanupFunction(function() {
       searchbar.currentEngine = currEngine;
     });
     // Set the current search engine to an unused one
     searchbar.currentEngine = unusedEngines[0];
     searchbar.select();
     return deferred.promise;
   }
+},
+
+{
+  desc: "Check POST search engine support",
+  setup: function() {},
+  run: function()
+  {
+    let deferred = Promise.defer();
+    let currEngine = Services.search.defaultEngine;
+    let searchObserver = function search_observer(aSubject, aTopic, aData) {
+      let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
+      info("Observer: " + aData + " for " + engine.name);
+
+      if (aData != "engine-added")
+        return;
+
+      if (engine.name != "POST Search")
+        return;
+
+      Services.search.defaultEngine = engine;
+
+      registerCleanupFunction(function() {
+        Services.search.removeEngine(engine);
+        Services.search.defaultEngine = currEngine;
+      });
+
+
+      // Ready to execute the tests!
+      let needle = "Search for something awesome.";
+      let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
+      let searchText = document.getElementById("searchText");
+
+      waitForLoad(function() {
+        let loadedText = gBrowser.contentDocument.body.textContent;
+        ok(loadedText, "search page loaded");
+        is(loadedText, "searchterms=" + escape(needle.replace(/\s/g, "+")),
+           "Search text should arrive correctly");
+        deferred.resolve();
+      });
+
+      searchText.value = needle;
+      searchText.focus();
+      EventUtils.synthesizeKey("VK_RETURN", {});
+    };
+    Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false);
+    registerCleanupFunction(function () {
+      Services.obs.removeObserver(searchObserver, "browser-search-engine-modified");
+    });
+    Services.search.addEngine("http://test:80/browser/browser/base/content/test/POSTSearchEngine.xml",
+                              Ci.nsISearchEngine.DATA_XML, null, false);
+    return deferred.promise;
+  }
 }
 
 ];
 
 function test()
 {
   waitForExplicitFinish();
   requestLongerTimeout(2);
@@ -437,8 +491,20 @@ function getNumberOfSearchesByDate(aEngi
 
     if (day.has(field)) {
       return day.get(field) || 0;
     }
   }
 
   return 0; // No records found.
 }
+
+function waitForLoad(cb) {
+  let browser = gBrowser.selectedBrowser;
+  browser.addEventListener("load", function listener() {
+    if (browser.currentURI.spec == "about:blank")
+      return;
+    info("Page loaded: " + browser.currentURI.spec);
+    browser.removeEventListener("load", listener, true);
+
+    cb();
+  }, true);
+}
--- a/browser/base/content/test/newtab/browser_newtab_bug735987.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug735987.js
@@ -3,24 +3,30 @@
 
 function runTests() {
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7,8");
 
-  yield simulateDrop(1);
+  yield simulateExternalDrop(1);
   checkGrid("0,99p,1,2,3,4,5,6,7");
 
   yield blockCell(1);
   checkGrid("0,1,2,3,4,5,6,7,8");
 
-  yield simulateDrop(1);
+  yield simulateExternalDrop(1);
   checkGrid("0,99p,1,2,3,4,5,6,7");
 
+  // Simulate a restart and force the next about:newtab
+  // instance to read its data from the storage again.
   NewTabUtils.blockedLinks.resetCache();
+
+  // Update all open pages, e.g. preloaded ones.
+  NewTabUtils.allPages.update();
+
   yield addNewTabPageTab();
   checkGrid("0,99p,1,2,3,4,5,6,7");
 
   yield blockCell(1);
   checkGrid("0,1,2,3,4,5,6,7,8");
 }
--- a/browser/base/content/test/newtab/browser_newtab_drag_drop.js
+++ b/browser/base/content/test/newtab/browser_newtab_drag_drop.js
@@ -12,17 +12,17 @@ function runTests() {
 
   // test a simple drag-and-drop scenario
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7,8");
 
-  yield simulateDrop(1, 0);
+  yield simulateDrop(0, 1);
   checkGrid("1,0p,2,3,4,5,6,7,8");
 
   // drag a cell to its current cell and make sure it's not pinned afterwards
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7,8");
@@ -32,86 +32,86 @@ function runTests() {
 
   // ensure that pinned pages aren't moved if that's not necessary
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks(",1,2");
 
   yield addNewTabPageTab();
   checkGrid("0,1p,2p,3,4,5,6,7,8");
 
-  yield simulateDrop(3, 0);
+  yield simulateDrop(0, 3);
   checkGrid("3,1p,2p,0p,4,5,6,7,8");
 
   // pinned sites should always be moved around as blocks. if a pinned site is
   // moved around, neighboring pinned are affected as well
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("0,1");
 
   yield addNewTabPageTab();
   checkGrid("0p,1p,2,3,4,5,6,7,8");
 
-  yield simulateDrop(0, 2);
+  yield simulateDrop(2, 0);
   checkGrid("2p,0p,1p,3,4,5,6,7,8");
 
   // pinned sites should not be pushed out of the grid (unless there are only
   // pinned ones left on the grid)
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks(",,,,,,,7,8");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7p,8p");
 
-  yield simulateDrop(8, 2);
+  yield simulateDrop(2, 8);
   checkGrid("0,1,3,4,5,6,7p,8p,2p");
 
   // make sure that pinned sites are re-positioned correctly
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("0,1,2,,,5");
 
   yield addNewTabPageTab();
   checkGrid("0p,1p,2p,3,4,5p,6,7,8");
 
-  yield simulateDrop(4, 0);
+  yield simulateDrop(0, 4);
   checkGrid("3,1p,2p,4,0p,5p,6,7,8");
 
   // drag a new site onto the very first cell
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks(",,,,,,,7,8");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7p,8p");
 
-  yield simulateDrop(0);
+  yield simulateExternalDrop(0);
   checkGrid("99p,0,1,2,3,4,5,7p,8p");
 
   // drag a new site onto the grid and make sure that pinned cells don't get
   // pushed out
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks(",,,,,,,7,8");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7p,8p");
 
-  yield simulateDrop(7);
+  yield simulateExternalDrop(7);
   checkGrid("0,1,2,3,4,5,7p,99p,8p");
 
   // drag a new site beneath a pinned cell and make sure the pinned cell is
   // not moved
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks(",,,,,,,,8");
 
   yield addNewTabPageTab();
   checkGrid("0,1,2,3,4,5,6,7,8p");
 
-  yield simulateDrop(7);
+  yield simulateExternalDrop(7);
   checkGrid("0,1,2,3,4,5,6,99p,8p");
 
   // drag a new site onto a block of pinned sites and make sure they're shifted
   // around accordingly
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("0,1,2,,,,,,");
 
   yield addNewTabPageTab();
   checkGrid("0p,1p,2p");
 
-  yield simulateDrop(1);
+  yield simulateExternalDrop(1);
   checkGrid("0p,99p,1p,2p,3,4,5,6,7");
 }
--- a/browser/base/content/test/newtab/browser_newtab_tabsync.js
+++ b/browser/base/content/test/newtab/browser_newtab_tabsync.js
@@ -38,22 +38,22 @@ function runTests() {
   // remove a cell
   yield blockCell(1);
   checkGrid("0,2,3,4,5,6,7,8,9");
   checkGrid("0,2,3,4,5,6,7,8,9", oldSites);
   ok(resetButton.hasAttribute("modified"), "page is modified");
   ok(oldResetButton.hasAttribute("modified"), "page is modified");
 
   // insert a new cell by dragging
-  yield simulateDrop(1);
+  yield simulateExternalDrop(1);
   checkGrid("0,99p,2,3,4,5,6,7,8");
   checkGrid("0,99p,2,3,4,5,6,7,8", oldSites);
 
   // drag a cell around
-  yield simulateDrop(1, 2);
+  yield simulateDrop(2, 1);
   checkGrid("0,2p,99p,3,4,5,6,7,8");
   checkGrid("0,2p,99p,3,4,5,6,7,8", oldSites);
 
   // reset the new tab page
   yield getContentWindow().gToolbar.reset(TestRunner.next);
   checkGrid("0,1,2,3,4,5,6,7,8");
   checkGrid("0,1,2,3,4,5,6,7,8", oldSites);
   ok(!resetButton.hasAttribute("modified"), "page is not modified");
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -1,26 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
 
 Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
 
 let tmp = {};
+Cu.import("resource://gre/modules/Promise.jsm", tmp);
 Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("chrome://browser/content/sanitize.js", tmp);
-
-let {NewTabUtils, Sanitizer} = tmp;
+let {Promise, NewTabUtils, Sanitizer} = tmp;
 
 let uri = Services.io.newURI("about:newtab", null, null);
 let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
 
+let isMac = ("nsILocalFileMac" in Ci);
+let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
+let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 let gWindow = window;
 
 registerCleanupFunction(function () {
   while (gWindow.gBrowser.tabs.length > 1)
     gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]);
 
   Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
 });
@@ -300,36 +303,176 @@ function pinCell(aIndex, aPinIndex) {
  * @param aIndex The cell index.
  */
 function unpinCell(aIndex) {
   whenPagesUpdated();
   getCell(aIndex).site.unpin();
 }
 
 /**
- * Simulates a drop and drop operation.
- * @param aDropIndex The cell index of the drop target.
- * @param aDragIndex The cell index containing the dragged site (optional).
+ * Simulates a drag and drop operation.
+ * @param aSourceIndex The cell index containing the dragged site.
+ * @param aDestIndex The cell index of the drop target.
+ */
+function simulateDrop(aSourceIndex, aDestIndex) {
+  let src = getCell(aSourceIndex).site.node;
+  let dest = getCell(aDestIndex).node;
+
+  // Drop 'src' onto 'dest' and continue testing when all newtab
+  // pages have been updated (i.e. the drop operation is completed).
+  startAndCompleteDragOperation(src, dest, whenPagesUpdated);
+}
+
+/**
+ * Simulates a drag and drop operation. Instead of rearranging a site that is
+ * is already contained in the newtab grid, this is used to simulate dragging
+ * an external link onto the grid e.g. the text from the URL bar.
+ * @param aDestIndex The cell index of the drop target.
  */
-function simulateDrop(aDropIndex, aDragIndex) {
-  let draggedSite;
-  let {gDrag: drag, gDrop: drop} = getContentWindow();
-  let event = createDragEvent("drop", "http://example.com/#99\nblank");
+function simulateExternalDrop(aDestIndex) {
+  let dest = getCell(aDestIndex).node;
+
+  // Create an iframe that contains the external link we'll drag.
+  createExternalDropIframe().then(iframe => {
+    let link = iframe.contentDocument.getElementById("link");
+
+    // Drop 'link' onto 'dest'.
+    startAndCompleteDragOperation(link, dest, () => {
+      // Wait until the drop operation is complete
+      // and all newtab pages have been updated.
+      whenPagesUpdated(() => {
+        // Clean up and remove the iframe.
+        iframe.remove();
+        // Continue testing.
+        TestRunner.next();
+      });
+    });
+  });
+}
+
+/**
+ * Starts and complete a drag-and-drop operation.
+ * @param aSource The node that is being dragged.
+ * @param aDest The node we're dragging aSource onto.
+ * @param aCallback The function that is called when we're done.
+ */
+function startAndCompleteDragOperation(aSource, aDest, aCallback) {
+  // Start by pressing the left mouse button.
+  synthesizeNativeMouseLDown(aSource);
+
+  // Move the mouse in 5px steps until the drag operation starts.
+  let offset = 0;
+  let interval = setInterval(() => {
+    synthesizeNativeMouseDrag(aSource, offset += 5);
+  }, 10);
+
+  // When the drag operation has started we'll move
+  // the dragged element to its target position.
+  aSource.addEventListener("dragstart", function onDragStart() {
+    aSource.removeEventListener("dragstart", onDragStart);
+    clearInterval(interval);
+
+    // Place the cursor above the drag target.
+    synthesizeNativeMouseMove(aDest);
+  });
+
+  // As soon as the dragged element hovers the target, we'll drop it.
+  aDest.addEventListener("dragenter", function onDragEnter() {
+    aDest.removeEventListener("dragenter", onDragEnter);
+
+    // Finish the drop operation.
+    synthesizeNativeMouseLUp(aDest);
+    aCallback();
+  });
+}
 
-  if (typeof aDragIndex != "undefined")
-    draggedSite = getCell(aDragIndex).site;
+/**
+ * Helper function that creates a temporary iframe in the about:newtab
+ * document. This will contain a link we can drag to the test the dropping
+ * of links from external documents.
+ */
+function createExternalDropIframe() {
+  const url = "data:text/html;charset=utf-8," +
+              "<a id='link' href='http://example.com/%2399'>link</a>";
+
+  let deferred = Promise.defer();
+  let doc = getContentDocument();
+  let iframe = doc.createElement("iframe");
+  iframe.setAttribute("src", url);
+  iframe.style.width = "50px";
+  iframe.style.height = "50px";
+
+  let margin = doc.getElementById("newtab-margin-top");
+  margin.appendChild(iframe);
 
-  if (draggedSite)
-    drag.start(draggedSite, event);
+  iframe.addEventListener("load", function onLoad() {
+    iframe.removeEventListener("load", onLoad);
+    executeSoon(() => deferred.resolve(iframe));
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Fires a synthetic 'mousedown' event on the current about:newtab page.
+ * @param aElement The element used to determine the cursor position.
+ */
+function synthesizeNativeMouseLDown(aElement) {
+  if (isLinux) {
+    let win = aElement.ownerDocument.defaultView;
+    EventUtils.synthesizeMouseAtCenter(aElement, {type: "mousedown"}, win);
+  } else {
+    let msg = isWindows ? 2 : 1;
+    synthesizeNativeMouseEvent(aElement, msg);
+  }
+}
 
-  whenPagesUpdated();
-  drop.drop(getCell(aDropIndex), event);
+/**
+ * Fires a synthetic 'mouseup' event on the current about:newtab page.
+ * @param aElement The element used to determine the cursor position.
+ */
+function synthesizeNativeMouseLUp(aElement) {
+  let msg = isWindows ? 4 : (isMac ? 2 : 7);
+  synthesizeNativeMouseEvent(aElement, msg);
+}
+
+/**
+ * Fires a synthetic mouse drag event on the current about:newtab page.
+ * @param aElement The element used to determine the cursor position.
+ * @param aOffsetX The left offset that is added to the position.
+ */
+function synthesizeNativeMouseDrag(aElement, aOffsetX) {
+  let msg = isMac ? 6 : 1;
+  synthesizeNativeMouseEvent(aElement, msg, aOffsetX);
+}
 
-  if (draggedSite)
-    drag.end(draggedSite);
+/**
+ * Fires a synthetic 'mousemove' event on the current about:newtab page.
+ * @param aElement The element used to determine the cursor position.
+ */
+function synthesizeNativeMouseMove(aElement) {
+  let msg = isMac ? 5 : 1;
+  synthesizeNativeMouseEvent(aElement, msg);
+}
+
+/**
+ * Fires a synthetic mouse event on the current about:newtab page.
+ * @param aElement The element used to determine the cursor position.
+ * @param aOffsetX The left offset that is added to the position (optional).
+ * @param aOffsetY The top offset that is added to the position (optional).
+ */
+function synthesizeNativeMouseEvent(aElement, aMsg, aOffsetX = 0, aOffsetY = 0) {
+  let rect = aElement.getBoundingClientRect();
+  let win = aElement.ownerDocument.defaultView;
+  let x = aOffsetX + win.mozInnerScreenX + rect.left + rect.width / 2;
+  let y = aOffsetY + win.mozInnerScreenY + rect.top + rect.height / 2;
+
+  win.QueryInterface(Ci.nsIInterfaceRequestor)
+     .getInterface(Ci.nsIDOMWindowUtils)
+     .sendNativeMouseEvent(x, y, aMsg, 0, null);
 }
 
 /**
  * Sends a custom drag event to a given DOM element.
  * @param aEventType The drag event's type.
  * @param aTarget The DOM element that the event is dispatched to.
  * @param aData The event's drag data (optional).
  */
--- a/browser/base/content/test/social/browser_social_window.js
+++ b/browser/base/content/test/social/browser_social_window.js
@@ -12,27 +12,32 @@ function resetSocial() {
   Social._provider = null;
   Social.providers = [];
   // *sob* - listeners keep getting added...
   let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
   SocialService._providerListeners.clear();
 }
 
 let createdWindows = [];
+let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 function openWindowAndWaitForInit(callback) {
   // this notification tells us SocialUI.init() has been run...
   let topic = "browser-delayed-startup-finished";
   let w = OpenBrowserWindow();
   createdWindows.push(w);
   Services.obs.addObserver(function providerSet(subject, topic, data) {
     Services.obs.removeObserver(providerSet, topic);
     info(topic + " observer was notified - continuing test");
-    // executeSoon to let the browser UI observers run first
-    executeSoon(function() {callback(w)});
+    // We need to wait for the SessionStore as well, since
+    // SocialUI.init() is also waiting on it.
+    ss.init(w).then(function () {
+      executeSoon(function() {callback(w);});
+    });
+
   }, topic, false);
 }
 
 function postTestCleanup(cb) {
   for (let w of createdWindows)
     w.close();
   createdWindows = [];
   Services.prefs.clearUserPref("social.enabled");
--- a/browser/components/sessionstore/nsISessionStore.idl
+++ b/browser/components/sessionstore/nsISessionStore.idl
@@ -20,23 +20,23 @@ interface nsIDOMNode;
  * global |window| object to the API, though (or |top| from a sidebar).
  * From elsewhere you can get browser windows through the nsIWindowMediator
  * by looking for "navigator:browser" windows.
  *
  * * "Tabbrowser tabs" are all the child nodes of a browser window's
  * |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
  */
 
-[scriptable, uuid(0aa5492c-15ad-4376-8eac-28895796826e)]
+[scriptable, uuid(092fa0cc-e99b-11e2-a2a3-a25b4f45d8e2)]
 interface nsISessionStore : nsISupports
 {
   /**
    * Initialize the service
    */
-  void init(in nsIDOMWindow aWindow);
+  jsval init(in nsIDOMWindow aWindow);
 
   /**
    * Is it possible to restore the previous session. Will always be false when
    * in Private Browsing mode.
    */
   attribute boolean canRestoreLastSession;
 
   /**
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -74,17 +74,17 @@ const TAB_EVENTS = [
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 // debug.js adds NS_ASSERT. cf. bug 669196
 Cu.import("resource://gre/modules/debug.js", this);
 Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm", this);
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", this);
+Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
   "@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
 XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
   "@mozilla.org/gfx/screenmanager;1", "nsIScreenManager");
 
 // List of docShell capabilities to (re)store. These are automatically
@@ -134,17 +134,17 @@ this.SessionStore = {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
   init: function ss_init(aWindow) {
-    SessionStoreInternal.init(aWindow);
+    return SessionStoreInternal.init(aWindow);
   },
 
   getBrowserState: function ss_getBrowserState() {
     return SessionStoreInternal.getBrowserState();
   },
 
   setBrowserState: function ss_setBrowserState(aState) {
     SessionStoreInternal.setBrowserState(aState);
@@ -574,19 +574,21 @@ let SessionStoreInternal = {
    */
   init: function ssi_init(aWindow) {
     if (!aWindow) {
       throw new Error("init() must be called with a valid window.");
     }
 
     let self = this;
     this.initService();
-    this._promiseInitialization.promise.then(
+    return this._promiseInitialization.promise.then(
       function onSuccess() {
-        self.onLoad(aWindow);
+        if (!aWindow.closed) {
+          self.onLoad(aWindow);
+        }
       }
     );
   },
 
   /**
    * Called on application shutdown, after notifications:
    * quit-application-granted, quit-application
    */
--- a/browser/components/sessionstore/src/_SessionFile.jsm
+++ b/browser/components/sessionstore/src/_SessionFile.jsm
@@ -28,17 +28,17 @@ this.EXPORTED_SYMBOLS = ["_SessionFile"]
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -34,17 +34,17 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
   "resource:///modules/sessionstore/_SessionFile.jsm");
 
 const STATE_RUNNING_STR = "running";
 
 function debug(aMsg) {
   aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -31,17 +31,18 @@ function SourcesView() {
 SourcesView.prototype = Heritage.extend(WidgetMethods, {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function() {
     dumpn("Initializing the SourcesView");
 
     this.widget = new SideMenuWidget(document.getElementById("sources"), {
-      showCheckboxes: true
+      showCheckboxes: true,
+      showArrows: true
     });
     this.emptyText = L10N.getStr("noSourcesText");
     this.unavailableText = L10N.getStr("noMatchingSourcesText");
     this._blackBoxCheckboxTooltip = L10N.getStr("blackBoxCheckboxTooltip");
 
     this._commandset = document.getElementById("debuggerCommands");
     this._popupset = document.getElementById("debuggerPopupset");
     this._cmPopup = document.getElementById("sourceEditorContextMenu");
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -104,16 +104,17 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_pause-exceptions-reload.js \
 	browser_dbg_multiple-windows.js \
 	browser_dbg_iframes.js \
 	browser_dbg_bfcache.js \
 	browser_dbg_progress-listener-bug.js \
 	browser_dbg_chrome-debugging.js \
 	browser_dbg_source_maps-01.js \
 	browser_dbg_source_maps-02.js \
+	browser_dbg_source_maps-03.js \
 	browser_dbg_step-out.js \
 	browser_dbg_event-listeners.js \
 	head.js \
 	$(NULL)
 
 MOCHITEST_BROWSER_PAGES = \
 	browser_dbg_blackboxing.html \
 	blackboxing_blackboxme.js \
@@ -146,16 +147,20 @@ MOCHITEST_BROWSER_PAGES = \
 	browser_dbg_function-search-02.html \
 	test-function-search-01.js \
 	test-function-search-02.js \
 	test-function-search-03.js \
 	binary_search.html \
 	binary_search.coffee \
 	binary_search.js \
 	binary_search.map \
+	math.js \
+	math.min.js \
+	math.map \
+	minified.html \
 	test-location-changes-bp.js \
 	test-location-changes-bp.html \
 	test-step-out.html \
 	test-pause-exceptions-reload.html \
 	test-event-listeners.html \
 	$(NULL)
 
 # Bug 888811 & bug 891176:
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-bp.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-bp.js
@@ -108,19 +108,19 @@ function testReloadPage()
 
 function clickAgain()
 {
   if (!sourcesShown || !tabNavigated) {
     return;
   }
 
   let controller = gDebugger.DebuggerController;
-  controller.activeThread.addOneTimeListener("framesadded", function() {
-    is(gDebugger.DebuggerController.activeThread.state, "paused",
-      "The breakpoint was hit.");
+  controller.activeThread.addOneTimeListener("paused", function(aEvent, aPacket) {
+    is(aPacket.why.type, "breakpoint",
+       "The breakpoint was hit.");
 
     let thread = gDebugger.DebuggerController.activeThread;
     thread.addOneTimeListener("paused", function test(aEvent, aPacket) {
       thread.addOneTimeListener("resumed", function() {
         executeSoon(closeDebuggerAndFinish);
       });
 
       is(aPacket.why.type, "debuggerStatement", "Execution has advanced to the next line.");
--- a/browser/devtools/debugger/test/browser_dbg_source_maps-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_source_maps-01.js
@@ -15,46 +15,48 @@ var gDebugger = null;
 
 function test()
 {
   let scriptShown = false;
   let framesAdded = false;
   let resumed = false;
   let testStarted = false;
 
-  Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", true);
-
-  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
-    resumed = true;
-    gTab = aTab;
-    gDebuggee = aDebuggee;
-    gPane = aPane;
-    gDebugger = gPane.panelWin;
+  SpecialPowers.pushPrefEnv({"set": [["devtools.debugger.source-maps-enabled", true]]}, () => {
+    debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+      resumed = true;
+      gTab = aTab;
+      gDebuggee = aDebuggee;
+      gPane = aPane;
+      gDebugger = gPane.panelWin;
 
-    gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) {
-      gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown);
-      ok(aEvent.detail.url.indexOf(".coffee") != -1,
-         "The debugger should show the source mapped coffee script file.");
-      ok(gDebugger.editor.getText().search(/isnt/) != -1,
-         "The debugger's editor should have the coffee script source displayed.");
+      gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) {
+        gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown);
+        ok(aEvent.detail.url.indexOf(".coffee") != -1,
+           "The debugger should show the source mapped coffee script file.");
+        ok(gDebugger.editor.getText().search(/isnt/) != -1,
+           "The debugger's editor should have the coffee script source displayed.");
 
-      testSetBreakpoint();
+        testSetBreakpoint();
+      });
     });
   });
 }
 
 function testSetBreakpoint() {
   let { activeThread } = gDebugger.DebuggerController;
   activeThread.interrupt(function (aResponse) {
     activeThread.setBreakpoint({
       url: EXAMPLE_URL + "binary_search.coffee",
       line: 5
     }, function (aResponse, bpClient) {
       ok(!aResponse.error,
          "Should be able to set a breakpoint in a coffee script file.");
+      ok(!aResponse.actualLocation,
+         "Should be able to set a breakpoint on line 5.");
       testSetBreakpointBlankLine();
     });
   });
 }
 
 function testSetBreakpointBlankLine() {
   let { activeThread } = gDebugger.DebuggerController;
   activeThread.setBreakpoint({
@@ -142,15 +144,14 @@ function waitForCaretPos(number, callbac
     }
     // We got the source editor at the expected line, it's safe to callback.
     window.clearInterval(intervalID);
     callback();
   }, 100);
 }
 
 registerCleanupFunction(function() {
-  Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
   removeTab(gTab);
   gPane = null;
   gTab = null;
   gDebuggee = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_source_maps-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_source_maps-02.js
@@ -17,45 +17,45 @@ function test()
 {
   let scriptShown = false;
   let framesAdded = false;
   let resumed = false;
   let testStarted = false;
 
   gPrevPref = Services.prefs.getBoolPref(
     "devtools.debugger.source-maps-enabled");
-  Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", true);
-
-  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
-    resumed = true;
-    gTab = aTab;
-    gDebuggee = aDebuggee;
-    gPane = aPane;
-    gDebugger = gPane.panelWin;
+  SpecialPowers.pushPrefEnv({"set": [["devtools.debugger.source-maps-enabled", true]]}, () => {
+    debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+      resumed = true;
+      gTab = aTab;
+      gDebuggee = aDebuggee;
+      gPane = aPane;
+      gDebugger = gPane.panelWin;
 
-    gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) {
-      gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown);
-      // Show original sources should be already enabled.
-      is(gPrevPref, false,
-        "The source maps functionality should be disabled by default.");
-      is(gDebugger.Prefs.sourceMapsEnabled, true,
-        "The source maps pref should be true from startup.");
-      is(gDebugger.DebuggerView.Options._showOriginalSourceItem.getAttribute("checked"),
-         "true", "Source maps should be enabled from startup. ")
+      gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) {
+        gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown);
+        // Show original sources should be already enabled.
+        is(gPrevPref, true,
+          "The source maps functionality should be enabled by default.");
+        is(gDebugger.Prefs.sourceMapsEnabled, true,
+          "The source maps pref should be true from startup.");
+        is(gDebugger.DebuggerView.Options._showOriginalSourceItem.getAttribute("checked"),
+           "true", "Source maps should be enabled from startup. ")
 
-      ok(aEvent.detail.url.indexOf(".coffee") != -1,
-         "The debugger should show the source mapped coffee script file.");
-      ok(aEvent.detail.url.indexOf(".js") == -1,
-         "The debugger should not show the generated js script file.");
-      ok(gDebugger.editor.getText().search(/isnt/) != -1,
-         "The debugger's editor should have the coffee script source displayed.");
-      ok(gDebugger.editor.getText().search(/function/) == -1,
-         "The debugger's editor should not have the JS source displayed.");
+        ok(aEvent.detail.url.indexOf(".coffee") != -1,
+           "The debugger should show the source mapped coffee script file.");
+        ok(aEvent.detail.url.indexOf(".js") == -1,
+           "The debugger should not show the generated js script file.");
+        ok(gDebugger.editor.getText().search(/isnt/) != -1,
+           "The debugger's editor should have the coffee script source displayed.");
+        ok(gDebugger.editor.getText().search(/function/) == -1,
+           "The debugger's editor should not have the JS source displayed.");
 
-      testToggleGeneratedSource();
+        testToggleGeneratedSource();
+      });
     });
   });
 }
 
 function testToggleGeneratedSource() {
   gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) {
     gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown);
 
@@ -190,16 +190,15 @@ function waitForCaretPos(number, callbac
        "The right line is focused.")
     // We got the source editor at the expected line, it's safe to callback.
     window.clearInterval(intervalID);
     callback();
   }, 100);
 }
 
 registerCleanupFunction(function() {
-  Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
   removeTab(gTab);
   gPane = null;
   gTab = null;
   gDebuggee = null;
   gDebugger = null;
   gPrevPref = null;
 });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_source_maps-03.js
@@ -0,0 +1,117 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that we can debug minified javascript with source maps.
+ */
+
+const TAB_URL = EXAMPLE_URL + "minified.html";
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+var gClient = null;
+
+let sourceShown = false;
+let hitDebuggerStatement = false;
+
+function test()
+{
+  SpecialPowers.pushPrefEnv({"set": [["devtools.debugger.source-maps-enabled", true]]}, () => {
+    debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+      gTab = aTab;
+      gDebuggee = aDebuggee;
+      gPane = aPane;
+      gDebugger = gPane.panelWin;
+      gClient = gDebugger.DebuggerController.client;
+
+      testMinJSAndSourceMaps();
+    });
+  });
+}
+
+function testMinJSAndSourceMaps() {
+  gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) {
+    gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown);
+    sourceShown = true;
+
+    ok(aEvent.detail.url.indexOf("math.min.js") == -1,
+       "The debugger should not show the minified js");
+    ok(aEvent.detail.url.indexOf("math.js") != -1,
+       "The debugger should show the original js");
+    ok(gDebugger.editor.getText().split("\n").length > 10,
+       "The debugger's editor should have the original source displayed, " +
+       "not the whitespace stripped minified version");
+
+    startTest();
+  });
+
+  gClient.addListener("paused", function _onPaused(aEvent, aPacket) {
+    if (aPacket.type === "paused" && aPacket.why.type === "breakpoint") {
+      gClient.removeListener("paused", _onPaused);
+      hitDebuggerStatement = true;
+
+      startTest();
+    }
+  });
+
+  gClient.activeThread.interrupt(function (aResponse) {
+    ok(!aResponse.error, "Shouldn't be an error interrupting.");
+
+    gClient.activeThread.setBreakpoint({
+      url: EXAMPLE_URL + "math.js",
+      line: 30,
+      column: 10
+    }, function (aResponse) {
+      ok(!aResponse.error, "Shouldn't be an error setting a breakpoint.");
+      ok(!aResponse.actualLocation, "Shouldn't be an actualLocation.");
+
+      gClient.activeThread.resume(function (aResponse) {
+        ok(!aResponse.error, "There shouldn't be an error resuming.");
+        gDebuggee.arithmetic();
+      });
+    });
+  });
+}
+
+function startTest() {
+  if (sourceShown && hitDebuggerStatement) {
+    testCaretPosition();
+  }
+}
+
+function testCaretPosition() {
+  waitForCaretPos(29, function () {
+    closeDebuggerAndFinish();
+  });
+}
+
+function waitForCaretPos(number, callback)
+{
+  // Poll every few milliseconds until the source editor line is active.
+  let count = 0;
+  let intervalID = window.setInterval(function() {
+    info("count: " + count + ", caret at " + gDebugger.DebuggerView.editor.getCaretPosition().line);
+    if (++count > 50) {
+      ok(false, "Timed out while polling for the line.");
+      window.clearInterval(intervalID);
+      return closeDebuggerAndFinish();
+    }
+    if (gDebugger.DebuggerView.editor.getCaretPosition().line != number) {
+      return;
+    }
+    // We got the source editor at the expected line, it's safe to callback.
+    window.clearInterval(intervalID);
+    callback();
+  }, 100);
+}
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+  gClient = null;
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/math.js
@@ -0,0 +1,45 @@
+function add(a, b, k) {
+  var result = a + b;
+  return k(result);
+}
+
+function sub(a, b, k) {
+  var result = a - b;
+  return k(result);
+}
+
+function mul(a, b, k) {
+  var result = a * b;
+  return k(result);
+}
+
+function div(a, b, k) {
+  var result = a / b;
+  return k(result);
+}
+
+function arithmetic() {
+  add(4, 4, function (a) {
+    // 8
+    sub(a, 2, function (b) {
+      // 6
+      mul(b, 3, function (c) {
+        // 18
+        div(c, 2, function (d) {
+          // 9
+          console.log(d);
+        });
+      });
+    });
+  });
+}
+
+// Compile with closure compiler and the following flags:
+//
+//     --compilation_level WHITESPACE_ONLY
+//     --source_map_format V3
+//     --create_source_map math.map
+//     --js_output_file    math.min.js
+//
+// And then append the sourceMappingURL comment directive to math.min.js
+// manually.
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/math.map
@@ -0,0 +1,8 @@
+{
+"version":3,
+"file":"math.min.js",
+"lineCount":1,
+"mappings":"AAAAA,QAASA,IAAG,CAACC,CAAD,CAAIC,CAAJ,CAAOC,CAAP,CAAU,CACpB,IAAIC,OAASH,CAATG,CAAaF,CACjB,OAAOC,EAAA,CAAEC,MAAF,CAFa,CAKtBC,QAASA,IAAG,CAACJ,CAAD,CAAIC,CAAJ,CAAOC,CAAP,CAAU,CACpB,IAAIC,OAASH,CAATG,CAAaF,CACjB,OAAOC,EAAA,CAAEC,MAAF,CAFa,CAKtBE,QAASA,IAAG,CAACL,CAAD,CAAIC,CAAJ,CAAOC,CAAP,CAAU,CACpB,IAAIC,OAASH,CAATG,CAAaF,CACjB,OAAOC,EAAA,CAAEC,MAAF,CAFa,CAKtBG,QAASA,IAAG,CAACN,CAAD,CAAIC,CAAJ,CAAOC,CAAP,CAAU,CACpB,IAAIC,OAASH,CAATG,CAAaF,CACjB,OAAOC,EAAA,CAAEC,MAAF,CAFa,CAKtBI,QAASA,WAAU,EAAG,CACpBR,GAAA,CAAI,CAAJ,CAAO,CAAP,CAAU,QAAS,CAACC,CAAD,CAAI,CAErBI,GAAA,CAAIJ,CAAJ,CAAO,CAAP,CAAU,QAAS,CAACC,CAAD,CAAI,CAErBI,GAAA,CAAIJ,CAAJ,CAAO,CAAP,CAAU,QAAS,CAACO,CAAD,CAAI,CAErBF,GAAA,CAAIE,CAAJ,CAAO,CAAP,CAAU,QAAS,CAACC,CAAD,CAAI,CAErBC,OAAAC,IAAA,CAAYF,CAAZ,CAFqB,CAAvB,CAFqB,CAAvB,CAFqB,CAAvB,CAFqB,CAAvB,CADoB;",
+"sources":["math.js"],
+"names":["add","a","b","k","result","sub","mul","div","arithmetic","c","d","console","log"]
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/math.min.js
@@ -0,0 +1,2 @@
+function add(a,b,k){var result=a+b;return k(result)}function sub(a,b,k){var result=a-b;return k(result)}function mul(a,b,k){var result=a*b;return k(result)}function div(a,b,k){var result=a/b;return k(result)}function arithmetic(){add(4,4,function(a){sub(a,2,function(b){mul(b,3,function(c){div(c,2,function(d){console.log(d)})})})})};
+//@ sourceMappingURL=math.map
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/minified.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <script src="math.min.js"></script>
+  </body>
+</html>
--- a/browser/devtools/profiler/sidebar.js
+++ b/browser/devtools/profiler/sidebar.js
@@ -18,17 +18,17 @@ const {
 } = require("devtools/profiler/consts");
 
 loader.lazyGetter(this, "L10N", () => new ViewHelpers.L10N(L10N_BUNDLE));
 
 function Sidebar(el) {
   EventEmitter.decorate(this);
 
   this.document = el.ownerDocument;
-  this.widget = new SideMenuWidget(el);
+  this.widget = new SideMenuWidget(el, { showArrows: true });
   this.widget.notice = L10N.getStr("profiler.sidebarNotice");
 
   this.widget.addEventListener("select", (ev) => {
     if (!ev.detail)
       return;
 
     this.emit("select", parseInt(ev.detail.value, 10));
   });
@@ -112,9 +112,9 @@ Sidebar.prototype = Heritage.extend(Widg
         return;
     }
 
     item.attachment.state = state;
     this.emit("stateChanged", item);
   }
 });
 
-module.exports = Sidebar;
\ No newline at end of file
+module.exports = Sidebar;
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -561,16 +561,17 @@ function SideMenuItem(aGroup, aContents,
     }, false);
     return checkbox;
   };
 
   if (aArrowFlag || aCheckboxFlag) {
     let container = this._container = this.document.createElement("hbox");
     container.className = "side-menu-widget-item";
     container.setAttribute("tooltiptext", aTooltip);
+    container.setAttribute("align", "start");
 
     let target = this._target = this.document.createElement("vbox");
     target.className = "side-menu-widget-item-contents";
 
     // Show a checkbox before the content.
     if (aCheckboxFlag) {
       let checkbox = this._checkbox = makeCheckbox();
       container.appendChild(checkbox);
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -533,16 +533,18 @@ function setupBracketCompletion(sourceEd
 
     // We detected an open bracket, sending closing character
     let keyCode = pair.closeKeyCode;
     let charCode = pair.closeString.charCodeAt(0);
     let modifiers = 0;
     let utils = editorElement.ownerDocument.defaultView.
                   QueryInterface(Ci.nsIInterfaceRequestor).
                   getInterface(Ci.nsIDOMWindowUtils);
-    let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
-    utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
+                  
+    if (utils.sendKeyEvent("keydown", keyCode, 0, modifiers)) {
+      utils.sendKeyEvent("keypress", 0, charCode, modifiers);
+    }
     utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
     // and rewind caret
     sourceEditor.setCaretOffset(sourceEditor.getCaretOffset() - 1);
   }, false);
 }
 
--- a/browser/devtools/tilt/tilt-visualizer.js
+++ b/browser/devtools/tilt/tilt-visualizer.js
@@ -1222,17 +1222,16 @@ TiltVisualizer.Controller.prototype = {
     canvas.addEventListener("mousedown", this._onMouseDown, false);
     canvas.addEventListener("mouseup", this._onMouseUp, false);
     canvas.addEventListener("mousemove", this._onMouseMove, false);
     canvas.addEventListener("mouseover", this._onMouseOver, false);
     canvas.addEventListener("mouseout", this._onMouseOut, false);
     canvas.addEventListener("MozMousePixelScroll", this._onMozScroll, false);
     canvas.addEventListener("keydown", this._onKeyDown, false);
     canvas.addEventListener("keyup", this._onKeyUp, false);
-    canvas.addEventListener("keypress", this._onKeyPress, true);
     canvas.addEventListener("blur", this._onBlur, false);
 
     // handle resize events to change the arcball dimensions
     presenter.contentWindow.addEventListener("resize", this._onResize, false);
   },
 
   /**
    * Removes all added events listeners required by this controller.
@@ -1245,17 +1244,16 @@ TiltVisualizer.Controller.prototype = {
     canvas.removeEventListener("mousedown", this._onMouseDown, false);
     canvas.removeEventListener("mouseup", this._onMouseUp, false);
     canvas.removeEventListener("mousemove", this._onMouseMove, false);
     canvas.removeEventListener("mouseover", this._onMouseOver, false);
     canvas.removeEventListener("mouseout", this._onMouseOut, false);
     canvas.removeEventListener("MozMousePixelScroll", this._onMozScroll, false);
     canvas.removeEventListener("keydown", this._onKeyDown, false);
     canvas.removeEventListener("keyup", this._onKeyUp, false);
-    canvas.removeEventListener("keypress", this._onKeyPress, true);
     canvas.removeEventListener("blur", this._onBlur, false);
 
     // Closing the tab would result in contentWindow being a dead object,
     // so operations like removing event listeners won't work anymore.
     if (presenter.contentWindow == presenter.chromeWindow.content) {
       presenter.contentWindow.removeEventListener("resize", this._onResize, false);
     }
   },
@@ -1387,16 +1385,25 @@ TiltVisualizer.Controller.prototype = {
 
     if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
       e.preventDefault();
       e.stopPropagation();
       this.arcball.keyDown(code);
     } else {
       this.arcball.cancelKeyEvents();
     }
+
+    if (e.keyCode === e.DOM_VK_ESCAPE) {
+      let {TiltManager} = require("devtools/tilt/tilt");
+      let tilt =
+        TiltManager.getTiltForBrowser(this.presenter.chromeWindow);
+      e.preventDefault();
+      e.stopPropagation();
+      tilt.destroy(tilt.currentWindowId, true);
+    }
   },
 
   /**
    * Called when a key is released.
    */
   _onKeyUp: function TVC__onKeyUp(e)
   {
     let code = e.keyCode || e.which;
@@ -1415,31 +1422,16 @@ TiltVisualizer.Controller.prototype = {
     if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
       e.preventDefault();
       e.stopPropagation();
       this.arcball.keyUp(code);
     }
   },
 
   /**
-   * Called when a key is pressed.
-   */
-  _onKeyPress: function TVC__onKeyPress(e)
-  {
-    if (e.keyCode === e.DOM_VK_ESCAPE) {
-      let {TiltManager} = require("devtools/tilt/tilt");
-      let tilt =
-        TiltManager.getTiltForBrowser(this.presenter.chromeWindow);
-      e.preventDefault();
-      e.stopPropagation();
-      tilt.destroy(tilt.currentWindowId, true);
-    }
-  },
-
-  /**
    * Called when the canvas looses focus.
    */
   _onBlur: function TVC__onBlur(e) {
     this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
--- a/browser/metro/base/content/TopSites.js
+++ b/browser/metro/base/content/TopSites.js
@@ -262,18 +262,16 @@ TopSitesView.prototype = Util.extend(Obj
       // default: no action
     }
     if (nextContextActions.size) {
       // at next tick, re-populate the context appbar
       setTimeout(function(){
         // fire a MozContextActionsChange event to update the context appbar
         let event = document.createEvent("Events");
         event.actions = [...nextContextActions];
-        event.noun = tileGroup.contextNoun;
-        event.qty = selectedTiles.length;
         event.initEvent("MozContextActionsChange", true, false);
         tileGroup.dispatchEvent(event);
       },0);
     }
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type){
--- a/browser/metro/base/content/appbar.js
+++ b/browser/metro/base/content/appbar.js
@@ -40,20 +40,19 @@ var Appbar = {
           this.activeTileset.clearSelection();
         }
         this.clearContextualActions();
         this.activeTileset = null;
         break;
 
       case 'MozContextActionsChange':
         let actions = aEvent.actions;
-        let noun = aEvent.noun;
-        let qty = aEvent.qty;
+        let setName = aEvent.target.contextSetName;
         // could transition in old, new buttons?
-        this.showContextualActions(actions, noun, qty);
+        this.showContextualActions(actions, setName);
         break;
 
       case "selectionchange":
         let nodeName = aEvent.target.nodeName;
         if ('richgrid' === nodeName) {
           this._onTileSelectionChanged(aEvent);
         }
         break;
@@ -156,24 +155,25 @@ var Appbar = {
       activeTileset.dispatchEvent(event);
       if (!event.defaultPrevented) {
         activeTileset.clearSelection();
         Elements.contextappbar.dismiss();
       }
     }
   },
 
-  showContextualActions: function(aVerbs, aNoun, aQty) {
+  showContextualActions: function(aVerbs, aSetName) {
     // When the appbar is not visible, we want the icons to refresh right away
     let immediate = !Elements.contextappbar.isShowing;
 
-    if (aVerbs.length)
+    if (aVerbs.length) {
       Elements.contextappbar.show();
-    else
+    } else {
       Elements.contextappbar.hide();
+    }
 
     // Look up all of the buttons for the verbs that should be visible.
     let idsToVisibleVerbs = new Map();
     for (let verb of aVerbs) {
       let id = verb + "-selected-button";
       if (!document.getElementById(id)) {
         throw new Error("Appbar.showContextualActions: no button for " + verb);
       }
@@ -182,17 +182,17 @@ var Appbar = {
 
     // Sort buttons into 2 buckets - needing showing and needing hiding.
     let toHide = [], toShow = [];
     let buttons = Elements.contextappbar.getElementsByTagName("toolbarbutton");
     for (let button of buttons) {
       let verb = idsToVisibleVerbs.get(button.id);
       if (verb != undefined) {
         // Button should be visible, and may or may not be showing.
-        this._updateContextualActionLabel(button, verb, aNoun, aQty);
+        this._updateContextualActionLabel(button, verb, aSetName);
         if (button.hidden) {
           toShow.push(button);
         }
       } else if (!button.hidden) {
         // Button is visible, but shouldn't be.
         toHide.push(button);
       }
     }
@@ -218,28 +218,22 @@ var Appbar = {
       }
     });
   },
 
   clearContextualActions: function() {
     this.showContextualActions([]);
   },
 
-  _updateContextualActionLabel: function(aBtnNode, aVerb, aNoun, aQty) {
-    // True if action modifies the noun for the grid (bookmark, top site, etc.),
-    // causing the label to be pluralized by the number of selected items.
-    let modifiesNoun = aBtnNode.getAttribute("modifies-noun") == "true";
-    if (modifiesNoun && (!aNoun || isNaN(aQty))) {
-      throw new Error("Appbar._updateContextualActionLabel: " +
-                      "missing noun/quantity for " + aVerb);
-    }
-
-    let labelName = "contextAppbar." + aVerb + (modifiesNoun ? "." + aNoun : "");
-    let label = Strings.browser.GetStringFromName(labelName);
-    aBtnNode.label = modifiesNoun ? PluralForm.get(aQty, label) : label;
+  _updateContextualActionLabel: function(aButton, aVerb, aSetName) {
+    // True if the action's label string contains the set name and
+    // thus has to be selected based on the list passed in.
+    let usesSetName = aButton.hasAttribute("label-uses-set-name");
+    let name = "contextAppbar2." + aVerb + (usesSetName ? "." + aSetName : "");
+    aButton.label = Strings.browser.GetStringFromName(name);
   },
 
   _onTileSelectionChanged: function _onTileSelectionChanged(aEvent){
     let activeTileset = aEvent.target;
 
     // deselect tiles in other tile groups
     if (this.activeTileset && this.activeTileset !== activeTileset) {
       this.activeTileset.clearSelection();
@@ -250,20 +244,18 @@ var Appbar = {
     // ask the view for the list verbs/action-names it thinks are
     // appropriate for the tiles selected
     let contextActions = activeTileset.contextActions;
     let verbs = [v for (v of contextActions)];
 
     // fire event with these verbs as payload
     let event = document.createEvent("Events");
     event.actions = verbs;
-    event.noun = activeTileset.contextNoun;
-    event.qty = activeTileset.selectedItems.length;
     event.initEvent("MozContextActionsChange", true, false);
-    Elements.contextappbar.dispatchEvent(event);
+    activeTileset.dispatchEvent(event);
 
     if (verbs.length) {
       Elements.contextappbar.show(); // should be no-op if we're already showing
     } else {
       Elements.contextappbar.dismiss();
     }
   },
 
--- a/browser/metro/base/content/bindings/grid.xml
+++ b/browser/metro/base/content/bindings/grid.xml
@@ -116,18 +116,18 @@
               return;
             // we'll republish this as a selectionchange event on the grid
             aEvent.stopPropagation();
             this.toggleItemSelection(aItem);
           ]]>
         </body>
       </method>
 
-      <property name="contextNoun" readonly="true"
-                onget="return this.getAttribute('noun');"/>
+      <property name="contextSetName" readonly="true"
+                onget="return this.getAttribute('set-name');"/>
 
       <property name="contextActions">
         <getter>
           <![CDATA[
             // return the subset of verbs that apply to all selected tiles
             let tileNodes = this.selectedItems;
             if (!tileNodes.length) {
               return new Set();
--- a/browser/metro/base/content/bookmarks.js
+++ b/browser/metro/base/content/bookmarks.js
@@ -289,18 +289,16 @@ BookmarksView.prototype = Util.extend(Ob
         aEvent.preventDefault();
 
         // at next tick, re-populate the context appbar.
         setTimeout(function(){
           // fire a MozContextActionsChange event to update the context appbar
           let event = document.createEvent("Events");
           // we need the restore button to show (the tile node will go away though)
           event.actions = ["restore"];
-          event.noun = tileGroup.contextNoun;
-          event.qty = selectedTiles.length;
           event.initEvent("MozContextActionsChange", true, false);
           tileGroup.dispatchEvent(event);
         }, 0);
         break;
 
       case "restore":
         // clear toRemove and let _sendNeedsRefresh update the items.
         this._toRemove = null;
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -191,41 +191,41 @@
         </hbox>
 
         <!-- Start UI -->
         <hbox id="start-container" flex="1" observes="bcast_windowState" class="meta content-height content-width">
           <!-- portrait/landscape/filled view -->
 
           <scrollbox id="start-scrollbox" observes="bcast_preciseInput" flex="1">
             <vbox id="start-topsites" class="meta-section">
-              <label class="meta-section-title wide-title" value="&startTopSitesHeader.label;"/>
+              <label class="meta-section-title wide-title" value="&topSitesHeader.label;"/>
               <label class="meta-section-title narrow-title" value="&snappedTopSitesHeader.label;"
                 onclick="StartUI.onNarrowTitleClick('start-topsites-grid')"/>
-              <richgrid id="start-topsites-grid" noun="topsite" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" flex="1" expanded="true"/>
+              <richgrid id="start-topsites-grid" set-name="topSites" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" flex="1" expanded="true"/>
             </vbox>
 
             <vbox id="start-bookmarks" class="meta-section">
-              <label class="meta-section-title wide-title" value="&startBookmarksHeader.label;"/>
+              <label class="meta-section-title wide-title" value="&bookmarksHeader.label;"/>
               <label class="meta-section-title narrow-title" value="&snappedBookmarksHeader.label;"
                 onclick="StartUI.onNarrowTitleClick('start-bookmarks-grid')"/>
-              <richgrid id="start-bookmarks-grid" noun="bookmark" seltype="multiple" flex="1"/>
+              <richgrid id="start-bookmarks-grid" set-name="bookmarks" seltype="multiple" flex="1"/>
             </vbox>
 
             <vbox id="start-history" class="meta-section">
-              <label class="meta-section-title wide-title" value="&startHistoryHeader.label;"/>
-              <label class="meta-section-title narrow-title" value="&snappedHistoryHeader.label;"
+              <label class="meta-section-title wide-title" value="&recentHistoryHeader.label;"/>
+              <label class="meta-section-title narrow-title" value="&snappedRecentHistoryHeader.label;"
                 onclick="StartUI.onNarrowTitleClick('start-history-grid')"/>
-              <richgrid id="start-history-grid" noun="history" seltype="multiple" flex="1"/>
+              <richgrid id="start-history-grid" set-name="recentHistory" seltype="multiple" flex="1"/>
             </vbox>
 
             <vbox id="start-remotetabs" class="meta-section">
-              <label class="meta-section-title wide-title" value="&startRemoteTabsHeader.label;"/>
+              <label class="meta-section-title wide-title" value="&remoteTabsHeader.label;"/>
               <label id="snappedRemoteTabsLabel" class="meta-section-title narrow-title" value="&snappedRemoteTabsHeader.label;"
                 onclick="StartUI.onNarrowTitleClick('start-remotetabs-grid')"/>
-              <richgrid id="start-remotetabs-grid" noun="tab" seltype="multiple" flex="1"/>
+              <richgrid id="start-remotetabs-grid" set-name="remoteTabs" seltype="multiple" flex="1"/>
             </vbox>
 
             <!-- Spacer to take extra space in snapped mode. -->
             <spacer flex="999"/>
           </scrollbox>
 
         </hbox>
       </vbox> <!-- end tray -->
@@ -306,26 +306,26 @@
           hidden="true" observes="bcast_windowState">
       <hbox id="panel-header">
         <toolbarbutton id="panel-close-button" class="appbar-primary"
                        command="cmd_panel"/>
       </hbox>
 
       <deck id="panel-items" selectedIndex="0" flex="1" >
         <scrollbox id="bookmarks-container" flex="1">
-          <richgrid id="bookmarks-list" noun="bookmark" class="canSnapTiles"
-                    seltype="multiple" flex="1"/>
+          <richgrid id="bookmarks-list" class="canSnapTiles"
+                    set-name="bookmarks" seltype="multiple" flex="1"/>
         </scrollbox>
         <scrollbox id="history-container" flex="1">
-          <richgrid id="history-list" noun="history" class="canSnapTiles"
-                    seltype="multiple" flex="1"/>
+          <richgrid id="history-list" class="canSnapTiles"
+                    set-name="recentHistory" seltype="multiple" flex="1"/>
         </scrollbox>
         <scrollbox id="remotetabs-container" flex="1">
-          <richgrid id="remotetabs-list" noun="tab" class="canSnapTiles"
-                    seltype="single" flex="1"/>
+          <richgrid id="remotetabs-list" class="canSnapTiles"
+                    set-name="remoteTabs" seltype="single" flex="1"/>
         </scrollbox>
         <vbox id="console-container" flex="1">
           <vbox id="console-header" class="panel-list">
             <label class="panel-header" value="&consoleHeader.label;"/>
             <hbox align="center">
               <label value="&consoleCodeEval.label;"
                      control="console-eval-textbox"/>
               <textbox id="console-eval-textbox" class="toolbar search-bar"
@@ -369,28 +369,28 @@
       <button class="next-button" command="cmd_findNext"/>
       <spacer flex="1"/>
       <button id="findbar-close" class="close-button" command="cmd_findClose"/>
     </appbar>
 
     <!-- Context button bar -->
     <appbar id="contextappbar">
       <toolbar id="contextualactions-tray" labelled="true" flex="1">
+        <toolbarbutton id="pin-selected-button" class="appbar-secondary"
+                       label-uses-set-name="true" hidden="true" fade="true"
+                       oncommand="Appbar.dispatchContextualAction('pin')"/>
+        <toolbarbutton id="unpin-selected-button" class="appbar-secondary"
+                       label-uses-set-name="true" hidden="true" fade="true"
+                       oncommand="Appbar.dispatchContextualAction('unpin')"/>
         <toolbarbutton id="delete-selected-button" class="appbar-secondary"
-                       modifies-noun="true" hidden="true" fade="true"
+                       hidden="true" fade="true"
                        oncommand="Appbar.dispatchContextualAction('delete')"/>
         <toolbarbutton id="restore-selected-button" class="appbar-secondary"
-                       modifies-noun="true" hidden="true" fade="true"
+                       hidden="true" fade="true"
                        oncommand="Appbar.dispatchContextualAction('restore')"/>
-        <toolbarbutton id="pin-selected-button" class="appbar-secondary"
-                       modifies-noun="true" hidden="true" fade="true"
-                       oncommand="Appbar.dispatchContextualAction('pin')"/>
-        <toolbarbutton id="unpin-selected-button" class="appbar-secondary"
-                       modifies-noun="true" hidden="true" fade="true"
-                       oncommand="Appbar.dispatchContextualAction('unpin')"/>
         <toolbarbutton id="clear-selected-button" class="appbar-secondary"
                        hidden="true" fade="true"
                        oncommand="Appbar.dispatchContextualAction('clear')"/>
       </toolbar>
     </appbar>
 
     <autoscroller class="autoscroller" id="autoscrollerid"/>
 
@@ -398,17 +398,17 @@
       <label id="about-product-label" value="&aboutHeader.product.label;"/>
       <label value="&aboutHeader.company.label;"/>
 #expand <label id="about-version-label">__MOZ_APP_VERSION__</label>
       <vbox id="updateBox">
 #ifdef MOZ_UPDATER
         <deck id="updateDeck" orient="vertical">
           <hbox id="updateButtonBox" align="center">
             <button id="updateButton" align="start"
-                    oncommand="gAppUpdater.buttonOnCommand();"/>
+                    oncommand="FlyoutPanelsUI.AboutFlyout.appUpdater.buttonOnCommand();"/>
             <spacer flex="1"/>
           </hbox>
           <hbox id="checkingForUpdates" align="center">
             <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
           </hbox>
           <hbox id="checkingAddonCompat" align="center">
             <image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
           </hbox>
--- a/browser/metro/base/content/flyouts/aboutFlyout.js
+++ b/browser/metro/base/content/flyouts/aboutFlyout.js
@@ -47,17 +47,18 @@ let AboutFlyout = {
 #endif
   },
 
   handleEvent: function Appbar_handleEvent(aEvent) {
     switch (aEvent.type) {
       case 'MozFlyoutPanelShowing':
 #ifdef MOZ_UPDATER
         onUnload();
-        gAppUpdater = new appUpdater();
+        this.appUpdater = new appUpdater();
+        gAppUpdater = this.appUpdater;
 #endif
         break;
     }
   }
 };
 
 #ifdef MOZ_UPDATER
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -69,16 +70,17 @@ function onUnload(aEvent) {
     return;
   }
 
   if (gAppUpdater.isChecking)
     gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
   // Safe to call even when there isn't a download in progress.
   gAppUpdater.removeDownloadListener();
   gAppUpdater = null;
+  AboutFlyout.appUpdater = null;
 }
 
 function appUpdater()
 {
   this.updateDeck = document.getElementById("updateDeck");
 
   XPCOMUtils.defineLazyServiceGetter(this, "aus",
                                      "@mozilla.org/updates/update-service;1",
@@ -542,17 +544,17 @@ appUpdater.prototype =
       // Do not remove UI listener since the user may resume downloading again.
       break;
     case Components.results.NS_OK:
       this.removeDownloadListener();
       if (this.backgroundUpdateEnabled) {
         this.selectPanel("applying");
         let update = this.um.activeUpdate;
         let self = this;
-        Services.obs.addObserver(function (aSubject, aTopic, aData) {
+        Services.obs.addObserver(function updateStaged(aSubject, aTopic, aData) {
           // Update the UI when the background updater is finished
           let status = aData;
           if (status == "applied" || status == "applied-service" ||
               status == "pending" || status == "pending-service") {
             // If the update is successfully applied, or if the updater has
             // fallen back to non-staged updates, show the Restart to Update
             // button.
             self.selectPanel("updateButtonBox");
@@ -564,17 +566,17 @@ appUpdater.prototype =
             self.selectPanel("downloadFailed");
           } else if (status == "downloading") {
             // We've fallen back to downloading the full update because the
             // partial update failed to get staged in the background.
             // Therefore we need to keep our observer.
             self.setupDownloadingUI();
             return;
           }
-          Services.obs.removeObserver(arguments.callee, "update-staged");
+          Services.obs.removeObserver(updateStaged, "update-staged");
         }, "update-staged", false);
       } else {
         this.selectPanel("updateButtonBox");
         this.setupUpdateButton("update.restart." +
                                (this.isMajor ? "upgradeButton" : "updateButton"));
       }
       break;
     default:
--- a/browser/metro/base/content/history.js
+++ b/browser/metro/base/content/history.js
@@ -143,18 +143,16 @@ HistoryView.prototype = Util.extend(Obje
         aEvent.preventDefault();
 
         // at next tick, re-populate the context appbar.
         setTimeout(function(){
           // fire a MozContextActionsChange event to update the context appbar
           let event = document.createEvent("Events");
           // we need the restore button to show (the tile node will go away though)
           event.actions = ["restore"];
-          event.noun = tileGroup.contextNoun;
-          event.qty = selectedTiles.length;
           event.initEvent("MozContextActionsChange", true, false);
           tileGroup.dispatchEvent(event);
         }, 0);
         break;
 
       case "restore":
         // clear toRemove and let _sendNeedsRefresh update the items.
         this._toRemove = null;
--- a/browser/metro/locales/en-US/chrome/browser.dtd
+++ b/browser/metro/locales/en-US/chrome/browser.dtd
@@ -1,58 +1,59 @@
 <!-- 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/. -->
 
+<!-- NAVBAR AND AUTOCOMPLETE -->
+
 <!ENTITY urlbar.emptytext      "Enter Search or Address">
 <!ENTITY urlbar.accesskey      "d">
 
 <!ENTITY back.label            "Back">
 <!ENTITY forward.label         "Forward">
 <!ENTITY newtab.label          "New Tab">
 <!ENTITY closetab.label        "Close Tab">
 
+<!ENTITY autocompleteResultsHeader.label  "Your Results">
+<!ENTITY autocompleteSearchesHeader.label "Internet Searches">
+
 <!ENTITY appbarErrorConsole.label   "Open error console">
 <!ENTITY appbarJSShell.label        "Open JavaScript shell">
 <!ENTITY appbarFindInPage2.label    "Find in page">
 <!ENTITY appbarViewOnDesktop2.label "View on desktop">
 
-<!ENTITY startTopSitesHeader.label        "Top Sites">
-<!ENTITY startBookmarksHeader.label       "Bookmarks">
-<!ENTITY startHistoryHeader.label         "Recent History">
-<!ENTITY startRemoteTabsHeader.label      "Tabs from Other Devices">
+<!ENTITY topSitesHeader.label        "Top Sites">
+<!ENTITY bookmarksHeader.label       "Bookmarks">
+<!ENTITY recentHistoryHeader.label   "Recent History">
+<!ENTITY remoteTabsHeader.label      "Tabs from Other Devices">
 
 <!-- LOCALIZATION NOTE (snappedRemoteTabsHeader.label): shortened version of startRemoteTabsHeader.label.
      Needs to be two words or shorter to fit in narrow vertical space.-->
 <!-- LOCALIZATION NOTE (snappedRemoteTabsHeader.label,
                         snappedBookmarksHeader.label,
                         snappedHistoryHeader.label,
                         snappedTopSitesHeader.label )
       The '>' character is not part of the name, but is an indicator of more content. Please do not localize the '>' -->
 <!ENTITY snappedRemoteTabsHeader.label    "Remote Tabs >">
 <!ENTITY snappedBookmarksHeader.label     "Bookmarks >">
-<!ENTITY snappedHistoryHeader.label       "Recent History >">
+<!ENTITY snappedRecentHistoryHeader.label "Recent History >">
 <!ENTITY snappedTopSitesHeader.label      "Top Sites >">
 
-<!ENTITY autocompleteResultsHeader.label  "Your Results">
-<!ENTITY autocompleteSearchesHeader.label "Internet Searches"> 
-
 <!ENTITY downloadsHeader.label     "Downloads">
 <!ENTITY downloadShowPage.label    "Go to Page">
 <!ENTITY downloadShow2.label       "Find">
 <!ENTITY downloadOpen2.label       "Open">
 <!ENTITY downloadCancel.label      "Cancel">
 <!ENTITY downloadPause.label       "Pause">
 <!ENTITY downloadResume.label      "Resume">
 <!ENTITY downloadRetry.label       "Retry">
 <!ENTITY downloadRemove.label      "Remove">
 <!ENTITY downloadDelete.label      "Delete">
 <!ENTITY downloadFailed.label      "Failed">
 
-<!ENTITY bookmarksHeader.label     "Bookmarks">
 <!ENTITY allBookmarks.label        "See all bookmarks">
 
 <!ENTITY consoleHeader.label       "Error Console">
 <!ENTITY consoleAll.label          "All">
 <!ENTITY consoleErrors.label       "Errors">
 <!ENTITY consoleWarnings.label     "Warnings">
 <!ENTITY consoleMessages.label     "Messages">
 <!ENTITY consoleCodeEval.label     "Code:">
--- a/browser/metro/locales/en-US/chrome/browser.properties
+++ b/browser/metro/locales/en-US/chrome/browser.properties
@@ -1,50 +1,52 @@
 # 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/.
 
+# LOCALIZATION NOTE : FILE Capitalized phrases like "Top Sites", "Bookmarks",
+# and "Recent History" are typically used as proper noun phrases to refer to
+# the specific visual set (usually displayed as a grid) of top sites,
+# bookmarks, etc. displayed on screen, rather than those concepts in general.
+# Buttons (like with contextAppbar2.pin.topSites) refer to actions against
+# the specific on-screen sets with similarly-named headings.
+
 # Default search engine
 browser.search.defaultenginename=Bing
 
 # Search engine order (order displayed in the search bar dropdown)s
 browser.search.order.1=Bing
 browser.search.order.2=Google
 browser.search.order.3=Yahoo
 
 # LOCALIZATION NOTE (browser.search.contextTextSearchLabel2): search context
 # menu item text will be: |Search (browser.search.defaultenginename) for "string"|
 browser.search.contextTextSearchLabel2=Search %S for "%S"
 
-# Contextual Appbar - button labels
-# LOCALIZATION NOTE: All of the following represent actions that can be done
-# to items (top sites, bookmarks, history items) that are selected by the user
-# on the start screen. Each string is a semicolon list of plural forms.
-contextAppbar.delete.topsite=Delete top site;Delete top sites
-contextAppbar.restore.topsite=Restore top site;Restore top sites
-contextAppbar.pin.topsite=Pin top site;Pin top sites
-contextAppbar.unpin.topsite=Unpin top site;Unpin top sites
-contextAppbar.delete.bookmark=Delete bookmark;Delete bookmarks
-contextAppbar.restore.bookmark=Restore bookmark;Restore bookmarks
-contextAppbar.pin.bookmark=Pin bookmark;Pin bookmarks
-contextAppbar.unpin.bookmark=Unpin bookmark;Unpin bookmarks
-contextAppbar.delete.history=Delete page;Delete pages
-contextAppbar.restore.history=Restore page;Restore pages
-contextAppbar.pin.history=Pin page;Pin pages
-contextAppbar.unpin.history=Unpin page;Unpin pages
-contextAppbar.delete.tab=Delete tab;Delete tabs
-contextAppbar.restore.tab=Restore tab;Restore tabs
-contextAppbar.pin.tab=Pin tab;Pin tabs
-contextAppbar.unpin.tab=Unpin page;Unpin tabs
-contextAppbar.delete.download=Delete download;Delete downloads
-contextAppbar.restore.download=Restore download;Restore downloads
-contextAppbar.pin.download=Pin download;Pin downloads
-contextAppbar.unpin.download=Unpin page;Unpin downloads
-# LOCALIZATION NOTE (contextAppbar.clear): Unselects items without modification.
-contextAppbar.clear=Clear selection
+# Contextual Appbar - Button Labels
+
+contextAppbar2.pin.topSites=Pin to Top Sites
+contextAppbar2.pin.bookmarks=Pin to Bookmarks
+contextAppbar2.pin.recentHistory=Pin to Recent History
+contextAppbar2.pin.downloads=Pin to Downloads
+
+contextAppbar2.unpin.topSites=Unpin from Top Sites
+contextAppbar2.unpin.bookmarks=Unpin from Bookmarks
+contextAppbar2.unpin.recentHistory=Unpin from Recent History
+contextAppbar2.unpin.downloads=Unpin from Downloads
+
+# LOCALIZATION NOTE (contextAppbar2.delete): Deletes selected pages.
+contextAppbar2.delete=Delete
+
+# LOCALIZATION NOTE (contextAppbar2.restore): Undoes a previous deletion.
+# Button with this label only appears immediately after a deletion.
+contextAppbar2.restore=Undo delete
+
+# LOCALIZATION NOTE (contextAppbar2.clear): Unselects pages without modification.
+contextAppbar2.clear=Clear selection
 
 # Settings Charms
 aboutCharm1=About
 optionsCharm=Options
 syncCharm=Sync
 helpOnlineCharm=Help (online)
 
 # General
--- a/browser/modules/AboutHomeUtils.jsm
+++ b/browser/modules/AboutHomeUtils.jsm
@@ -20,23 +20,21 @@ this.AboutHomeUtils = {
 
   /**
    * Returns an object containing the name and searchURL of the original default
    * search engine.
    */
   get defaultSearchEngine() {
     let defaultEngine = Services.search.defaultEngine;
     let submission = defaultEngine.getSubmission("_searchTerms_", null, "homepage");
-    if (submission.postData) {
-      throw new Error("Home page does not support POST search engines.");
-    }
   
     return Object.freeze({
       name: defaultEngine.name,
-      searchURL: submission.uri.spec
+      searchURL: submission.uri.spec,
+      postDataString: submission.postDataString
     });
   },
 
   /*
    * showKnowYourRights - Determines if the user should be shown the
    * about:rights notification. The notification should *not* be shown if
    * we've already shown the current version, or if the override pref says to
    * never show it. The notification *should* be shown if it's never been seen
--- a/browser/themes/linux/devtools/debugger.css
+++ b/browser/themes/linux/devtools/debugger.css
@@ -16,16 +16,27 @@
 #sources-pane + .devtools-side-splitter {
   -moz-border-start-color: transparent;
 }
 
 .side-menu-widget-item-checkbox {
  -moz-appearance: none;
   padding: 0;
   margin: 0 -4px 0 4px;
+  opacity: 0;
+  transition: opacity .25s ease 0s;
+}
+
+/* Only show the checkbox when the source is hovered over, is selected, or if it
+ * is not checked. */
+.side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
+.side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
+.side-menu-widget-item-checkbox:not([checked]) {
+  opacity: 1;
+  transition: opacity .25s ease 0s;
 }
 
 .side-menu-widget-item-checkbox > .checkbox-check {
   -moz-appearance: none;
   background: none;
   background-image: url(itemToggle.png);
   background-repeat: no-repeat;
   background-clip: content-box;
--- a/browser/themes/linux/devtools/widgets.css
+++ b/browser/themes/linux/devtools/widgets.css
@@ -335,27 +335,30 @@
 }
 
 .side-menu-widget-item-arrow {
   -moz-margin-start: -8px;
   width: 8px;
 }
 
 .side-menu-widget-item-contents {
-  padding: 4px;
+  padding: 4px 0px;
 }
 
 .side-menu-widget-item label {
   cursor: inherit;
 }
 
+.side-menu-widget-item-other:first-of-type {
+  border-top-left-radius: 4px;
+}
+
 .side-menu-widget-item-other {
   background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65);
-  margin: 0 -4px;
-  -moz-padding-start: 5px;
+  -moz-margin-start: -22px;
 }
 
 .side-menu-widget-item-other.selected {
   background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.15);
   box-shadow: inset 0 1px 0 hsla(210,40%,83%,.07),
               inset 0 -1px 0 hsla(210,40%,83%,.07);
 }
 
--- a/browser/themes/osx/devtools/debugger.css
+++ b/browser/themes/osx/devtools/debugger.css
@@ -18,16 +18,27 @@
 #sources-pane + .devtools-side-splitter {
   -moz-border-start-color: transparent;
 }
 
 .side-menu-widget-item-checkbox {
  -moz-appearance: none;
   padding: 0;
   margin: 0 -4px 0 4px;
+  opacity: 0;
+  transition: opacity .25s ease-out 0s;
+}
+
+/* Only show the checkbox when the source is hovered over, is selected, or if it
+ * is not checked. */
+.side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
+.side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
+.side-menu-widget-item-checkbox:not([checked]) {
+  opacity: 1;
+  transition: opacity .25s ease-out 0s;
 }
 
 .side-menu-widget-item-checkbox > .checkbox-check {
   -moz-appearance: none;
   background: none;
   background-image: url(itemToggle.png);
   background-repeat: no-repeat;
   background-clip: content-box;
--- a/browser/themes/osx/devtools/widgets.css
+++ b/browser/themes/osx/devtools/widgets.css
@@ -335,27 +335,30 @@
 }
 
 .side-menu-widget-item-arrow {
   -moz-margin-start: -8px;
   width: 8px;
 }
 
 .side-menu-widget-item-contents {
-  padding: 4px;
+  padding: 4px 0px;
 }
 
 .side-menu-widget-item label {
   cursor: inherit;
 }
 
+.side-menu-widget-item-other:first-of-type {
+  border-top-left-radius: 4px;
+}
+
 .side-menu-widget-item-other {
   background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65);
-  margin: 0 -4px;
-  -moz-padding-start: 5px;
+  -moz-margin-start: -22px;
 }
 
 .side-menu-widget-item-other.selected {
   background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.15);
   box-shadow: inset 0 1px 0 hsla(210,40%,83%,.07),
               inset 0 -1px 0 hsla(210,40%,83%,.07);
 }
 
--- a/browser/themes/windows/devtools/debugger.css
+++ b/browser/themes/windows/devtools/debugger.css
@@ -16,16 +16,27 @@
 #sources-pane + .devtools-side-splitter {
   -moz-border-start-color: transparent;
 }
 
 .side-menu-widget-item-checkbox {
  -moz-appearance: none;
   padding: 0;
   margin: 0 -4px 0 4px;
+  opacity: 0;
+  transition: opacity .25s ease 0s;
+}
+
+/* Only show the checkbox when the source is hovered over, is selected, or if it
+ * is not checked. */
+.side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
+.side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
+.side-menu-widget-item-checkbox:not([checked]) {
+  opacity: 1;
+  transition: opacity .25s ease-out 0s;
 }
 
 .side-menu-widget-item-checkbox > .checkbox-check {
   -moz-appearance: none;
   background: none;
   background-image: url(itemToggle.png);
   background-repeat: no-repeat;
   background-clip: content-box;
--- a/browser/themes/windows/devtools/widgets.css
+++ b/browser/themes/windows/devtools/widgets.css
@@ -339,27 +339,30 @@
 }
 
 .side-menu-widget-item-arrow {
   -moz-margin-start: -8px;
   width: 8px;
 }
 
 .side-menu-widget-item-contents {
-  padding: 4px;
+  padding: 4px 0px;
 }
 
 .side-menu-widget-item label {
   cursor: inherit;
 }
 
+.side-menu-widget-item-other:first-of-type {
+  border-top-left-radius: 4px;
+}
+
 .side-menu-widget-item-other {
   background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65);
-  margin: 0 -4px;
-  -moz-padding-start: 5px;
+  -moz-margin-start: -22px;
 }
 
 .side-menu-widget-item-other.selected {
   background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.15);
   box-shadow: inset 0 1px 0 hsla(210,40%,83%,.07),
               inset 0 -1px 0 hsla(210,40%,83%,.07);
 }
 
--- a/configure.in
+++ b/configure.in
@@ -8145,17 +8145,21 @@ dnl ====================================
 
 if test "${OS_TARGET}" = "WINNT"; then
   if $PERL -e "exit($MOZ_WINSDK_MAXVER < 0x06020000)"; then
     MOZ_ENABLE_DIRECT2D1_1=1
     AC_SUBST(MOZ_ENABLE_DIRECT2D1_1)
   fi
 fi
 
-if test "${OS_TARGET}" = "WINNT" -o "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
+if test "${OS_TARGET}" = "WINNT" -o \
+        "${OS_ARCH}" = "Darwin" -o \
+        "${MOZ_WIDGET_TOOLKIT}" = "android" -o \
+        "${MOZ_WIDGET_TOOLKIT}" = "gonk" -o \
+        "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
     case "${target_cpu}" in
     i*86*|x86_64|arm)
         MOZ_ENABLE_SKIA=1
         ;;
     *)
         MOZ_ENABLE_SKIA=
         ;;
     esac
--- a/content/base/test/test_CSP_bug888172.html
+++ b/content/base/test/test_CSP_bug888172.html
@@ -55,24 +55,24 @@ function checkDefaultSrcWithStyleSrc() {
   // last test calls finish
   SimpleTest.finish();
 }
 
 SpecialPowers.pushPrefEnv(
   {'set':[["security.csp.speccompliant", true]]},
   function () {
     document.getElementById('testframe1').src = 'file_CSP_bug888172.sjs?csp=' +
-      escape("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'");
+      escape("default-src 'self' 'unsafe-inline' 'unsafe-eval'");
     document.getElementById('testframe1').addEventListener('load', checkDefaultSrcOnly, false);
 
     document.getElementById('testframe2').src = 'file_CSP_bug888172.sjs?csp=' +
-      escape("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'self'");
+      escape("default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'self'");
     document.getElementById('testframe2').addEventListener('load', checkDefaultSrcWithScriptSrc, false);
 
     document.getElementById('testframe3').src = 'file_CSP_bug888172.sjs?csp=' +
-      escape("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self'");
+      escape("default-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self'");
     document.getElementById('testframe3').addEventListener('load', checkDefaultSrcWithStyleSrc, false);
   }
 );
 </script>
 </pre>
 </body>
 </html>
--- a/content/events/test/test_dom_keyboard_event.html
+++ b/content/events/test/test_dom_keyboard_event.html
@@ -268,17 +268,20 @@ function testSynthesizedKeyLocation()
   var currentTest, description;
   var events = { keydown: false, keypress: false, keyup: false };
 
   function handler(aEvent)
   {
     is(aEvent.location, currentTest.event.location,
        description + "location of " + aEvent.type + " was invalid");
     events[aEvent.type] = true;
-    aEvent.preventDefault();
+    if (aEvent.type != "keydown" ||
+        (currentTest.event.isModifier && aEvent.type == "keydown")) {
+      aEvent.preventDefault();
+    }
   }
 
   window.addEventListener("keydown", handler, true);
   window.addEventListener("keypress", handler, true);
   window.addEventListener("keyup", handler, true);
 
   for (var i = 0; i < kTests.length; i++) {
     currentTest = kTests[i];
--- a/content/media/MediaDecoderReader.cpp
+++ b/content/media/MediaDecoderReader.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "GonkIOSurfaceImage.h"
+#include "GrallocImages.h"
 #include "MediaDecoderReader.h"
 #include "AbstractMediaDecoder.h"
 #include "MediaDecoderStateMachine.h"
 #include "VideoUtils.h"
 #include "ImageContainer.h"
 
 #include "mozilla/mozalloc.h"
 #include "mozilla/StandardInteger.h"
@@ -343,26 +343,26 @@ VideoData* VideoData::Create(VideoInfo& 
 
   nsAutoPtr<VideoData> v(new VideoData(aOffset,
                                        aTime,
                                        aEndTime,
                                        aKeyframe,
                                        aTimecode,
                                        aInfo.mDisplay));
 
-  ImageFormat format = GONK_IO_SURFACE;
+  ImageFormat format = GRALLOC_PLANAR_YCBCR;
   v->mImage = aContainer->CreateImage(&format, 1);
   if (!v->mImage) {
     return nullptr;
   }
-  NS_ASSERTION(v->mImage->GetFormat() == GONK_IO_SURFACE,
+  NS_ASSERTION(v->mImage->GetFormat() == GRALLOC_PLANAR_YCBCR,
                "Wrong format?");
-  typedef mozilla::layers::GonkIOSurfaceImage GonkIOSurfaceImage;
-  GonkIOSurfaceImage* videoImage = static_cast<GonkIOSurfaceImage*>(v->mImage.get());
-  GonkIOSurfaceImage::Data data;
+  typedef mozilla::layers::GrallocImage GrallocImage;
+  GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get());
+  GrallocImage::GrallocData data;
 
   data.mPicSize = gfxIntSize(aPicture.width, aPicture.height);
   data.mGraphicBuffer = aBuffer;
 
   videoImage->SetData(data);
 
   return v.forget();
 }
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -142,17 +142,17 @@ MediaRecorder::ExtractEncodedData()
   TimeStamp lastBlobTimeStamp = TimeStamp::Now();
   do {
     nsTArray<nsTArray<uint8_t> > outputBufs;
     mEncoder->GetEncodedData(&outputBufs, mMimeType);
     for (uint i = 0; i < outputBufs.Length(); i++) {
       mEncodedBufferCache->AppendBuffer(outputBufs[i]);
     }
 
-    if ((TimeStamp::Now() - lastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
+    if (mTimeSlice > 0 && (TimeStamp::Now() - lastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
       NS_DispatchToMainThread(new PushBlobTask(this));
       lastBlobTimeStamp = TimeStamp::Now();
     }
   } while (mState == RecordingState::Recording && !mEncoder->IsShutdown());
 
   NS_DispatchToMainThread(new PushBlobTask(this));
 }
 
--- a/content/media/omx/MPAPI.h
+++ b/content/media/omx/MPAPI.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(MPAPI_h_)
 #define MPAPI_h_
 
 #include <stdint.h>
-#include "GonkIOSurfaceImage.h"
+#include "GrallocImages.h"
 
 namespace MPAPI {
 
 struct VideoPlane {
   VideoPlane() :
     mData(nullptr),
     mStride(0),
     mWidth(0),
--- a/content/media/omx/OmxDecoder.h
+++ b/content/media/omx/OmxDecoder.h
@@ -3,17 +3,17 @@
 #include <stagefright/foundation/ALooper.h>
 #include <stagefright/MediaSource.h>
 #include <stagefright/DataSource.h>
 #include <stagefright/MediaSource.h>
 #include <utils/RefBase.h>
 
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
-#include "GonkIOSurfaceImage.h"
+#include "GrallocImages.h"
 #include "MPAPI.h"
 #include "MediaResource.h"
 #include "AbstractMediaDecoder.h"
 #include "OMXCodecProxy.h"
 
 namespace android {
 class OmxDecoder;
 };
--- a/dom/activities/src/ActivitiesService.jsm
+++ b/dom/activities/src/ActivitiesService.jsm
@@ -232,17 +232,21 @@ let Activities = {
         debug("Sending system message...");
         let result = aResults.options[aChoice];
         sysmm.sendMessage("activity", {
             "id": aMsg.id,
             "payload": aMsg.options,
             "target": result.description
           },
           Services.io.newURI(result.description.href, null, null),
-          Services.io.newURI(result.manifest, null, null));
+          Services.io.newURI(result.manifest, null, null),
+          {
+            "manifestURL": Activities.callers[aMsg.id].manifestURL,
+            "pageURL": Activities.callers[aMsg.id].pageURL
+          });
 
         if (!result.description.returnValue) {
           Activities.callers[aMsg.id].mm.sendAsyncMessage("Activity:FireSuccess", {
             "id": aMsg.id,
             "result": null
           });
           // No need to notify observers, since we don't want the caller
           // to be raised on the foreground that quick.
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -277,32 +277,38 @@ nsPluginArray::EnsurePlugins()
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPluginElement)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPluginElement)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginElement)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsPluginElement,
-                                        mMimeTypes)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPluginElement)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  // Invalidate before we unlink mMimeTypes
+  tmp->Invalidate();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMimeTypes)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPluginElement)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsPluginElement)
 
 nsPluginElement::nsPluginElement(nsWeakPtr aWindow,
                                  nsPluginTag* aPluginTag)
   : mWindow(aWindow),
     mPluginTag(aPluginTag)
 {
   SetIsDOMBinding();
 }
 
 nsPluginElement::~nsPluginElement()
 {
-  for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
-    mMimeTypes[i]->Invalidate();
-  }
+  Invalidate();
 }
 
 nsPIDOMWindow*
 nsPluginElement::GetParentObject() const
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mWindow));
   MOZ_ASSERT(win);
   return win;
@@ -414,8 +420,16 @@ nsPluginElement::EnsureMimeTypes()
     return;
   }
 
   for (uint32_t i = 0; i < mPluginTag->mMimeTypes.Length(); ++i) {
     NS_ConvertUTF8toUTF16 type(mPluginTag->mMimeTypes[i]);
     mMimeTypes.AppendElement(new nsMimeType(mWindow, this, i, type));
   }
 }
+
+void
+nsPluginElement::Invalidate()
+{
+  for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
+    mMimeTypes[i]->Invalidate();
+  }
+}
--- a/dom/base/nsPluginArray.h
+++ b/dom/base/nsPluginArray.h
@@ -97,15 +97,16 @@ public:
   nsMimeType* NamedGetter(const nsAString& name, bool &found);
   uint32_t Length();
   void GetSupportedNames(nsTArray< nsString >& retval);
 
   nsTArray<nsRefPtr<nsMimeType> >& MimeTypes();
 
 protected:
   void EnsureMimeTypes();
+  void Invalidate();
 
   nsWeakPtr mWindow;
   nsRefPtr<nsPluginTag> mPluginTag;
   nsTArray<nsRefPtr<nsMimeType> > mMimeTypes;
 };
 
 #endif /* nsPluginArray_h___ */
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -793,17 +793,19 @@ BluetoothService::Notify(const Bluetooth
     NS_WARNING(warningMsg.get());
     return;
   }
 
   nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
     do_GetService("@mozilla.org/system-message-internal;1");
   NS_ENSURE_TRUE_VOID(systemMessenger);
 
-  systemMessenger->BroadcastMessage(type, OBJECT_TO_JSVAL(obj));
+  systemMessenger->BroadcastMessage(type,
+                                    OBJECT_TO_JSVAL(obj),
+                                    JS::UndefinedValue());
 }
 
 void
 BluetoothService::DispatchToCommandThread(nsRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
   MOZ_ASSERT(mBluetoothCommandThread);
--- a/dom/bluetooth/BluetoothUtils.cpp
+++ b/dom/bluetooth/BluetoothUtils.cpp
@@ -119,17 +119,19 @@ BroadcastSystemMessage(const nsAString& 
     NS_WARNING("Failed to set properties of system message!");
     return false;
   }
 
   nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
     do_GetService("@mozilla.org/system-message-internal;1");
   NS_ENSURE_TRUE(systemMessenger, false);
 
-  systemMessenger->BroadcastMessage(aType, OBJECT_TO_JSVAL(obj));
+  systemMessenger->BroadcastMessage(aType,
+                                    OBJECT_TO_JSVAL(obj),
+                                    JS::UndefinedValue());
 
   return true;
 }
 
 void
 DispatchBluetoothReply(BluetoothReplyRunnable* aRunnable,
                        const BluetoothValue& aValue,
                        const nsAString& aErrorStr)
--- a/dom/browser-element/BrowserElementPanning.js
+++ b/dom/browser-element/BrowserElementPanning.js
@@ -211,16 +211,19 @@ const ContentPanning = {
     if (this.target && click && (this.panning || this.preventNextClick)) {
       let target = this.target;
       let view = target.ownerDocument ? target.ownerDocument.defaultView
                                       : target;
       view.addEventListener('click', this, true, true);
     }
 
     this._finishPanning();
+
+    // Now that we're done, avoid entraining the thing we just panned.
+    this.pointerDownTarget = null;
   },
 
   // True when there's an async pan-zoom controll watching the
   // outermost scrollable frame, and we're waiting to see whether
   // we're going to take over from it and synchronously scroll an
   // inner scrollable frame.
   detectingScrolling: false,
 
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -1481,27 +1481,27 @@ AutoFocusComplete(nsGonkCameraControl* g
 
 static void
 GonkFrameBuilder(Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight)
 {
   /**
    * Cast the generic Image back to our platform-specific type and
    * populate it.
    */
-  GonkIOSurfaceImage* videoImage = static_cast<GonkIOSurfaceImage*>(aImage);
-  GonkIOSurfaceImage::Data data;
+  GrallocImage* videoImage = static_cast<GrallocImage*>(aImage);
+  GrallocImage::GrallocData data;
   data.mGraphicBuffer = static_cast<layers::GraphicBufferLocked*>(aBuffer);
   data.mPicSize = gfxIntSize(aWidth, aHeight);
   videoImage->SetData(data);
 }
 
 void
 ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer)
 {
-  if (!gc->ReceiveFrame(aBuffer, ImageFormat::GONK_IO_SURFACE, GonkFrameBuilder)) {
+  if (!gc->ReceiveFrame(aBuffer, ImageFormat::GRALLOC_PLANAR_YCBCR, GonkFrameBuilder)) {
     aBuffer->Unlock();
   }
 }
 
 void
 OnShutter(nsGonkCameraControl* gc)
 {
   gc->OnShutter();
--- a/dom/camera/GonkNativeWindow.h
+++ b/dom/camera/GonkNativeWindow.h
@@ -27,17 +27,17 @@
 #include <ui/Rect.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 #include <utils/threads.h>
 
 #include "mozilla/layers/LayersSurfaces.h"
 #include "mozilla/layers/ImageBridgeChild.h"
-#include "GonkIOSurfaceImage.h"
+#include "GrallocImages.h"
 #include "CameraCommon.h"
 
 namespace android {
 
 // The user of GonkNativeWindow who wants to receive notification of
 // new frames should implement this interface.
 class GonkNativeWindowNewFrameCallback {
 public:
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -163,98 +163,104 @@ SystemMessageInternal.prototype = {
       if (this._isPageMatched(aPage, aType, aPageURL, aManifestURL)) {
         page = aPage;
       }
       return page !== null;
     }, this);
     return page;
   },
 
-  sendMessage: function sendMessage(aType, aMessage, aPageURI, aManifestURI) {
+  sendMessage: function sendMessage(aType, aMessage, aPageURI, aManifestURI, aExtra) {
     // Buffer system messages until the webapps' registration is ready,
     // so that we can know the correct pages registered to be sent.
     if (!this._webappsRegistryReady) {
       this._bufferedSysMsgs.push({ how: "send",
                                    type: aType,
                                    msg: aMessage,
                                    pageURI: aPageURI,
-                                   manifestURI: aManifestURI });
+                                   manifestURI: aManifestURI,
+                                   extra: aExtra });
       return;
     }
 
     // Give this message an ID so that we can identify the message and
     // clean it up from the pending message queue when apps receive it.
     let messageID = gUUIDGenerator.generateUUID().toString();
 
     debug("Sending " + aType + " " + JSON.stringify(aMessage) +
-      " for " + aPageURI.spec + " @ " + aManifestURI.spec);
+      " for " + aPageURI.spec + " @ " + aManifestURI.spec +
+      '; extra: ' + JSON.stringify(aExtra));
 
     let result = this._sendMessageCommon(aType,
                                          aMessage,
                                          messageID,
                                          aPageURI.spec,
-                                         aManifestURI.spec);
+                                         aManifestURI.spec,
+                                         aExtra);
     debug("Returned status of sending message: " + result);
 
     // Don't need to open the pages and queue the system message
     // which was not allowed to be sent.
     if (result === MSG_SENT_FAILURE_PERM_DENIED) {
       return;
     }
 
     let page = this._findPage(aType, aPageURI.spec, aManifestURI.spec);
     if (page) {
       // Queue this message in the corresponding pages.
       this._queueMessage(page, aMessage, messageID);
 
-        if (result === MSG_SENT_FAILURE_APP_NOT_RUNNING) {
-          // Don't open the page again if we already sent the message to it.
-          this._openAppPage(page, aMessage);
-        }
+      if (result === MSG_SENT_FAILURE_APP_NOT_RUNNING) {
+        // Don't open the page again if we already sent the message to it.
+        this._openAppPage(page, aMessage, aExtra);
+      }
     }
   },
 
-  broadcastMessage: function broadcastMessage(aType, aMessage) {
+  broadcastMessage: function broadcastMessage(aType, aMessage, aExtra) {
     // Buffer system messages until the webapps' registration is ready,
     // so that we can know the correct pages registered to be broadcasted.
     if (!this._webappsRegistryReady) {
       this._bufferedSysMsgs.push({ how: "broadcast",
                                    type: aType,
-                                   msg: aMessage });
+                                   msg: aMessage,
+                                   extra: aExtra });
       return;
     }
 
     // Give this message an ID so that we can identify the message and
     // clean it up from the pending message queue when apps receive it.
     let messageID = gUUIDGenerator.generateUUID().toString();
 
-    debug("Broadcasting " + aType + " " + JSON.stringify(aMessage));
+    debug("Broadcasting " + aType + " " + JSON.stringify(aMessage) +
+      '; extra = ' + JSON.stringify(aExtra));
     // Find pages that registered an handler for this type.
     this._pages.forEach(function(aPage) {
       if (aPage.type == aType) {
         let result = this._sendMessageCommon(aType,
                                              aMessage,
                                              messageID,
                                              aPage.uri,
-                                             aPage.manifest);
+                                             aPage.manifest,
+                                             aExtra);
         debug("Returned status of sending message: " + result);
 
 
         // Don't need to open the pages and queue the system message
         // which was not allowed to be sent.
         if (result === MSG_SENT_FAILURE_PERM_DENIED) {
           return;
         }
 
         // Queue this message in the corresponding pages.
         this._queueMessage(aPage, aMessage, messageID);
 
         if (result === MSG_SENT_FAILURE_APP_NOT_RUNNING) {
           // Open app pages to handle their pending messages.
-          this._openAppPage(aPage, aMessage);
+          this._openAppPage(aPage, aMessage, aExtra);
         }
       }
     }, this);
   },
 
   registerPage: function registerPage(aType, aPageURI, aManifestURI) {
     if (!aPageURI || !aManifestURI) {
       throw Cr.NS_ERROR_INVALID_ARG;
@@ -498,20 +504,20 @@ SystemMessageInternal.prototype = {
       case "webapps-registry-ready":
         // After the webapps' registration has been done for sure,
         // re-fire the buffered system messages if there is any.
         this._webappsRegistryReady = true;
         this._bufferedSysMsgs.forEach(function(aSysMsg) {
           switch (aSysMsg.how) {
             case "send":
               this.sendMessage(
-                aSysMsg.type, aSysMsg.msg, aSysMsg.pageURI, aSysMsg.manifestURI);
+                aSysMsg.type, aSysMsg.msg, aSysMsg.pageURI, aSysMsg.manifestURI, aSysMsg.extra);
               break;
             case "broadcast":
-              this.broadcastMessage(aSysMsg.type, aSysMsg.msg);
+              this.broadcastMessage(aSysMsg.type, aSysMsg.msg, aSysMsg.extra);
               break;
           }
         }, this);
         this._bufferedSysMsgs.length = 0;
         break;
     }
   },
 
@@ -519,21 +525,22 @@ SystemMessageInternal.prototype = {
     // Queue the message for this page because we've never known if an app is
     // opened or not. We'll clean it up when the app has already received it.
     aPage.pendingMessages.push({ msg: aMessage, msgID: aMessageID });
     if (aPage.pendingMessages.length > kMaxPendingMessages) {
       aPage.pendingMessages.splice(0, 1);
     }
   },
 
-  _openAppPage: function _openAppPage(aPage, aMessage) {
+  _openAppPage: function _openAppPage(aPage, aMessage, aExtra) {
     // We don't need to send the full object to observers.
     let page = { uri: aPage.uri,
                  manifest: aPage.manifest,
                  type: aPage.type,
+                 extra: aExtra,
                  target: aMessage.target };
     debug("Asking to open " + JSON.stringify(page));
     Services.obs.notifyObservers(this, "system-messages-open-app", JSON.stringify(page));
   },
 
   _isPageMatched: function _isPageMatched(aPage, aType, aPageURI, aManifestURI) {
     return (aPage.type === aType &&
             aPage.manifest === aManifestURI &&
@@ -554,17 +561,17 @@ SystemMessageInternal.prototype = {
       let data = converter.convertToByteArray(aPage[aProp], {});
       hasher.update(data, data.length);
     });
 
     return hasher.finish(true);
   },
 
   _sendMessageCommon:
-    function _sendMessageCommon(aType, aMessage, aMessageID, aPageURI, aManifestURI) {
+    function _sendMessageCommon(aType, aMessage, aMessageID, aPageURI, aManifestURI, aExtra) {
     // Don't send the system message not granted by the app's permissions.
     if (!SystemMessagePermissionsChecker
           .isSystemMessagePermittedToSend(aType,
                                           aPageURI,
                                           aManifestURI)) {
       return MSG_SENT_FAILURE_PERM_DENIED;
     }
 
--- a/dom/messages/interfaces/nsISystemMessagesInternal.idl
+++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl
@@ -4,35 +4,39 @@
 
 #include "domstubs.idl"
 
 interface nsIURI;
 interface nsIDOMWindow;
 
 // Implemented by the contract id @mozilla.org/system-message-internal;1
 
-[scriptable, uuid(d8de761a-94fe-44d5-80eb-3c8bd8cd7d0b)]
+[scriptable, uuid(6296a314-2abf-4cd0-9097-5e81ee6832e2)]
 interface nsISystemMessagesInternal : nsISupports
 {
   /*
    * Allow any internal user to broadcast a message of a given type.
    * @param type        The type of the message to be sent.
    * @param message     The message payload.
    * @param pageURI     The URI of the page that will be opened.
    * @param manifestURI The webapp's manifest URI.
+   * @param extra       Extra opaque information that will be passed around in the observer
+   *                    notification to open the page.
    */
-  void sendMessage(in DOMString type, in jsval message, in nsIURI pageURI, in nsIURI manifestURI);
+  void sendMessage(in DOMString type, in jsval message, in nsIURI pageURI, in nsIURI manifestURI, [optional] in jsval extra);
 
   /*
    * Allow any internal user to broadcast a message of a given type.
    * The application that registers the message will be launched.
    * @param type        The type of the message to be sent.
    * @param message     The message payload.
+   * @param extra       Extra opaque information that will be passed around in the observer
+   *                    notification to open the page.
    */
-  void broadcastMessage(in DOMString type, in jsval message);
+  void broadcastMessage(in DOMString type, in jsval message, [optional] in jsval extra);
 
   /*
    * Registration of a page that wants to be notified of a message type.
    * @param type          The message type.
    * @param pageURI       The URI of the page that will be opened.
    * @param manifestURI   The webapp's manifest URI.
    */
   void registerPage(in DOMString type, in nsIURI pageURI, in nsIURI manifestURI);
--- a/dom/network/interfaces/nsIDOMMobileConnection.idl
+++ b/dom/network/interfaces/nsIDOMMobileConnection.idl
@@ -376,28 +376,78 @@ interface nsIDOMMozMobileNetworkInfo: ns
   /**
    * State of this network operator.
    *
    * Possible values: 'available', 'connected', 'forbidden', or null (unknown)
    */
   readonly attribute DOMString state;
 };
 
-[scriptable, uuid(aa546788-4f34-488b-8c3e-2786e02ab992)]
+[scriptable, uuid(9750b3a7-d913-436e-95d4-7ef2973ec6a1)]
 interface nsIDOMMozMobileCellInfo: nsISupports
 {
   /**
    * Mobile Location Area Code (LAC) for GSM/WCDMA networks.
+   *
+   * Possible ranges from 0x0000 to 0xffff.
+   * -1 if the LAC is unknown.
    */
-  readonly attribute unsigned short gsmLocationAreaCode;
+  readonly attribute long gsmLocationAreaCode;
 
   /**
    * Mobile Cell ID for GSM/WCDMA networks.
+   *
+   * Possible ranges from 0x00000000 to 0xffffffff.
+   * -1 if the cell id is unknown.
    */
-  readonly attribute unsigned long gsmCellId;
+  readonly attribute long long gsmCellId;
+
+  /**
+   * Base Station ID for CDMA networks.
+   *
+   * Possible ranges from 0 to 65535
+   * -1 if the base station id is unknown.
+   */
+  readonly attribute long cdmaBaseStationId;
+
+  /**
+   * Base Station Latitude for CDMA networks.
+   *
+   * Possible ranges from -1296000 to 1296000.
+   * -2147483648 if the latitude is unknown.
+   *
+   * @see 3GPP2 C.S0005-A v6.0.
+   */
+  readonly attribute long cdmaBaseStationLatitude;
+
+  /**
+   * Base Station Longitude for CDMA networks.
+   *
+   * Possible ranges from -2592000 to 2592000.
+   * -2147483648 if the longitude is unknown.
+   *
+   * @see 3GPP2 C.S0005-A v6.0.
+   */
+  readonly attribute long cdmaBaseStationLongitude;
+
+  /**
+   * System ID for CDMA networks.
+   *
+   * Possible ranges from 0 to 32767.
+   * -1 if the system id is unknown.
+   */
+  readonly attribute long cdmaSystemId;
+
+  /**
+   * Network ID for CDMA networks.
+   *
+   * Possible ranges from 0 to 65535.
+   * -1 if the network id is unknown.
+   */
+  readonly attribute long cdmaNetworkId;
 };
 
 [scriptable, uuid(d1b35ad8-99aa-47cc-ab49-2e72b00e39df)]
 interface nsIDOMMozMobileCFInfo : nsISupports
 {
   /**
    * Call forwarding rule status.
    *
--- a/dom/network/tests/marionette/test_mobile_voice_state.js
+++ b/dom/network/tests/marionette/test_mobile_voice_state.js
@@ -40,32 +40,42 @@ function testConnectionInfo() {
   is(voice.state, "registered");
   is(voice.emergencyCallsOnly, false);
   is(voice.roaming, false);
 
   testCellLocation();
 }
 
 function testCellLocation() {
-  let voice = connection.voice;
+  let cell = connection.voice.cell;
 
   // Emulator always reports valid lac/cid value because its AT command parser
   // insists valid value for every complete response. See source file
   // hardare/ril/reference-ril/at_tok.c, function at_tok_nexthexint().
-  ok(voice.cell, "location available");
+  ok(cell, "location available");
 
-  // Initial LAC/CID. Android emulator initializes both value to -1.
-  is(voice.cell.gsmLocationAreaCode, 65535);
-  is(voice.cell.gsmCellId, 268435455);
+  // Initial LAC/CID. Android emulator initializes both value to 0xffff/0xffffffff.
+  is(cell.gsmLocationAreaCode, 65535);
+  is(cell.gsmCellId, 268435455);
+  is(cell.cdmaBaseStationId, -1);
+  is(cell.cdmaBaseStationLatitude, -2147483648);
+  is(cell.cdmaBaseStationLongitude, -2147483648);
+  is(cell.cdmaSystemId, -1);
+  is(cell.cdmaNetworkId, -1);
 
   connection.addEventListener("voicechange", function onvoicechange() {
     connection.removeEventListener("voicechange", onvoicechange);
 
-    is(voice.cell.gsmLocationAreaCode, 100);
-    is(voice.cell.gsmCellId, 100);
+    is(cell.gsmLocationAreaCode, 100);
+    is(cell.gsmCellId, 100);
+    is(cell.cdmaBaseStationId, -1);
+    is(cell.cdmaBaseStationLatitude, -2147483648);
+    is(cell.cdmaBaseStationLongitude, -2147483648);
+    is(cell.cdmaSystemId, -1);
+    is(cell.cdmaNetworkId, -1);
 
     testUnregistered();
   });
 
   setEmulatorGsmLocation(100, 100);
 }
 
 function testUnregistered() {
@@ -73,46 +83,49 @@ function testUnregistered() {
 
   connection.addEventListener("voicechange", function onvoicechange() {
     connection.removeEventListener("voicechange", onvoicechange);
 
     is(connection.voice.connected, false);
     is(connection.voice.state, "notSearching");
     is(connection.voice.emergencyCallsOnly, false);
     is(connection.voice.roaming, false);
+    is(connection.voice.cell, null);
 
     testSearching();
   });
 }
 
 function testSearching() {
   setEmulatorVoiceState("searching");
 
   connection.addEventListener("voicechange", function onvoicechange() {
     connection.removeEventListener("voicechange", onvoicechange);
 
     is(connection.voice.connected, false);
     is(connection.voice.state, "searching");
     is(connection.voice.emergencyCallsOnly, false);
     is(connection.voice.roaming, false);
+    is(connection.voice.cell, null);
 
     testDenied();
   });
 }
 
 function testDenied() {
   setEmulatorVoiceState("denied");
 
   connection.addEventListener("voicechange", function onvoicechange() {
     connection.removeEventListener("voicechange", onvoicechange);
 
     is(connection.voice.connected, false);
     is(connection.voice.state, "denied");
     is(connection.voice.emergencyCallsOnly, false);
     is(connection.voice.roaming, false);
+    is(connection.voice.cell, null);
 
     testRoaming();
   });
 }
 
 function testRoaming() {
   setEmulatorVoiceState("roaming");
 
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -476,18 +476,32 @@ GonkGPSGeolocationProvider::SetReference
       }
     }
     nsCOMPtr<nsIDOMMozMobileConnectionInfo> voice;
     rilCtx->GetVoice(getter_AddRefs(voice));
     if (voice) {
       nsCOMPtr<nsIDOMMozMobileCellInfo> cell;
       voice->GetCell(getter_AddRefs(cell));
       if (cell) {
-        cell->GetGsmLocationAreaCode(&location.u.cellID.lac);
-        cell->GetGsmCellId(&location.u.cellID.cid);
+        int32_t lac;
+        int64_t cid;
+
+        cell->GetGsmLocationAreaCode(&lac);
+        // The valid range of LAC is 0x0 to 0xffff which is defined in
+        // hardware/ril/include/telephony/ril.h
+        if (lac >= 0x0 && lac <= 0xffff) {
+          location.u.cellID.lac = lac;
+        }
+
+        cell->GetGsmCellId(&cid);
+        // The valid range of cell id is 0x0 to 0xffffffff which is defined in
+        // hardware/ril/include/telephony/ril.h
+        if (cid >= 0x0 && cid <= 0xffffffff) {
+          location.u.cellID.cid = cid;
+        }
       }
     }
     if (mAGpsRilInterface) {
       mAGpsRilInterface->set_ref_location(&location, sizeof(location));
     }
   }
 }
 
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -47,17 +47,17 @@ const RILCONTENTHELPER_CID =
   Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
 const ICCINFO_CID =
   Components.ID("{fab2c0f0-d73a-11e2-8b8b-0800200c9a66}");
 const MOBILECONNECTIONINFO_CID =
   Components.ID("{a35cfd39-2d93-4489-ac7d-396475dacb27}");
 const MOBILENETWORKINFO_CID =
   Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
 const MOBILECELLINFO_CID =
-  Components.ID("{5e809018-68c0-4c54-af0b-2a9b8f748c45}");
+  Components.ID("{ae724dd4-ccaf-4006-98f1-6ce66a092464}");
 const VOICEMAILSTATUS_CID=
   Components.ID("{5467f2eb-e214-43ea-9b89-67711241ec8e}");
 const MOBILECFINFO_CID=
   Components.ID("{a4756f16-e728-4d9f-8baa-8464f894888a}");
 const CELLBROADCASTMESSAGE_CID =
   Components.ID("{29474c96-3099-486f-bb4a-3c9a1da834e4}");
 const CELLBROADCASTETWSINFO_CID =
   Components.ID("{59f176ee-9dcd-4005-9d47-f6be0cd08e17}");
@@ -221,18 +221,23 @@ MobileCellInfo.prototype = {
     classID:          MOBILECELLINFO_CID,
     classDescription: "MobileCellInfo",
     flags:            Ci.nsIClassInfo.DOM_OBJECT,
     interfaces:       [Ci.nsIDOMMozMobileCellInfo]
   }),
 
   // nsIDOMMozMobileCellInfo
 
-  gsmLocationAreaCode: null,
-  gsmCellId: null
+  gsmLocationAreaCode: -1,
+  gsmCellId: -1,
+  cdmaBaseStationId: -1,
+  cdmaBaseStationLatitude: -2147483648,
+  cdmaBaseStationLongitude: -2147483648,
+  cdmaSystemId: -1,
+  cdmaNetworkId: -1
 };
 
 function VoicemailStatus() {}
 VoicemailStatus.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozVoicemailStatus]),
   classID:        VOICEMAILSTATUS_CID,
   classInfo:      XPCOMUtils.generateCI({
     classID:          VOICEMAILSTATUS_CID,
@@ -438,18 +443,17 @@ RILContentHelper.prototype = {
     if (!srcCell) {
       destInfo.cell = null;
     } else {
       let cell = destInfo.cell;
       if (!cell) {
         cell = destInfo.cell = new MobileCellInfo();
       }
 
-      cell.gsmLocationAreaCode = srcCell.gsmLocationAreaCode;
-      cell.gsmCellId = srcCell.gsmCellId;
+      this.updateInfo(srcCell, cell);
     }
 
     let srcNetwork = srcInfo.network;
     if (!srcNetwork) {
       destInfo.network= null;
       return;
     }
 
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1170,27 +1170,24 @@ RadioInterface.prototype = {
     voiceInfo.state = newInfo.state;
     voiceInfo.connected = newInfo.connected;
     voiceInfo.roaming = newInfo.roaming;
     voiceInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
     voiceInfo.type = newInfo.type;
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
-    if (newInfo.regState == RIL.NETWORK_CREG_STATE_UNKNOWN) {
+    if (newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_HOME &&
+        newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_ROAMING) {
+      voiceInfo.cell = null;
       voiceInfo.network = null;
       voiceInfo.signalStrength = null;
       voiceInfo.relSignalStrength = null;
-    }
-
-    let newCell = newInfo.cell;
-    if ((newCell.gsmLocationAreaCode < 0) || (newCell.gsmCellId < 0)) {
-      voiceInfo.cell = null;
     } else {
-      voiceInfo.cell = newCell;
+      voiceInfo.cell = newInfo.cell;
     }
 
     if (!newInfo.batch) {
       gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
                                                   this.clientId, voiceInfo);
     }
   },
 
@@ -1206,27 +1203,24 @@ RadioInterface.prototype = {
     dataInfo.connected = false;
     if (apnSetting) {
       dataInfo.connected = (this.getDataCallStateByType("default") ==
                             RIL.GECKO_NETWORK_STATE_CONNECTED);
     }
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
-    if (newInfo.regState == RIL.NETWORK_CREG_STATE_UNKNOWN) {
+    if (newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_HOME &&
+        newInfo.regState !== RIL.NETWORK_CREG_STATE_REGISTERED_ROAMING) {
+      dataInfo.cell = null;
       dataInfo.network = null;
       dataInfo.signalStrength = null;
       dataInfo.relSignalStrength = null;
-    }
-
-    let newCell = newInfo.cell;
-    if ((newCell.gsmLocationAreaCode < 0) || (newCell.gsmCellId < 0)) {
-      dataInfo.cell = null;
     } else {
-      dataInfo.cell = newCell;
+      dataInfo.cell = newInfo.cell;
     }
 
     if (!newInfo.batch) {
       gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
                                                   this.clientId, dataInfo);
     }
     this.updateRILNetworkInterface();
   },
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -818,21 +818,16 @@ let RIL = {
     this.iccInfo = {};
 
     /**
      * CDMA specific information. ex. CDMA Network ID, CDMA System ID... etc.
      */
     this.cdmaHome = null;
 
     /**
-     * CDMA Subscription information.
-     */
-    this.cdmaSubscription = {};
-
-    /**
      * Application identification for apps in ICC.
      */
     this.aid = null;
 
     /**
      * Application type for apps in ICC.
      */
     this.appType = null;
@@ -3398,33 +3393,43 @@ let RIL = {
 
   _processVoiceRegistrationState: function _processVoiceRegistrationState(state) {
     let rs = this.voiceRegistrationState;
     let stateChanged = this._processCREG(rs, state);
     if (stateChanged && rs.connected) {
       RIL.getSMSCAddress();
     }
 
+    let cell = rs.cell;
     if (this._isCdma) {
       // Some variables below are not used. Comment them instead of removing to
       // keep the information about state[x].
-
-      // let baseStationId = RIL.parseInt(state[4]);
-      let baseStationLatitude = RIL.parseInt(state[5]);
-      let baseStationLongitude = RIL.parseInt(state[6]);
-      if (!baseStationLatitude && !baseStationLongitude) {
-        baseStationLatitude = baseStationLongitude = null;
-      }
+      let cdmaBaseStationId = RIL.parseInt(state[4], -1);
+      let cdmaBaseStationLatitude = RIL.parseInt(state[5], -2147483648);
+      let cdmaBaseStationLongitude = RIL.parseInt(state[6], -2147483648);
       // let cssIndicator = RIL.parseInt(state[7]);
-      RIL.cdmaSubscription.systemId = RIL.parseInt(state[8]);
-      RIL.cdmaSubscription.networkId = RIL.parseInt(state[9]);
+      let cdmaSystemId = RIL.parseInt(state[8], -1);
+      let cdmaNetworkId = RIL.parseInt(state[9], -1);
       // let roamingIndicator = RIL.parseInt(state[10]);
       // let systemIsInPRL = RIL.parseInt(state[11]);
       // let defaultRoamingIndicator = RIL.parseInt(state[12]);
       // let reasonForDenial = RIL.parseInt(state[13]);
+
+      if (cell.cdmaBaseStationId !== cdmaBaseStationId ||
+          cell.cdmaBaseStationLatitude !== cdmaBaseStationLatitude ||
+          cell.cdmaBaseStationLongitude !== cdmaBaseStationLongitude ||
+          cell.cdmaSystemId !== cdmaSystemId ||
+          cell.cdmaNetworkId !== cdmaNetworkId) {
+        stateChanged = true;
+        cell.cdmaBaseStationId = cdmaBaseStationId;
+        cell.cdmaBaseStationLatitude = cdmaBaseStationLatitude;
+        cell.cdmaBaseStationLongitude = cdmaBaseStationLongitude;
+        cell.cdmaSystemId = cdmaSystemId;
+        cell.cdmaNetworkId = cdmaNetworkId;
+      }
     }
 
     if (stateChanged) {
       rs.rilMessageType = "voiceregistrationstatechange";
       this._sendNetworkInfoMessage(NETWORK_INFO_VOICE_REGISTRATION_STATE, rs);
     }
   },
 
@@ -4886,34 +4891,37 @@ RIL[REQUEST_SIGNAL_STRENGTH] = function 
     obj.lteRSSNR = Buf.readUint32();
     // Channel Quality Indicator, valid values are 0 to 15.
     obj.lteCQI = Buf.readUint32();
   }
 
   if (DEBUG) debug("Signal strength " + JSON.stringify(obj));
   obj.rilMessageType = "signalstrengthchange";
   this.sendChromeMessage(obj);
-
-  if (this.cachedDialRequest && obj.gsmDBM && obj.gsmRelative) {
-    // Radio is ready for making the cached emergency call.
-    this.cachedDialRequest.callback();
-    this.cachedDialRequest = null;
-  }
 };
 RIL[REQUEST_VOICE_REGISTRATION_STATE] = function REQUEST_VOICE_REGISTRATION_STATE(length, options) {
   this._receivedNetworkInfo(NETWORK_INFO_VOICE_REGISTRATION_STATE);
 
   if (options.rilRequestError) {
     return;
   }
 
   let state = Buf.readStringList();
   if (DEBUG) debug("voice registration state: " + state);
 
   this._processVoiceRegistrationState(state);
+
+  if (this.cachedDialRequest &&
+       (this.voiceRegistrationState.emergencyCallsOnly ||
+        this.voiceRegistrationState.connected) &&
+      this.voiceRegistrationState.radioTech != NETWORK_CREG_TECH_UNKNOWN) {
+    // Radio is ready for making the cached emergency call.
+    this.cachedDialRequest.callback();
+    this.cachedDialRequest = null;
+  }
 };
 RIL[REQUEST_DATA_REGISTRATION_STATE] = function REQUEST_DATA_REGISTRATION_STATE(length, options) {
   this._receivedNetworkInfo(NETWORK_INFO_DATA_REGISTRATION_STATE);
 
   if (options.rilRequestError) {
     return;
   }
 
@@ -11645,18 +11653,19 @@ let ICCUtilsHelper = {
     let origIsDisplaySPNRequired = iccInfo.isDisplaySpnRequired;
 
     if (displayCondition === undefined) {
       iccInfo.isDisplayNetworkNameRequired = true;
       iccInfo.isDisplaySpnRequired = false;
     } else if (RIL._isCdma) {
       // CDMA family display rule.
       let cdmaHome = RIL.cdmaHome;
-      let sid = RIL.cdmaSubscription.systemId;
-      let nid = RIL.cdmaSubscription.networkId;
+      let cell = RIL.voiceRegistrationState.cell;
+      let sid = cell && cell.cdmaSystemId;
+      let nid = cell && cell.cdmaNetworkId;
 
       iccInfo.isDisplayNetworkNameRequired = false;
 
       // If display condition is 0x0, we don't even need to check network id
       // or system id.
       if (displayCondition === 0x0) {
         iccInfo.isDisplaySpnRequired = false;
       } else {
--- a/dom/system/gonk/tests/test_ril_worker_ruim.js
+++ b/dom/system/gonk/tests/test_ril_worker_ruim.js
@@ -199,19 +199,19 @@ add_test(function test_cdma_spn_display_
                                 currentSystemId, currentNetworkId,
                                 expectUpdateDisplayCondition,
                                 expectIsDisplaySPNRequired) {
     RIL.iccInfoPrivate.spnDisplayCondition = ruimDisplayCondition;
     RIL.cdmaHome = {
       systemId: homeSystemIds,
       networkId: homeNetworkIds
     };
-    RIL.cdmaSubscription = {
-      systemId: currentSystemId,
-      networkId: currentNetworkId
+    RIL.voiceRegistrationState.cell = {
+      cdmaSystemId: currentSystemId,
+      cdmaNetworkId: currentNetworkId
     };
 
     do_check_eq(ICCUtilsHelper.updateDisplayCondition(), expectUpdateDisplayCondition);
     do_check_eq(RIL.iccInfo.isDisplayNetworkNameRequired, false);
     do_check_eq(RIL.iccInfo.isDisplaySpnRequired, expectIsDisplaySPNRequired);
   };
 
   // SPN is not required when ruimDisplayCondition is false.
new file mode 100644
--- /dev/null
+++ b/dom/wappush/src/gonk/CpPduHelper.jsm
@@ -0,0 +1,276 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+let WSP = {};
+Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
+let WBXML = {};
+Cu.import("resource://gre/modules/WbxmlPduHelper.jsm", WBXML);
+
+// set to true to see debug messages
+let DEBUG = WBXML.DEBUG_ALL | false;
+
+/**
+ * Public identifier for CP
+ *
+ * @see http://technical.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.aspx
+ */
+const PUBLIC_IDENTIFIER_CP = "-//WAPFORUM//DTD PROV 1.0//EN";
+
+this.PduHelper = {
+
+  /**
+    * @param data
+    *        A wrapped object containing raw PDU data.
+    * @param contentType
+    *        Content type of incoming CP message, should be "text/vnd.wap.connectivity-xml"
+    *        or "application/vnd.wap.connectivity-wbxml".
+    *
+    * @return A message object containing attribute content and contentType.
+    *         |content| will contain string of decoded CP message if successfully
+    *         decoded, or raw data if failed.
+    *         |contentType| will be string representing corresponding type of
+    *         content.
+    */
+  parse: function parse_cp(data, contentType) {
+    // We only need content and contentType
+    let msg = {
+      contentType: contentType
+    };
+
+    /**
+     * Message is compressed by WBXML, decode into string.
+     *
+     * @see WAP-192-WBXML-20010725-A
+     */
+    if (contentType === "application/vnd.wap.connectivity-wbxml") {
+      let appToken = {
+        publicId: PUBLIC_IDENTIFIER_CP,
+        tagTokenList: CP_TAG_FIELDS,
+        attrTokenList: CP_ATTRIBUTE_FIELDS,
+        valueTokenList: CP_VALUE_FIELDS,
+        globalTokenOverride: null
+      }
+
+      try {
+        let parseResult = WBXML.PduHelper.parse(data, appToken, msg);
+        msg.content = parseResult.content;
+        msg.contentType = "text/vnd.wap.connectivity-xml";
+      } catch (e) {
+        // Provide raw data if we failed to parse.
+        msg.content = data.array;
+      }
+
+      return msg;
+    }
+
+    /**
+     * Message is plain text, transform raw to string.
+     */
+    try {
+      let stringData = WSP.Octet.decodeMultiple(data, data.array.length);
+      msg.content = WSP.PduHelper.decodeStringContent(stringData, "UTF-8");
+    } catch (e) {
+      // Provide raw data if we failed to parse.
+      msg.content = data.array;
+    }
+    return msg;
+
+  }
+};
+
+/**
+  * Tag tokens
+  *
+  * @see WAP-183-ProvCont-20010724-A, clause 8.1
+  */
+const CP_TAG_FIELDS = (function () {
+  let names = {};
+  function add(name, number) {
+    let entry = {
+      name: name,
+      number: number,
+    };
+    names[number] = entry;
+  }
+
+  add("wap-provisioningdoc", 0x05);
+  add("characteristic",      0x06);
+  add("parm",                0x07);
+
+  return names;
+})();
+
+/**
+  * Attribute Tokens
+  *
+  * @see WAP-183-ProvCont-20010724-A, clause 8.2
+  */
+const CP_ATTRIBUTE_FIELDS = (function () {
+  let names = {};
+  function add(name, value, number) {
+    let entry = {
+      name: name,
+      value: value,
+      number: number,
+    };
+    names[number] = entry;
+  }
+
+  add("name",     "",                                 0x05);
+  add("value",    "",                                 0x06);
+  add("name",     "NAME",                             0x07);
+  add("name",     "NAP-ADDRESS",                      0x08);
+  add("name",     "NAP-ADDRTYPE",                     0x09);
+  add("name",     "CALLTYPE",                         0x0A);
+  add("name",     "VALIDUNTIL",                       0x0B);
+  add("name",     "AUTHTYPE",                         0x0C);
+  add("name",     "AUTHNAME",                         0x0D);
+  add("name",     "AUTHSECRET",                       0x0E);
+  add("name",     "LINGER",                           0x0F);
+  add("name",     "BEARER",                           0x10);
+  add("name",     "NAPID",                            0x11);
+  add("name",     "COUNTRY",                          0x12);
+  add("name",     "NETWORK",                          0x13);
+  add("name",     "INTERNET",                         0x14);
+  add("name",     "PROXY-ID",                         0x15);
+  add("name",     "PROXY-PROVIDER-ID",                0x16);
+  add("name",     "DOMAIN",                           0x17);
+  add("name",     "PROVURL",                          0x18);
+  add("name",     "PXAUTH-TYPE",                      0x19);
+  add("name",     "PXAUTH-ID",                        0x1A);
+  add("name",     "PXAUTH-PW",                        0x1B);
+  add("name",     "STARTPAGE",                        0x1C);
+  add("name",     "BASAUTH-ID",                       0x1D);
+  add("name",     "BASAUTH-PW",                       0x1E);
+  add("name",     "PUSHENABLED",                      0x1F);
+  add("name",     "PXADDR",                           0x20);
+  add("name",     "PXADDRTYPE",                       0x21);
+  add("name",     "TO-NAPID",                         0x22);
+  add("name",     "PORTNBR",                          0x23);
+  add("name",     "SERVICE",                          0x24);
+  add("name",     "LINKSPEED",                        0x25);
+  add("name",     "DNLINKSPEED",                      0x26);
+  add("name",     "LOCAL-ADDR",                       0x27);
+  add("name",     "LOCAL-ADDRTYPE",                   0x28);
+  add("name",     "CONTEXT-ALLOW",                    0x29);
+  add("name",     "TRUST",                            0x2A);
+  add("name",     "MASTER",                           0x2B);
+  add("name",     "SID",                              0x2C);
+  add("name",     "SOC",                              0x2D);
+  add("name",     "WSP-VERSION",                      0x2E);
+  add("name",     "PHYSICAL-PROXY-ID",                0x2F);
+  add("name",     "CLIENT-ID",                        0x30);
+  add("name",     "DELIVERY-ERR-PDU",                 0x31);
+  add("name",     "DELIVERY-ORDER",                   0x32);
+  add("name",     "TRAFFIC-CLASS",                    0x33);
+  add("name",     "MAX-SDU-SIZE",                     0x34);
+  add("name",     "MAX-BITRATE-UPLINK",               0x35);
+  add("name",     "MAX-BITRATE-DNLINK",               0x36);
+  add("name",     "RESIDUAL-BER",                     0x37);
+  add("name",     "SDU-ERROR-RATIO",                  0x38);
+  add("name",     "TRAFFIC-HANDL-PRIO",               0x39);
+  add("name",     "TRANSFER-DELAY",                   0x3A);
+  add("name",     "GUARANTEED-BITRATE-UPLINK",        0x3B);
+  add("name",     "GUARANTEED-BITRATE-DNLINK",        0x3C);
+  add("version",  "",                                 0x45);
+  add("version",  "1.0",                              0x46);
+  add("type",     "",                                 0x50);
+  add("type",     "PXLOGICAL",                        0x51);
+  add("type",     "PXPHYSICAL",                       0x52);
+  add("type",     "PORT",                             0x53);
+  add("type",     "VALIDITY",                         0x54);
+  add("type",     "NAPDEF",                           0x55);
+  add("type",     "BOOTSTRAP",                        0x56);
+/*
+ *  Mark out VENDORCONFIG so if it is contained in message, parse
+ *  will failed and raw data is returned.
+ */
+//  add("type",     "VENDORCONFIG",                     0x57);
+  add("type",     "CLIENTIDENTITY",                   0x58);
+  add("type",     "PXAUTHINFO",                       0x59);
+  add("type",     "NAPAUTHINFO",                      0x5A);
+
+  return names;
+})();
+
+const CP_VALUE_FIELDS = (function () {
+  let names = {};
+  function add(value, number) {
+    let entry = {
+      value: value,
+      number: number,
+    };
+    names[number] = entry;
+  }
+
+  add("IPV4",                             0x85);
+  add("IPV6",                             0x86);
+  add("E164",                             0x87);
+  add("ALPHA",                            0x88);
+  add("APN",                              0x89);
+  add("SCODE",                            0x8A);
+  add("TETRA-ITSI",                       0x8B);
+  add("MAN",                              0x8C);
+  add("ANALOG-MODEM",                     0x90);
+  add("V.120",                            0x91);
+  add("V.110",                            0x92);
+  add("X.31",                             0x93);
+  add("BIT-TRANSPARENT",                  0x94);
+  add("DIRECT-ASYNCHRONOUS-DATA-SERVICE", 0x95);
+  add("PAP",                              0x9A);
+  add("CHAP",                             0x9B);
+  add("HTTP-BASIC",                       0x9C);
+  add("HTTP-DIGEST",                      0x9D);
+  add("WTLS-SS",                          0x9E);
+  add("GSM-USSD",                         0xA2);
+  add("GSM-SMS",                          0xA3);
+  add("ANSI-136-GUTS",                    0xA4);
+  add("IS-95-CDMA-SMS",                   0xA5);
+  add("IS-95-CDMA-CSD",                   0xA6);
+  add("IS-95-CDMA-PAC",                   0xA7);
+  add("ANSI-136-CSD",                     0xA8);
+  add("ANSI-136-GPRS",                    0xA9);
+  add("GSM-CSD",                          0xAA);
+  add("GSM-GPRS",                         0xAB);
+  add("AMPS-CDPD",                        0xAC);
+  add("PDC-CSD",                          0xAD);
+  add("PDC-PACKET",                       0xAE);
+  add("IDEN-SMS",                         0xAF);
+  add("IDEN-CSD",                         0xB0);
+  add("IDEN-PACKET",                      0xB1);
+  add("FLEX/REFLEX",                      0xB2);
+  add("PHS-SMS",                          0xB3);
+  add("PHS-CSD",                          0xB4);
+  add("TETRA-SDS",                        0xB5);
+  add("TETRA-PACKET",                     0xB6);
+  add("ANSI-136-GHOST",                   0xB7);
+  add("MOBITEX-MPAK",                     0xB8);
+  add("AUTOBOUDING",                      0xC5);
+  add("CL-WSP",                           0xCA);
+  add("CO-WSP",                           0xCB);
+  add("CL-SEC-WSP",                       0xCC);
+  add("CO-SEC-WSP",                       0xCD);
+  add("CL-SEC-WTA",                       0xCE);
+  add("CO-SEC-WTA",                       0xCF);
+
+  return names;
+})();
+
+let debug;
+if (DEBUG) {
+  debug = function (s) {
+    dump("-$- CpPduHelper: " + s + "\n");
+  };
+} else {
+  debug = function (s) {};
+}
+
+this.EXPORTED_SYMBOLS = [
+  // Parser
+  "PduHelper",
+];
--- a/dom/wappush/src/gonk/SiPduHelper.jsm
+++ b/dom/wappush/src/gonk/SiPduHelper.jsm
@@ -60,92 +60,91 @@ this.OpaqueAsDate = {
   },
 };
 
 this.PduHelper = {
 
   /**
    * @param data
    *        A wrapped object containing raw PDU data.
-   * @param contentType [optional]
+   * @param contentType
    *        Content type of incoming SI message, should be "text/vnd.wap.si" or
    *        "application/vnd.wap.sic".
-   *        Default value is "application/vnd.wap.sic".
    *
-   * @return A SI message object or null in case of errors found.
+   * @return A message object containing attribute content and contentType.
+   *         |content| will contain string of decoded SI message if successfully
+   *         decoded, or raw data if failed.
+   *         |contentType| will be string representing corresponding type of
+   *         content.
    */
   parse: function parse_si(data, contentType) {
-    let msg = {};
+    // We only need content and contentType
+    let msg = {
+      contentType: contentType
+    };
 
     /**
      * Message is compressed by WBXML, decode into string.
      *
      * @see WAP-192-WBXML-20010725-A
      */
-    if (!contentType || contentType === "application/vnd.wap.sic") {
+    if (contentType === "application/vnd.wap.sic") {
       let globalTokenOverride = {};
       globalTokenOverride[0xC3] = {
         number: 0xC3,
         coder: OpaqueAsDate
       };
 
       let appToken = {
         publicId: PUBLIC_IDENTIFIER_SI,
-        tagToken: SI_TAG_FIELDS,
-        attrToken: SI_ATTRIBUTE_FIELDS,
+        tagTokenList: SI_TAG_FIELDS,
+        attrTokenList: SI_ATTRIBUTE_FIELDS,
+        valueTokenList: SI_VALUE_FIELDS,
         globalTokenOverride: globalTokenOverride
       }
 
-      WBXML.PduHelper.parse(data, appToken, msg);
-
-      msg.contentType = "text/vnd.wap.si";
+      try {
+        let parseResult = WBXML.PduHelper.parse(data, appToken);
+        msg.content = parseResult.content;
+        msg.contentType = "text/vnd.wap.si";
+      } catch (e) {
+        // Provide raw data if we failed to parse.
+        msg.content = data.array;
+      }
       return msg;
     }
 
     /**
      * Message is plain text, transform raw to string.
      */
-    if (contentType === "text/vnd.wap.si") {
+    try {
       let stringData = WSP.Octet.decodeMultiple(data, data.array.length);
-      msg.publicId = PUBLIC_IDENTIFIER_SI;
       msg.content = WSP.PduHelper.decodeStringContent(stringData, "UTF-8");
-      msg.contentType = "text/vnd.wap.si";
-      return msg;
+    } catch (e) {
+      // Provide raw data if we failed to parse.
+      msg.content = data.array;
     }
-
-    return null;
-  },
+    return msg;
 
-  /**
-   * @param multiStream
-   *        An exsiting nsIMultiplexInputStream.
-   * @param msg
-   *        A SI message object.
-   *
-   * @return An instance of nsIMultiplexInputStream or null in case of errors.
-   */
-  compose: function compose_si(multiStream, msg) {
-    // Composing SI message is not supported
-    return null;
-  },
+  }
 };
 
 /**
  * Tag tokens
  *
  * @see WAP-167-SERVICEIND-20010731-A, clause 8.3.1
  */
 const SI_TAG_FIELDS = (function () {
   let names = {};
   function add(name, number) {
     let entry = {
       name: name,
       number: number,
     };
-    names[name] = names[number] = entry;
+    names[number] = entry;
   }
 
   add("si",           0x05);
   add("indication",   0x06);
   add("info",         0x07);
   add("item",         0x08);
 
   return names;
@@ -159,37 +158,51 @@ const SI_TAG_FIELDS = (function () {
 const SI_ATTRIBUTE_FIELDS = (function () {
   let names = {};
   function add(name, value, number) {
     let entry = {
       name: name,
       value: value,
       number: number,
     };
-    names[name] = names[number] = entry;
+    names[number] = entry;
   }
 
   add("action",       "signal-none",    0x05);
   add("action",       "signal-low",     0x06);
   add("action",       "signal-medium",  0x07);
   add("action",       "signal-high",    0x08);
   add("action",       "delete",         0x09);
   add("created",      "",               0x0A);
   add("href",         "",               0x0B);
   add("href",         "http://",        0x0C);
   add("href",         "http://www.",    0x0D);
   add("href",         "https://",       0x0E);
   add("href",         "https://www.",   0x0F);
   add("si-expires",   "",               0x10);
   add("si-id",        "",               0x11);
   add("class",        "",               0x12);
-  add("",             ".com/",          0x85);
-  add("",             ".edu/",          0x86);
-  add("",             ".net/",          0x87);
-  add("",             ".org/",          0x88);
+
+  return names;
+})();
+
+const SI_VALUE_FIELDS = (function () {
+  let names = {};
+  function add(value, number) {
+    let entry = {
+      value: value,
+      number: number,
+    };
+    names[number] = entry;
+  }
+
+  add(".com/",          0x85);
+  add(".edu/",          0x86);
+  add(".net/",          0x87);
+  add(".org/",          0x88);
 
   return names;
 })();
 
 this.EXPORTED_SYMBOLS = [
   // Parser
   "PduHelper",
 ];
--- a/dom/wappush/src/gonk/SlPduHelper.jsm
+++ b/dom/wappush/src/gonk/SlPduHelper.jsm
@@ -21,86 +21,86 @@ let DEBUG = WBXML.DEBUG_ALL | false;
  */
 const PUBLIC_IDENTIFIER_SL    = "-//WAPFORUM//DTD SL 1.0//EN";
 
 this.PduHelper = {
 
   /**
    * @param data
    *        A wrapped object containing raw PDU data.
-   * @param contentType [optional]
+   * @param contentType
    *        Content type of incoming SL message, should be "text/vnd.wap.sl" or
    *        "application/vnd.wap.slc".
-   *        Default value is "application/vnd.wap.slc".
    *
-   * @return A SL message object or null in case of errors found.
+   * @return A message object containing attribute content and contentType.
+   *         |content| will contain string of decoded SL message if successfully
+   *         decoded, or raw data if failed.
+   *         |contentType| will be string representing corresponding type of
+   *         content.
    */
   parse: function parse_sl(data, contentType) {
-    let msg = {};
+    // We only need content and contentType
+    let msg = {
+      contentType: contentType
+    };
 
     /**
      * Message is compressed by WBXML, decode into string.
      *
      * @see WAP-192-WBXML-20010725-A
      */
-    if (!contentType || contentType === "application/vnd.wap.slc") {
+    if (contentType === "application/vnd.wap.slc") {
       let appToken = {
         publicId: PUBLIC_IDENTIFIER_SL,
-        tagToken: SL_TAG_FIELDS,
-        attrToken: SL_ATTRIBUTE_FIELDS,
+        tagTokenList: SL_TAG_FIELDS,
+        attrTokenList: SL_ATTRIBUTE_FIELDS,
+        valueTokenList: SL_VALUE_FIELDS,
         globalTokenOverride: null
       }
 
-      WBXML.PduHelper.parse(data, appToken, msg);
+      try {
+        let parseResult = WBXML.PduHelper.parse(data, appToken);
+        msg.content = parseResult.content;
+        msg.contentType = "text/vnd.wap.sl";
+      } catch (e) {
+        // Provide raw data if we failed to parse.
+        msg.content = data.array;
+      }
 
-      msg.contentType = "text/vnd.wap.sl";
       return msg;
     }
 
     /**
      * Message is plain text, transform raw to string.
      */
-    if (contentType === "text/vnd.wap.sl") {
+    try {
       let stringData = WSP.Octet.decodeMultiple(data, data.array.length);
-      msg.publicId = PUBLIC_IDENTIFIER_SL;
       msg.content = WSP.PduHelper.decodeStringContent(stringData, "UTF-8");
-      msg.contentType = "text/vnd.wap.sl";
-      return msg;
+    } catch (e) {
+      // Provide raw data if we failed to parse.
+      msg.content = data.array;
     }
-
-    return null;
-  },
+    return msg;
 
-  /**
-   * @param multiStream
-   *        An exsiting nsIMultiplexInputStream.
-   * @param msg
-   *        A SL message object.
-   *
-   * @return An instance of nsIMultiplexInputStream or null in case of errors.
-   */
-  compose: function compose_sl(multiStream, msg) {
-    // Composing SL message is not supported
-    return null;
-  },
+  }
 };
 
 /**
  * Tag tokens
  *
  * @see WAP-168-SERVICELOAD-20010731-A, clause 9.3.1
  */
 const SL_TAG_FIELDS = (function () {
   let names = {};
   function add(name, number) {
     let entry = {
       name: name,
       number: number,
     };
-    names[name] = names[number] = entry;
+    names[number] = entry;
   }
 
   add("sl",           0x05);
 
   return names;
 })();
 
 /**
@@ -111,31 +111,45 @@ const SL_TAG_FIELDS = (function () {
 const SL_ATTRIBUTE_FIELDS = (function () {
   let names = {};
   function add(name, value, number) {
     let entry = {
       name: name,
       value: value,
       number: number,
     };
-    names[name] = names[number] = entry;
+    names[number] = entry;
   }
 
   add("action",       "execute-low",    0x05);
   add("action",       "execute-high",   0x06);
   add("action",       "cache",          0x07);
   add("href",         "",               0x08);
   add("href",         "http://",        0x09);
   add("href",         "http://www.",    0x0A);
   add("href",         "https://",       0x0B);
   add("href",         "https://www.",   0x0C);
-  add("",             ".com/",          0x85);
-  add("",             ".edu/",          0x86);
-  add("",             ".net/",          0x87);
-  add("",             ".org/",          0x88);
+
+  return names;
+})();
+
+const SL_VALUE_FIELDS = (function () {
+  let names = {};
+  function add(value, number) {
+    let entry = {
+      value: value,
+      number: number,
+    };
+    names[number] = entry;
+  }
+
+  add(".com/",          0x85);
+  add(".edu/",          0x86);
+  add(".net/",          0x87);
+  add(".org/",          0x88);
 
   return names;
 })();
 
 this.EXPORTED_SYMBOLS = [
   // Parser
   "PduHelper",
 ];
--- a/dom/wappush/src/gonk/WapPushManager.js
+++ b/dom/wappush/src/gonk/WapPushManager.js
@@ -23,16 +23,22 @@ XPCOMUtils.defineLazyGetter(this, "SI", 
 });
 
 XPCOMUtils.defineLazyGetter(this, "SL", function () {
   let SL = {};
   Cu.import("resource://gre/modules/SlPduHelper.jsm", SL);
   return SL;
 });
 
+XPCOMUtils.defineLazyGetter(this, "CP", function () {
+  let CP = {};
+  Cu.import("resource://gre/modules/CpPduHelper.jsm", CP);
+  return CP;
+});
+
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
 
 /**
  * Helpers for WAP PDU processing.
  */
 this.WapPushManager = {
@@ -87,19 +93,20 @@ this.WapPushManager = {
     let msg;
 
     if (contentType === "text/vnd.wap.si" ||
         contentType === "application/vnd.wap.sic") {
       msg = SI.PduHelper.parse(data, contentType);
     } else if (contentType === "text/vnd.wap.sl" ||
                contentType === "application/vnd.wap.slc") {
       msg = SL.PduHelper.parse(data, contentType);
+    } else if (contentType === "text/vnd.wap.connectivity-xml" ||
+               contentType === "application/vnd.wap.connectivity-wbxml") {
+      msg = CP.PduHelper.parse(data, contentType);
     } else {
-      // TODO: Bug 869291 - Support Receiving WAP-Push-CP
-
       // Unsupported type, provide raw data.
       msg = {
         contentType: contentType,
         content: data.array
       };
     }
 
     gSystemMessenger.broadcastMessage("wappush-received", {
--- a/dom/wappush/src/gonk/WbxmlPduHelper.jsm
+++ b/dom/wappush/src/gonk/WbxmlPduHelper.jsm
@@ -120,105 +120,115 @@ this.WbxmlOpaque = {
 
 this.PduHelper = {
 
   /**
    * Parse WBXML encoded message into plain text.
    *
    * @param data
    *        A wrapped object containing raw PDU data.
-   * @param msg
-   *        Target object for decoding.
+   * @param decodeInfo
+   *        Information for decoding, now requires charset and string table.
    * @param appToken
    *        Application-specific token difinition.
    *
    * @return Decoded WBXML message string.
    */
-  parseWbxml: function parseWbxml_wbxml(data, msg, appToken) {
+  parseWbxml: function parseWbxml_wbxml(data, decodeInfo, appToken) {
 
     // Parse token definition to my structure.
-    let tagTokenList = appToken.tagToken;
-    let attrTokenList = appToken.attrToken;
-    let globalTokenOverrideList = appToken.globalTokenOverride || {};
+    let tagTokenList = appToken.tagTokenList;
+    let attrTokenList = appToken.attrTokenList;
+    let valueTokenList = appToken.valueTokenList;
+
+    decodeInfo.tagStack = [];    // tag decode stack
 
-    let decodeInfo = {
-      tagStack: [],                                                 // tag decode stack
-      charset: WSP.WSP_WELL_KNOWN_CHARSETS[msg.charset].converter,  // document character set
-      stringTable: msg.stringTable                                  // document string table
-    };
+    // Merge global tag tokens into single list, so we don't have
+    // to search two lists every time.
+    let globalTagTokenList = Object.create(WBXML_GLOBAL_TOKENS);
+    if (appToken.globalTokenOverride) {
+      let globalTokenOverrideList = appToken.globalTokenOverride;
+      for (let token in globalTokenOverrideList) {
+        globalTagTokenList[token] = globalTokenOverrideList[token];
+      }
+    }
 
-    msg.content = "";
+    let content = "";
     while (data.offset < data.array.length) {
       // Decode content, might be a new tag token, an end of tag token, or an
       // inline string.
 
       let tagToken = WSP.Octet.decode(data);
       let tagTokenValue = tagToken & TAG_TOKEN_VALUE_MASK;
 
       // Try global tag first, tagToken of string table is 0x83, and will be 0x03
       // in tagTokenValue, which is collision with inline string.
       // So tagToken need to be searched before tagTokenValue.
-      let tagInfo = globalTokenOverrideList[tagToken] ||
-                    globalTokenOverrideList[tagTokenValue] ||
-                    WBXML_GLOBAL_TOKENS[tagToken] ||
-                    WBXML_GLOBAL_TOKENS[tagTokenValue];
+      let tagInfo = globalTagTokenList[tagToken] ||
+                    globalTagTokenList[tagTokenValue];
       if (tagInfo) {
-        msg.content += tagInfo.coder.decode(data, decodeInfo);
+        content += tagInfo.coder.decode(data, decodeInfo);
         continue;
       }
 
       // Check if application tag token is valid
       tagInfo = tagTokenList[tagTokenValue];
-      if (!tagInfo)
-        continue;
+      if (!tagInfo) {
+        throw new Error("Unsupported WBXML token: " + tagTokenValue + ".");
+      }
 
-      msg.content += "<" + tagInfo.name;
+      content += "<" + tagInfo.name;
 
       if (tagToken & TAG_TOKEN_ATTR_MASK) {
         // Decode attributes, might be a new attribute token, a value token,
         // or an inline string
 
         let attrSeperator = "";
         while (data.offset < data.array.length) {
           let attrToken = WSP.Octet.decode(data);
           if (attrToken === TAG_END_TOKEN) {
             break;
           }
 
-          let attrInfo = globalTokenOverrideList[attrToken] ||
-                         WBXML_GLOBAL_TOKENS[attrToken];
+          let attrInfo = globalTagTokenList[attrToken];
           if (attrInfo) {
-            msg.content += attrInfo.coder.decode(data, decodeInfo);
+            content += attrInfo.coder.decode(data, decodeInfo);
             continue;
           }
 
           // Check if attribute token is valid
           attrInfo = attrTokenList[attrToken];
-          if (!attrInfo)
+          if (attrInfo) {
+            content += attrSeperator + " " + attrInfo.name + "=\"" + attrInfo.value;
+            attrSeperator = "\"";
             continue;
+          }
 
-          if (attrInfo.name !== "") {
-            msg.content += attrSeperator + " " + attrInfo.name + "=\"" + attrInfo.value;
-            attrSeperator = "\"";
-          } else {
-            msg.content += attrInfo.value;
+          attrInfo = valueTokenList[attrToken];
+          if (attrInfo) {
+            content += attrInfo.value;
+            continue;
           }
+
+          throw new Error("Unsupported WBXML token: " + attrToken + ".");
         }
 
-        msg.content += attrSeperator;
+        content += attrSeperator;
       }
 
       if (tagToken & TAG_TOKEN_CONTENT_MASK) {
-        msg.content += ">";
+        content += ">";
         decodeInfo.tagStack.push(tagInfo);
         continue;
       }
 
-      msg.content += "/>";
+      content += "/>";
     }
+
+    return content;
   },
 
   /**
    * @param data
    *        A wrapped object containing raw PDU data.
    * @param appToken
    *        contains application-specific token info, including
    *        {
@@ -243,25 +253,21 @@ this.PduHelper = {
    *                                  Object[GLOBAL_TOKEN_NUMBER] =
    *                                  {
    *                                    decode: function(data),
    *                                    encode: function(data)
    *                                  }
    *                                  decode() returns decoded text, encode() returns
    *                                  encoded raw data.
    *        }
-   * @param msg [optional]
-   *        Optional target object for decoding.
    *
    * @return A WBXML message object or null in case of errors found.
    */
-  parse: function parse_wbxml(data, appToken, msg) {
-    if (!msg) {
-      msg = {};
-    }
+  parse: function parse_wbxml(data, appToken) {
+    let msg = {};
 
     /**
      * Read WBXML header.
      *
      * @see WAP-192-WBXML-20010725-A, Clause 5.3
      */
     let headerRaw = {};
     headerRaw.version = WSP.Octet.decode(data);
@@ -284,38 +290,29 @@ this.PduHelper = {
 
     if (headerRaw.publicId !== 0) {
       msg.publicId = WBXML_PUBLIC_ID[headerRaw.publicId];
     } else {
       msg.publicId = readStringTable(headerRaw.publicIdStr, msg.stringTable,
                                      WSP.WSP_WELL_KNOWN_CHARSETS[msg.charset].converter);
     }
     if (msg.publicId != appToken.publicId) {
-      return null;
+      throw new Error("Public ID does not match.");
     }
 
     msg.version = ((headerRaw.version >> 4) + 1) + "." + (headerRaw.version & 0x0F);
 
-    this.parseWbxml(data, msg, appToken);
+    let decodeInfo = {
+      charset: WSP.WSP_WELL_KNOWN_CHARSETS[msg.charset].converter,  // document character set
+      stringTable: msg.stringTable                                  // document string table
+    };
+    msg.content = this.parseWbxml(data, decodeInfo, appToken);
 
     return msg;
-  },
-
-  /**
-   * @param multiStream
-   *        An exsiting nsIMultiplexInputStream.
-   * @param msg
-   *        A SI message object.
-   *
-   * @return An instance of nsIMultiplexInputStream or null in case of errors.
-   */
-  compose: function compose_wbxml(multiStream, msg) {
-    // Composing WBXML message is not supported
-    return null;
-  },
+  }
 };
 
 /**
  * Global Tokens
  *
  * @see WAP-192-WBXML-20010725-A, clause 7.1
  */
 const WBXML_GLOBAL_TOKENS = (function () {
--- a/dom/wappush/src/moz.build
+++ b/dom/wappush/src/moz.build
@@ -1,12 +1,13 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_B2G_RIL']:
     EXTRA_JS_MODULES = [
+        'gonk/CpPduHelper.jsm',
         'gonk/SiPduHelper.jsm',
         'gonk/SlPduHelper.jsm',
         'gonk/WapPushManager.js',
-        'gonk/WbxmlPduHelper.jsm',
+        'gonk/WbxmlPduHelper.jsm'
     ]
new file mode 100644
--- /dev/null
+++ b/dom/wappush/tests/test_cp_pdu_helper.js
@@ -0,0 +1,359 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let CP = {};
+subscriptLoader.loadSubScript("resource://gre/modules/CpPduHelper.jsm", CP);
+CP.debug = do_print;
+
+function run_test() {
+  run_next_test();
+}
+
+function test_parser(rawDataArray, contentType, expectResult) {
+  let data = { array: rawDataArray, offset: 0 };
+  let msg = CP.PduHelper.parse(data, contentType);
+  do_check_eq(msg.contentType, expectResult.contentType);
+  do_check_eq(msg.content, expectResult.content);
+}
+
+/*
+ * Test data from OMA-TS-WAP_ProvCont-V1_1-2009 0421-C.pdf, clause 6.1
+ */
+let text_data_array = new Uint8Array([
+  0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20, 0x76, 0x65,
+  0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x31,
+  0x2E, 0x30, 0x22, 0x3F, 0x3E, 0x3C, 0x21, 0x44,
+  0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x77,
+  0x61, 0x70, 0x2D, 0x70, 0x72, 0x6F, 0x76, 0x69,
+  0x73, 0x69, 0x6F, 0x6E, 0x69, 0x6E, 0x67, 0x64,
+  0x6F, 0x63, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49,
+  0x43, 0x20, 0x22, 0x2D, 0x2F, 0x2F, 0x57, 0x41,
+  0x50, 0x46, 0x4F, 0x52, 0x55, 0x4D, 0x2F, 0x2F,
+  0x44, 0x54, 0x44, 0x20, 0x50, 0x52, 0x4F, 0x56,
+  0x20, 0x31, 0x2E, 0x30, 0x2F, 0x2F, 0x45, 0x4E,
+  0x22, 0x20, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A,
+  0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x61,
+  0x70, 0x66, 0x6F, 0x72, 0x75, 0x6D, 0x2E, 0x6F,
+  0x72, 0x67, 0x2F, 0x44, 0x54, 0x44, 0x2F, 0x70,
+  0x72, 0x6F, 0x76, 0x2E, 0x64, 0x74, 0x64, 0x22,
+  0x3E, 0x3C, 0x77, 0x61, 0x70, 0x2D, 0x70, 0x72,
+  0x6F, 0x76, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x69,
+  0x6E, 0x67, 0x64, 0x6F, 0x63, 0x20, 0x76, 0x65,
+  0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x22, 0x31,
+  0x2E, 0x30, 0x22, 0x3E, 0x3C, 0x63, 0x68, 0x61,
+  0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73,
+  0x74, 0x69, 0x63, 0x20, 0x74, 0x79, 0x70, 0x65,
+  0x3D, 0x22, 0x50, 0x58, 0x4C, 0x4F, 0x47, 0x49,
+  0x43, 0x41, 0x4C, 0x22, 0x3E, 0x3C, 0x70, 0x61,
+  0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
+  0x22, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x2D, 0x49,
+  0x44, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65,
+  0x3D, 0x22, 0x31, 0x37, 0x30, 0x2E, 0x31, 0x38,
+  0x37, 0x2E, 0x35, 0x31, 0x2E, 0x34, 0x22, 0x2F,
+  0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E,
+  0x61, 0x6D, 0x65, 0x3D, 0x22, 0x4E, 0x41, 0x4D,
+  0x45, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65,
+  0x3D, 0x22, 0x42, 0x61, 0x6E, 0x6B, 0x4D, 0x61,
+  0x69, 0x6E, 0x50, 0x72, 0x6F, 0x78, 0x79, 0x22,
+  0x2F, 0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20,
+  0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x53, 0x54,
+  0x41, 0x52, 0x54, 0x50, 0x41, 0x47, 0x45, 0x22,
+  0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22,
+  0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77,
+  0x77, 0x77, 0x2E, 0x62, 0x61, 0x6E, 0x6B, 0x2E,
+  0x63, 0x6F, 0x6D, 0x2F, 0x73, 0x74, 0x61, 0x72,
+  0x74, 0x70, 0x61, 0x67, 0x65, 0x2E, 0x77, 0x6D,
+  0x6C, 0x22, 0x2F, 0x3E, 0x3C, 0x63, 0x68, 0x61,
+  0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73,
+  0x74, 0x69, 0x63, 0x20, 0x74, 0x79, 0x70, 0x65,
+  0x3D, 0x22, 0x50, 0x58, 0x41, 0x55, 0x54, 0x48,
+  0x49, 0x4E, 0x46, 0x4F, 0x22, 0x3E, 0x3C, 0x70,
+  0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65,
+  0x3D, 0x22, 0x50, 0x58, 0x41, 0x55, 0x54, 0x48,
+  0x2D, 0x54, 0x59, 0x50, 0x45, 0x22, 0x20, 0x76,
+  0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x48, 0x54,
+  0x54, 0x50, 0x2D, 0x42, 0x41, 0x53, 0x49, 0x43,
+  0x22, 0x2F, 0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D,
+  0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x50,
+  0x58, 0x41, 0x55, 0x54, 0x48, 0x2D, 0x49, 0x44,
+  0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D,
+  0x22, 0x70, 0x78, 0x75, 0x73, 0x65, 0x72, 0x6E,
+  0x61, 0x6D, 0x65, 0x22, 0x2F, 0x3E, 0x3C, 0x70,
+  0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65,
+  0x3D, 0x22, 0x50, 0x58, 0x41, 0x55, 0x54, 0x48,
+  0x2D, 0x50, 0x57, 0x22, 0x20, 0x76, 0x61, 0x6C,
+  0x75, 0x65, 0x3D, 0x22, 0x70, 0x78, 0x75, 0x73,
+  0x65, 0x72, 0x70, 0x61, 0x73, 0x73, 0x77, 0x64,
+  0x22, 0x2F, 0x3E, 0x3C, 0x2F, 0x63, 0x68, 0x61,
+  0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73,
+  0x74, 0x69, 0x63, 0x3E, 0x3C, 0x63, 0x68, 0x61,
+  0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73,
+  0x74, 0x69, 0x63, 0x20, 0x74, 0x79, 0x70, 0x65,
+  0x3D, 0x22, 0x50, 0x58, 0x50, 0x48, 0x59, 0x53,
+  0x49, 0x43, 0x41, 0x4C, 0x22, 0x3E, 0x3C, 0x70,
+  0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65,
+  0x3D, 0x22, 0x50, 0x48, 0x59, 0x53, 0x49, 0x43,
+  0x41, 0x4C, 0x2D, 0x50, 0x52, 0x4F, 0x58, 0x59,
+  0x2D, 0x49, 0x44, 0x22, 0x20, 0x76, 0x61, 0x6C,
+  0x75, 0x65, 0x3D, 0x22, 0x50, 0x52, 0x4F, 0x58,
+  0x59, 0x20, 0x31, 0x22, 0x2F, 0x3E, 0x3C, 0x70,
+  0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65,
+  0x3D, 0x22, 0x44, 0x4F, 0x4D, 0x41, 0x49, 0x4E,
+  0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D,
+  0x22, 0x77, 0x77, 0x77, 0x2E, 0x62, 0x61, 0x6E,
+  0x6B, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x22, 0x2F,
+  0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E,
+  0x61, 0x6D, 0x65, 0x3D, 0x22, 0x50, 0x58, 0x41,
+  0x44, 0x44, 0x52, 0x22, 0x20, 0x76, 0x61, 0x6C,
+  0x75, 0x65, 0x3D, 0x22, 0x31, 0x37, 0x30, 0x2E,
+  0x31, 0x38, 0x37, 0x2E, 0x35, 0x31, 0x2E, 0x33,
+  0x22, 0x2F, 0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D,
+  0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x50,
+  0x58, 0x41, 0x44, 0x44, 0x52, 0x54, 0x59, 0x50,
+  0x45, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65,
+  0x3D, 0x22, 0x49, 0x50, 0x56, 0x34, 0x22, 0x2F,
+  0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E,
+  0x61, 0x6D, 0x65, 0x3D, 0x22, 0x54, 0x4F, 0x2D,
+  0x4E, 0x41, 0x50, 0x49, 0x44, 0x22, 0x20, 0x76,
+  0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x49, 0x4E,
+  0x54, 0x45, 0x52, 0x4E, 0x45, 0x54, 0x22, 0x2F,
+  0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E,
+  0x61, 0x6D, 0x65, 0x3D, 0x22, 0x54, 0x4F, 0x2D,
+  0x4E, 0x41, 0x50, 0x49, 0x44, 0x22, 0x20, 0x76,
+  0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x4E, 0x41,
+  0x50, 0x31, 0x22, 0x2F, 0x3E, 0x3C, 0x63, 0x68,
+  0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69,
+  0x73, 0x74, 0x69, 0x63, 0x20, 0x74, 0x79, 0x70,
+  0x65, 0x3D, 0x22, 0x50, 0x4F, 0x52, 0x54, 0x22,
+  0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E,
+  0x61, 0x6D, 0x65, 0x3D, 0x22, 0x50, 0x4F, 0x52,
+  0x54, 0x4E, 0x42, 0x52, 0x22, 0x20, 0x76, 0x61,
+  0x6C, 0x75, 0x65, 0x3D, 0x22, 0x39, 0x32, 0x30,
+  0x33, 0x22, 0x2F, 0x3E, 0x3C, 0x2F, 0x63, 0x68,
+  0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69,
+  0x73, 0x74, 0x69, 0x63, 0x3E, 0x3C, 0x2F, 0x63,
+  0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72,
+  0x69, 0x73, 0x74, 0x69, 0x63, 0x3E, 0x3C, 0x2F,
+  0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65,
+  0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x3E, 0x3C,
+  0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65,
+  0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x20, 0x74,
+  0x79, 0x70, 0x65, 0x3D, 0x22, 0x4E, 0x41, 0x50,
+  0x44, 0x45, 0x46, 0x22, 0x3E, 0x3C, 0x70, 0x61,
+  0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
+  0x22, 0x4E, 0x41, 0x50, 0x49, 0x44, 0x22, 0x20,
+  0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x4E,
+  0x41, 0x50, 0x31, 0x22, 0x2F, 0x3E, 0x3C, 0x70,
+  0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65,
+  0x3D, 0x22, 0x42, 0x45, 0x41, 0x52, 0x45, 0x52,
+  0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D,
+  0x22, 0x47, 0x53, 0x4D, 0x2D, 0x43, 0x53, 0x44,
+  0x22, 0x2F, 0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D,
+  0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x4E,
+  0x41, 0x4D, 0x45, 0x22, 0x20, 0x76, 0x61, 0x6C,
+  0x75, 0x65, 0x3D, 0x22, 0x4D, 0x59, 0x20, 0x49,
+  0x53, 0x50, 0x20, 0x43, 0x53, 0x44, 0x22, 0x2F,
+  0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E,
+  0x61, 0x6D, 0x65, 0x3D, 0x22, 0x4E, 0x41, 0x50,
+  0x2D, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53,
+  0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D,
+  0x22, 0x2B, 0x33, 0x35, 0x38, 0x30, 0x38, 0x31,
+  0x32, 0x34, 0x30, 0x30, 0x32, 0x22, 0x2F, 0x3E,
+  0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61,
+  0x6D, 0x65, 0x3D, 0x22, 0x4E, 0x41, 0x50, 0x2D,
+  0x41, 0x44, 0x44, 0x52, 0x54, 0x59, 0x50, 0x45,
+  0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D,
+  0x22, 0x45, 0x31, 0x36, 0x34, 0x22, 0x2F, 0x3E,
+  0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61,
+  0x6D, 0x65, 0x3D, 0x22, 0x43, 0x41, 0x4C, 0x4C,
+  0x54, 0x59, 0x50, 0x45, 0x22, 0x20, 0x76, 0x61,
+  0x6C, 0x75, 0x65, 0x3D, 0x22, 0x41, 0x4E, 0x41,
+  0x4C, 0x4F, 0x47, 0x2D, 0x4D, 0x4F, 0x44, 0x45,
+  0x4D, 0x22, 0x2F, 0x3E, 0x3C, 0x63, 0x68, 0x61,
+  0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73,
+  0x74, 0x69, 0x63, 0x20, 0x74, 0x79, 0x70, 0x65,
+  0x3D, 0x22, 0x4E, 0x41, 0x50, 0x41, 0x55, 0x54,
+  0x48, 0x49, 0x4E, 0x46, 0x4F, 0x22, 0x3E, 0x3C,
+  0x70, 0x61, 0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D,
+  0x65, 0x3D, 0x22, 0x41, 0x55, 0x54, 0x48, 0x54,
+  0x59, 0x50, 0x45, 0x22, 0x20, 0x76, 0x61, 0x6C,
+  0x75, 0x65, 0x3D, 0x22, 0x50, 0x41, 0x50, 0x22,
+  0x2F, 0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D, 0x20,
+  0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x41, 0x55,
+  0x54, 0x48, 0x4E, 0x41, 0x4D, 0x45, 0x22, 0x20,
+  0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x77,
+  0x77, 0x77, 0x6D, 0x6D, 0x6D, 0x75, 0x73, 0x65,
+  0x72, 0x22, 0x2F, 0x3E, 0x3C, 0x70, 0x61, 0x72,
+  0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22,
+  0x41, 0x55, 0x54, 0x48, 0x53, 0x45, 0x43, 0x52,
+  0x45, 0x54, 0x22, 0x20, 0x76, 0x61, 0x6C, 0x75,
+  0x65, 0x3D, 0x22, 0x77, 0x77, 0x77, 0x6D, 0x6D,
+  0x6D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22,
+  0x2F, 0x3E, 0x3C, 0x2F, 0x63, 0x68, 0x61, 0x72,
+  0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74,
+  0x69, 0x63, 0x3E, 0x3C, 0x63, 0x68, 0x61, 0x72,
+  0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74,
+  0x69, 0x63, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D,
+  0x22, 0x56, 0x41, 0x4C, 0x49, 0x44, 0x49, 0x54,
+  0x59, 0x22, 0x3E, 0x3C, 0x70, 0x61, 0x72, 0x6D,
+  0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x43,
+  0x4F, 0x55, 0x4E, 0x54, 0x52, 0x59, 0x22, 0x20,
+  0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x22, 0x32,
+  0x32, 0x38, 0x22, 0x2F, 0x3E, 0x3C, 0x70, 0x61,
+  0x72, 0x6D, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D,
+  0x22, 0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B,
+  0x22, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D,
+  0x22, 0x30, 0x30, 0x31, 0x22, 0x2F, 0x3E, 0x3C,
+  0x2F, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74,
+  0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x3E,
+  0x3C, 0x2F, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63,
+  0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63,
+  0x3E, 0x3C, 0x2F, 0x77, 0x61, 0x70, 0x2D, 0x70,
+  0x72, 0x6F, 0x76, 0x69, 0x73, 0x69, 0x6F, 0x6E,
+  0x69, 0x6E, 0x67, 0x64, 0x6F, 0x63, 0x3E
+]);
+
+/*
+ * Test data from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, Appendix C
+ */
+let wbxml_data_array = new Uint8Array([
+  0x03, 0x0b, 0x6a, 0x05, 0x4e, 0x41, 0x50, 0x31,
+  0x00, 0xC5, 0x46, 0x01, 0xc6, 0x51, 0x01, 0x87,
+  0x15, 0x06, 0x03, 0x31, 0x37, 0x30, 0x2e, 0x31,
+  0x38, 0x37, 0x2e, 0x35, 0x31, 0x2e, 0x34, 0x00,
+  0x01, 0x87, 0x07, 0x06, 0x03, 0x42, 0x61, 0x6e,
+  0x6b, 0x4d, 0x61, 0x69, 0x6e, 0x50, 0x72, 0x6f,
+  0x78, 0x79, 0x00, 0x01, 0x87, 0x1c, 0x06, 0x03,
+  0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+  0x77, 0x77, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e,
+  0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x72,
+  0x74, 0x70, 0x61, 0x67, 0x65, 0x2e, 0x77, 0x6d,
+  0x6c, 0x00, 0x01, 0xc6, 0x59, 0x01, 0x87, 0x19,
+  0x06, 0x9c, 0x01, 0x87, 0x1a, 0x06, 0x03, 0x70,
+  0x78, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d,
+  0x65, 0x00, 0x01, 0x87, 0x1b, 0x06, 0x03, 0x70,
+  0x78, 0x75, 0x73, 0x65, 0x72, 0x70, 0x61, 0x73,
+  0x73, 0x77, 0x64, 0x00, 0x01, 0x01, 0xc6, 0x52,
+  0x01, 0x87, 0x2f, 0x06, 0x03, 0x50, 0x52, 0x4f,
+  0x58, 0x59, 0x20, 0x31, 0x00, 0x01, 0x87, 0x17,
+  0x06, 0x03, 0x77, 0x77, 0x77, 0x2e, 0x62, 0x61,
+  0x6e, 0x6b, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x00,
+  0x01, 0x87, 0x20, 0x06, 0x03, 0x31, 0x37, 0x30,
+  0x2e, 0x31, 0x38, 0x37, 0x2e, 0x35, 0x31, 0x2e,
+  0x33, 0x00, 0x01, 0x87, 0x21, 0x06, 0x85, 0x01,
+  0x87, 0x22, 0x06, 0x03, 0x49, 0x4e, 0x54, 0x45,
+  0x52, 0x4e, 0x45, 0x54, 0x00, 0x01, 0x87, 0x22,
+  0x06, 0x83, 0x00, 0x01, 0xc6, 0x53, 0x01, 0x87,
+  0x23, 0x06, 0x03, 0x39, 0x32, 0x30, 0x33, 0x00,
+  0x01, 0x01, 0x01, 0x01, 0xc6, 0x55, 0x01, 0x87,
+  0x11, 0x06, 0x83, 0x00, 0x01, 0x87, 0x10, 0x06,
+  0xaa, 0x01, 0x87, 0x07, 0x06, 0x03, 0x4d, 0x59,
+  0x20, 0x49, 0x53, 0x50, 0x20, 0x43, 0x53, 0x44,
+  0x00, 0x01, 0x87, 0x08, 0x06, 0x03, 0x2b, 0x33,
+  0x35, 0x38, 0x30, 0x38, 0x31, 0x32, 0x34, 0x30,
+  0x30, 0x32, 0x00, 0x01, 0x87, 0x09, 0x06, 0x87,
+  0x01, 0x87, 0x0a, 0x06, 0x90, 0x01, 0xc6, 0x5a,
+  0x01, 0x87, 0x0c, 0x06, 0x9a, 0x01, 0x87, 0x0d,
+  0x06, 0x03, 0x77, 0x77, 0x77, 0x6d, 0x6d, 0x6d,
+  0x75, 0x73, 0x65, 0x72, 0x00, 0x01, 0x87, 0x0e,
+  0x06, 0x03, 0x77, 0x77, 0x77, 0x6d, 0x6d, 0x6d,
+  0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x00, 0x01,
+  0x01, 0xc6, 0x54, 0x01, 0x87, 0x12, 0x06, 0x03,
+  0x32, 0x32, 0x38, 0x00, 0x01, 0x87, 0x13, 0x06,
+  0x03, 0x30, 0x30, 0x31, 0x00, 0x01, 0x01, 0x01,
+  0x01
+]);
+
+/*
+ * Test data from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, clause 6.1
+ */
+let xml_header =
+  "<?xml version=\"1.0\"?>" +
+  "<!DOCTYPE wap-provisioningdoc PUBLIC \"-//WAPFORUM//DTD PROV 1.0//EN\" \"http://www.wapforum.org/DTD/prov.dtd\">";
+
+let xml_body =
+  "<wap-provisioningdoc version=\"1.0\">" +
+    "<characteristic type=\"PXLOGICAL\">" +
+      "<parm name=\"PROXY-ID\" value=\"170.187.51.4\"/>" +
+      "<parm name=\"NAME\" value=\"BankMainProxy\"/>" +
+      "<parm name=\"STARTPAGE\" value=\"http://www.bank.com/startpage.wml\"/>" +
+      "<characteristic type=\"PXAUTHINFO\">" +
+        "<parm name=\"PXAUTH-TYPE\" value=\"HTTP-BASIC\"/>" +
+        "<parm name=\"PXAUTH-ID\" value=\"pxusername\"/>" +
+        "<parm name=\"PXAUTH-PW\" value=\"pxuserpasswd\"/>" +
+      "</characteristic>" +
+      "<characteristic type=\"PXPHYSICAL\">" +
+        "<parm name=\"PHYSICAL-PROXY-ID\" value=\"PROXY 1\"/>" +
+        "<parm name=\"DOMAIN\" value=\"www.bank.com/\"/>" +
+        "<parm name=\"PXADDR\" value=\"170.187.51.3\"/>" +
+        "<parm name=\"PXADDRTYPE\" value=\"IPV4\"/>" +
+        "<parm name=\"TO-NAPID\" value=\"INTERNET\"/>" +
+        "<parm name=\"TO-NAPID\" value=\"NAP1\"/>" +
+        "<characteristic type=\"PORT\">" +
+          "<parm name=\"PORTNBR\" value=\"9203\"/>" +
+        "</characteristic>" +
+      "</characteristic>" +
+    "</characteristic>" +
+    "<characteristic type=\"NAPDEF\">" +
+      "<parm name=\"NAPID\" value=\"NAP1\"/>" +
+      "<parm name=\"BEARER\" value=\"GSM-CSD\"/>" +
+      "<parm name=\"NAME\" value=\"MY ISP CSD\"/>" +
+      "<parm name=\"NAP-ADDRESS\" value=\"+35808124002\"/>" +
+      "<parm name=\"NAP-ADDRTYPE\" value=\"E164\"/>" +
+      "<parm name=\"CALLTYPE\" value=\"ANALOG-MODEM\"/>" +
+      "<characteristic type=\"NAPAUTHINFO\">" +
+        "<parm name=\"AUTHTYPE\" value=\"PAP\"/>" +
+        "<parm name=\"AUTHNAME\" value=\"wwwmmmuser\"/>" +
+        "<parm name=\"AUTHSECRET\" value=\"wwwmmmsecret\"/>" +
+      "</characteristic>" +
+      "<characteristic type=\"VALIDITY\">" +
+        "<parm name=\"COUNTRY\" value=\"228\"/>" +
+        "<parm name=\"NETWORK\" value=\"001\"/>" +
+      "</characteristic>" +
+    "</characteristic>" +
+  "</wap-provisioningdoc>";
+
+/**
+ * CP in plain text
+ *
+ * Test case from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, clause 6.1
+ */
+add_test(function test_cp_parse_plain_text() {
+  test_parser(text_data_array, "text/vnd.wap.connectivity-xml", {
+    contentType: "text/vnd.wap.connectivity-xml",
+    content: xml_header + xml_body
+  });
+
+  run_next_test();
+});
+
+/**
+ * CP compressed by WBXML
+ *
+ * Test case from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, Appendix C
+ */
+add_test(function test_cp_parse_wbxml() {
+  test_parser(wbxml_data_array, "application/vnd.wap.connectivity-wbxml", {
+    contentType: "text/vnd.wap.connectivity-xml",
+    content: xml_body
+  });
+
+  run_next_test();
+});
+
+/**
+ * CP compressed by WBXML with VENDORCONFIG
+ */
+add_test(function test_cp_parse_wbxml() {
+  let wbxml_vendor_config_data_array = new Uint8Array([
+    0x03, 0x0b, 0x6a, 0x05, 0x4e, 0x41, 0x50, 0x31,
+    0x00, 0xC5, 0x46, 0x01, 0xc6, 0x57, 0x01, 0x01
+  ]);
+
+  test_parser(wbxml_vendor_config_data_array, "application/vnd.wap.connectivity-wbxml", {
+                contentType: "application/vnd.wap.connectivity-wbxml",
+                content: wbxml_vendor_config_data_array
+              });
+
+  run_next_test();
+});
--- a/dom/wappush/tests/xpcshell.ini
+++ b/dom/wappush/tests/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 head = header_helpers.js
 tail =
 
 [test_si_pdu_helper.js]
 [test_sl_pdu_helper.js]
+[test_cp_pdu_helper.js]
deleted file mode 100644
--- a/gfx/layers/GonkIOSurfaceImage.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "GonkIOSurfaceImage.h"
-
-#include <OMX_IVCommon.h>
-#include <ColorConverter.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-
-#define ALIGN(x, align) ((x + align - 1) & ~(align - 1))
-
-typedef android::GraphicBuffer GraphicBuffer;
-
-namespace mozilla {
-namespace layers {
-
-uint32_t GonkIOSurfaceImage::sColorIdMap[] = {
-    HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar,
-    HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar,
-    HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar,
-    HAL_PIXEL_FORMAT_YCrCb_420_SP, -1,
-    HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1,
-    HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar,
-    0, 0
-};
-
-struct GraphicBufferAutoUnlock {
-  android::sp<GraphicBuffer> mGraphicBuffer;
-
-  GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer)
-    : mGraphicBuffer(aGraphicBuffer) { }
-
-  ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); }
-};
-
-/**
- * Converts YVU420 semi planar frames to RGB565, possibly taking different
- * stride values.
- * Needed because the Android ColorConverter class assumes that the Y and UV
- * channels have equal stride.
- */
-static void
-ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride,
-                        void *aUVData, uint32_t aUVStride,
-                        void *aOut,
-                        uint32_t aWidth, uint32_t aHeight)
-{
-  uint8_t *y = (uint8_t*)aYData;
-  int8_t *uv = (int8_t*)aUVData;
-
-  uint16_t *rgb = (uint16_t*)aOut;
-
-  for (size_t i = 0; i < aHeight; i++) {
-    for (size_t j = 0; j < aWidth; j++) {
-      int8_t d = uv[j | 1] - 128;
-      int8_t e = uv[j & ~1] - 128;
-
-      // Constants taken from https://en.wikipedia.org/wiki/YUV
-      int32_t r = (298 * y[j] + 409 * e + 128) >> 11;
-      int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10;
-      int32_t b = (298 * y[j] + 516 * d + 128) >> 11;
-
-      r = r > 0x1f ? 0x1f : r < 0 ? 0 : r;
-      g = g > 0x3f ? 0x3f : g < 0 ? 0 : g;
-      b = b > 0x1f ? 0x1f : b < 0 ? 0 : b;
-
-      *rgb++ = (uint16_t)(r << 11 | g << 5 | b);
-    }
-
-    y += aYStride;
-    if (i % 2) {
-      uv += aUVStride;
-    }
-  }
-}
-
-already_AddRefed<gfxASurface>
-GonkIOSurfaceImage::GetAsSurface()
-{
-  android::sp<GraphicBuffer> graphicBuffer =
-    GrallocBufferActor::GetFrom(GetSurfaceDescriptor());
-
-  void *buffer;
-  int32_t rv =
-    graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer);
-
-  if (rv) {
-    NS_WARNING("Couldn't lock graphic buffer");
-    return nullptr;
-  }
-
-  GraphicBufferAutoUnlock unlock(graphicBuffer);
-
-  uint32_t format = graphicBuffer->getPixelFormat();
-  uint32_t omxFormat = 0;
-
-  for (int i = 0; sColorIdMap[i]; i += 2) {
-    if (sColorIdMap[i] == format) {
-      omxFormat = sColorIdMap[i + 1];
-      break;
-    }
-  }
-
-  if (!omxFormat) {
-    NS_WARNING("Unknown color format");
-    return nullptr;
-  }
-
-  nsRefPtr<gfxImageSurface> imageSurface =
-    new gfxImageSurface(GetSize(), gfxASurface::ImageFormatRGB16_565);
-
-  uint32_t width = GetSize().width;
-  uint32_t height = GetSize().height;
-
-  if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) {
-    // The Adreno hardware decoder aligns image dimensions to a multiple of 32,
-    // so we have to account for that here
-    uint32_t alignedWidth = ALIGN(width, 32);
-    uint32_t alignedHeight = ALIGN(height, 32);
-    uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096);
-    uint32_t uvStride = 2 * ALIGN(width / 2, 32);
-    uint8_t* buffer_as_bytes = static_cast<uint8_t*>(buffer);
-    ConvertYVU420SPToRGB565(buffer, alignedWidth,
-                            buffer_as_bytes + uvOffset, uvStride,
-                            imageSurface->Data(),
-                            width, height);
-
-    return imageSurface.forget();
-  }
-  else if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
-    uint32_t uvOffset = height * width;
-    ConvertYVU420SPToRGB565(buffer, width,
-                            buffer + uvOffset, width,
-                            imageSurface->Data(),
-                            width, height);
-
-    return imageSurface.forget();
-  }
-
-  android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
-                                         OMX_COLOR_Format16bitRGB565);
-
-  if (!colorConverter.isValid()) {
-    NS_WARNING("Invalid color conversion");
-    return nullptr;
-  }
-
-  rv = colorConverter.convert(buffer, width, height,
-                              0, 0, width - 1, height - 1 /* source crop */,
-                              imageSurface->Data(), width, height,
-                              0, 0, width - 1, height - 1 /* dest crop */);
-
-  if (rv) {
-    NS_WARNING("OMX color conversion failed");
-    return nullptr;
-  }
-
-  return imageSurface.forget();
-}
-
-} // namespace layers
-} // namespace mozilla
deleted file mode 100644
--- a/gfx/layers/GonkIOSurfaceImage.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; 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 GONKIOSURFACEIMAGE_H
-#define GONKIOSURFACEIMAGE_H
-
-#ifdef MOZ_WIDGET_GONK
-
-#include "mozilla/layers/LayersSurfaces.h"
-#include "ImageContainer.h"
-
-#include <ui/GraphicBuffer.h>
-
-namespace mozilla {
-namespace layers {
-
-/**
- * The gralloc buffer maintained by android GraphicBuffer can be
- * shared between the compositor thread and the producer thread. The
- * mGraphicBuffer is owned by the producer thread, but when it is
- * wrapped by GraphicBufferLocked and passed to the compositor, the
- * buffer content is guaranteed to not change until Unlock() is
- * called. Each producer must maintain their own buffer queue and
- * implement the GraphicBufferLocked::Unlock() interface.
- */
-class GraphicBufferLocked {
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphicBufferLocked)
-
-public:
-  GraphicBufferLocked(SurfaceDescriptor aGraphicBuffer)
-    : mSurfaceDescriptor(aGraphicBuffer)
-  {}
-
-  virtual ~GraphicBufferLocked() {}
-
-  virtual void Unlock() {}
-
-  SurfaceDescriptor GetSurfaceDescriptor()
-  {
-    return mSurfaceDescriptor;
-  }
-
-protected:
-  SurfaceDescriptor mSurfaceDescriptor;
-};
-
-class GonkIOSurfaceImage : public Image {
-  typedef android::GraphicBuffer GraphicBuffer;
-  static uint32_t sColorIdMap[];
-public:
-  struct Data {
-    nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
-    gfxIntSize mPicSize;
-  };
-  GonkIOSurfaceImage()
-    : Image(nullptr, GONK_IO_SURFACE)
-    , mSize(0, 0)
-    {}
-
-  virtual ~GonkIOSurfaceImage()
-  {
-    mGraphicBuffer->Unlock();
-  }
-
-  virtual void SetData(const Data& aData)
-  {
-    mGraphicBuffer = aData.mGraphicBuffer;
-    mSize = aData.mPicSize;
-  }
-
-  virtual gfxIntSize GetSize()
-  {
-    return mSize;
-  }
-
-  // From [android 4.0.4]/hardware/msm7k/libgralloc-qsd8k/gralloc_priv.h
-  enum {
-    /* OEM specific HAL formats */
-    HAL_PIXEL_FORMAT_YCbCr_422_P            = 0x102,
-    HAL_PIXEL_FORMAT_YCbCr_420_P            = 0x103,
-    HAL_PIXEL_FORMAT_YCbCr_420_SP           = 0x109,
-    HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO    = 0x10A,
-  };
-
-  enum {
-    OMX_QCOM_COLOR_FormatYVU420SemiPlanar   = 0x7FA30C00,
-  };
-
-  virtual already_AddRefed<gfxASurface> GetAsSurface();
-
-  void* GetNativeBuffer()
-  {
-    return GrallocBufferActor::GetFrom(GetSurfaceDescriptor())->getNativeBuffer();
-  }
-
-  SurfaceDescriptor GetSurfaceDescriptor()
-  {
-    return mGraphicBuffer->GetSurfaceDescriptor();
-  }
-
-private:
-  nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
-  gfxIntSize mSize;
-};
-
-} // namespace layers
-} // namespace mozilla
-#endif
-
-#endif /* GONKIOSURFACEIMAGE_H */
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -5,55 +5,93 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/ImageBridgeChild.h"
 
 #include "nsDebug.h"
 #include "ImageContainer.h"
 #include "GrallocImages.h"
 
+#include <OMX_IVCommon.h>
+#include <ColorConverter.h>
+
 using namespace mozilla::ipc;
 using namespace android;
 
+#define ALIGN(x, align) ((x + align - 1) & ~(align - 1))
+
 namespace mozilla {
 namespace layers {
 
-GrallocPlanarYCbCrImage::GrallocPlanarYCbCrImage()
-  : PlanarYCbCrImage(nullptr)
+uint32_t GrallocImage::sColorIdMap[] = {
+    HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar,
+    HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar,
+    HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar,
+    HAL_PIXEL_FORMAT_YCrCb_420_SP, -1,
+    HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1,
+    HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar,
+    0, 0
+};
+
+struct GraphicBufferAutoUnlock {
+  android::sp<GraphicBuffer> mGraphicBuffer;
+
+  GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer)
+    : mGraphicBuffer(aGraphicBuffer) { }
+
+  ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); }
+};
+
+GrallocImage::GrallocImage()
+  : PlanarYCbCrImage(nullptr),
+    mBufferAllocated(false),
+    mGraphicBuffer(nullptr)
 {
   mFormat = GRALLOC_PLANAR_YCBCR;
 }
 
-GrallocPlanarYCbCrImage::~GrallocPlanarYCbCrImage()
+GrallocImage::~GrallocImage()
 {
-  ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
-  ibc->DeallocSurfaceDescriptorGralloc(mSurfaceDescriptor);
+  if (mGraphicBuffer.get()) {
+    mGraphicBuffer->Unlock();
+    if (mBufferAllocated) {
+      ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
+      ibc->DeallocSurfaceDescriptorGralloc(mGraphicBuffer->GetSurfaceDescriptor());
+      mBufferAllocated = false;
+    }
+  }
 }
 
 void
-GrallocPlanarYCbCrImage::SetData(const Data& aData)
+GrallocImage::SetData(const Data& aData)
 {
   NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width");
   NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height");
   NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels");
 
   mData = aData;
   mSize = aData.mPicSize;
 
-  if (mSurfaceDescriptor.type() == SurfaceDescriptor::T__None) {
+  if (!mGraphicBuffer.get()) {
+
+    SurfaceDescriptor desc;
     ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
     ibc->AllocSurfaceDescriptorGralloc(aData.mYSize,
                                        HAL_PIXEL_FORMAT_YV12,
                                        GraphicBuffer::USAGE_SW_READ_OFTEN |
                                        GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                                        GraphicBuffer::USAGE_HW_TEXTURE,
-                                       &mSurfaceDescriptor);
+                                       &desc);
+    mBufferAllocated = true;
+    mGraphicBuffer = new GraphicBufferLocked(desc);
   }
+
   sp<GraphicBuffer> graphicBuffer =
-    GrallocBufferActor::GetFrom(mSurfaceDescriptor.get_SurfaceDescriptorGralloc());
+    GrallocBufferActor::GetFrom(
+      mGraphicBuffer->GetSurfaceDescriptor().get_SurfaceDescriptorGralloc());
   if (!graphicBuffer.get()) {
     return;
   }
 
   if (graphicBuffer->initCheck() != NO_ERROR) {
     return;
   }
 
@@ -99,10 +137,141 @@ GrallocPlanarYCbCrImage::SetData(const D
       memcpy(vChannel + i * uvStride,
              mData.mCrChannel + i * mData.mCbCrStride,
              uvSize.width);
     }
   }
   graphicBuffer->unlock();
 }
 
+void GrallocImage::SetData(const GrallocData& aData)
+{
+  mGraphicBuffer = aData.mGraphicBuffer;
+  mSize = aData.mPicSize;
+}
+
+/**
+ * Converts YVU420 semi planar frames to RGB565, possibly taking different
+ * stride values.
+ * Needed because the Android ColorConverter class assumes that the Y and UV
+ * channels have equal stride.
+ */
+static void
+ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride,
+                        void *aUVData, uint32_t aUVStride,
+                        void *aOut,
+                        uint32_t aWidth, uint32_t aHeight)
+{
+  uint8_t *y = (uint8_t*)aYData;
+  int8_t *uv = (int8_t*)aUVData;
+
+  uint16_t *rgb = (uint16_t*)aOut;
+
+  for (size_t i = 0; i < aHeight; i++) {
+    for (size_t j = 0; j < aWidth; j++) {
+      int8_t d = uv[j | 1] - 128;
+      int8_t e = uv[j & ~1] - 128;
+
+      // Constants taken from https://en.wikipedia.org/wiki/YUV
+      int32_t r = (298 * y[j] + 409 * e + 128) >> 11;
+      int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10;
+      int32_t b = (298 * y[j] + 516 * d + 128) >> 11;
+
+      r = r > 0x1f ? 0x1f : r < 0 ? 0 : r;
+      g = g > 0x3f ? 0x3f : g < 0 ? 0 : g;
+      b = b > 0x1f ? 0x1f : b < 0 ? 0 : b;
+
+      *rgb++ = (uint16_t)(r << 11 | g << 5 | b);
+    }
+
+    y += aYStride;
+    if (i % 2) {
+      uv += aUVStride;
+    }
+  }
+}
+
+already_AddRefed<gfxASurface>
+GrallocImage::GetAsSurface()
+{
+  android::sp<GraphicBuffer> graphicBuffer =
+    GrallocBufferActor::GetFrom(GetSurfaceDescriptor());
+
+  void *buffer;
+  int32_t rv =
+    graphicBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &buffer);
+
+  if (rv) {
+    NS_WARNING("Couldn't lock graphic buffer");
+    return nullptr;
+  }
+
+  GraphicBufferAutoUnlock unlock(graphicBuffer);
+
+  uint32_t format = graphicBuffer->getPixelFormat();
+  uint32_t omxFormat = 0;
+
+  for (int i = 0; sColorIdMap[i]; i += 2) {
+    if (sColorIdMap[i] == format) {
+      omxFormat = sColorIdMap[i + 1];
+      break;
+    }
+  }
+
+  if (!omxFormat) {
+    NS_WARNING("Unknown color format");
+    return nullptr;
+  }
+
+  nsRefPtr<gfxImageSurface> imageSurface =
+    new gfxImageSurface(GetSize(), gfxASurface::ImageFormatRGB16_565);
+
+  uint32_t width = GetSize().width;
+  uint32_t height = GetSize().height;
+
+  if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) {
+    // The Adreno hardware decoder aligns image dimensions to a multiple of 32,
+    // so we have to account for that here
+    uint32_t alignedWidth = ALIGN(width, 32);
+    uint32_t alignedHeight = ALIGN(height, 32);
+    uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096);
+    uint32_t uvStride = 2 * ALIGN(width / 2, 32);
+    uint8_t* buffer_as_bytes = static_cast<uint8_t*>(buffer);
+    ConvertYVU420SPToRGB565(buffer, alignedWidth,
+                            buffer_as_bytes + uvOffset, uvStride,
+                            imageSurface->Data(),
+                            width, height);
+
+    return imageSurface.forget();
+  }
+  else if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+    uint32_t uvOffset = height * width;
+    ConvertYVU420SPToRGB565(buffer, width,
+                            buffer + uvOffset, width,
+                            imageSurface->Data(),
+                            width, height);
+
+    return imageSurface.forget();
+  }
+
+  android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
+                                         OMX_COLOR_Format16bitRGB565);
+
+  if (!colorConverter.isValid()) {
+    NS_WARNING("Invalid color conversion");
+    return nullptr;
+  }
+
+  rv = colorConverter.convert(buffer, width, height,
+                              0, 0, width - 1, height - 1 /* source crop */,
+                              imageSurface->Data(), width, height,
+                              0, 0, width - 1, height - 1 /* dest crop */);
+
+  if (rv) {
+    NS_WARNING("OMX color conversion failed");
+    return nullptr;
+  }
+
+  return imageSurface.forget();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/GrallocImages.h
+++ b/gfx/layers/GrallocImages.h
@@ -5,23 +5,54 @@
 
 #ifndef GRALLOCIMAGES_H
 #define GRALLOCIMAGES_H
 
 #ifdef MOZ_WIDGET_GONK
 
 #include "mozilla/layers/LayersSurfaces.h"
 #include "ImageLayers.h"
+#include "ImageContainer.h"
 
 #include <ui/GraphicBuffer.h>
 
 namespace mozilla {
 namespace layers {
 
 /**
+ * The gralloc buffer maintained by android GraphicBuffer can be
+ * shared between the compositor thread and the producer thread. The
+ * mGraphicBuffer is owned by the producer thread, but when it is
+ * wrapped by GraphicBufferLocked and passed to the compositor, the
+ * buffer content is guaranteed to not change until Unlock() is
+ * called. Each producer must maintain their own buffer queue and
+ * implement the GraphicBufferLocked::Unlock() interface.
+ */
+class GraphicBufferLocked {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphicBufferLocked)
+
+public:
+  GraphicBufferLocked(SurfaceDescriptor aGraphicBuffer)
+    : mSurfaceDescriptor(aGraphicBuffer)
+  {}
+
+  virtual ~GraphicBufferLocked() {}
+
+  virtual void Unlock() {}
+
+  SurfaceDescriptor GetSurfaceDescriptor()
+  {
+    return mSurfaceDescriptor;
+  }
+
+protected:
+  SurfaceDescriptor mSurfaceDescriptor;
+};
+
+/**
  * The YUV format supported by Android HAL
  *
  * 4:2:0 - CbCr width and height is half that of Y.
  *
  * This format assumes
  * - an even width
  * - an even height
  * - a horizontal stride multiple of 16 pixels
@@ -32,39 +63,73 @@ namespace layers {
  * size = y_size + c_size * 2
  * cr_offset = y_size
  * cb_offset = y_size + c_size
  *
  * The Image that is rendered is the picture region defined by
  * mPicX, mPicY and mPicSize. The size of the rendered image is
  * mPicSize, not mYSize or mCbCrSize.
  */
-class GrallocPlanarYCbCrImage : public PlanarYCbCrImage {
+class GrallocImage : public PlanarYCbCrImage {
   typedef PlanarYCbCrImage::Data Data;
+  static uint32_t sColorIdMap[];
 
 public:
-  GrallocPlanarYCbCrImage();
+  struct GrallocData {
+      nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
+      gfxIntSize mPicSize;
+  };
 
-  virtual ~GrallocPlanarYCbCrImage();
+  GrallocImage();
+
+  virtual ~GrallocImage();
 
   /**
    * This makes a copy of the data buffers, in order to support functioning
    * in all different layer managers.
    */
   virtual void SetData(const Data& aData);
 
-  virtual uint32_t GetDataSize() { return 0; }
+  /**
+   *  Share the SurfaceDescriptor without making the copy, in order
+   *  to support functioning in all different layer managers.
+   */
+  virtual void SetData(const GrallocData& aData);
 
-  virtual bool IsValid() { return mSurfaceDescriptor.type() != SurfaceDescriptor::T__None; }
+  // From [android 4.0.4]/hardware/msm7k/libgralloc-qsd8k/gralloc_priv.h
+  enum {
+    /* OEM specific HAL formats */
+    HAL_PIXEL_FORMAT_YCbCr_422_P            = 0x102,
+    HAL_PIXEL_FORMAT_YCbCr_420_P            = 0x103,
+    HAL_PIXEL_FORMAT_YCbCr_420_SP           = 0x109,
+    HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO    = 0x10A,
+  };
+
+  virtual already_AddRefed<gfxASurface> GetAsSurface();
+
+  void* GetNativeBuffer()
+  {
+    if (IsValid()) {
+      return GrallocBufferActor::GetFrom(GetSurfaceDescriptor())->getNativeBuffer();
+    } else {
+      return nullptr;
+    }
+  }
+
+  virtual bool IsValid() { return GetSurfaceDescriptor().type() != SurfaceDescriptor::T__None; }
 
   SurfaceDescriptor GetSurfaceDescriptor() {
-    return mSurfaceDescriptor;
+    if (mGraphicBuffer.get()) {
+      return mGraphicBuffer->GetSurfaceDescriptor();
+    }
+    return SurfaceDescriptor();
   }
 
 private:
-  SurfaceDescriptor mSurfaceDescriptor;
+  bool mBufferAllocated;
+  nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
 };
 
 } // namespace layers
 } // namespace mozilla
 #endif
 
 #endif /* GRALLOCIMAGES_H */
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -2,17 +2,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/. */
 
 
 #include "mozilla/layers/ImageBridgeChild.h"
 
 #include "ImageContainer.h"
-#include "GonkIOSurfaceImage.h"
 #include "GrallocImages.h"
 #include "mozilla/ipc/Shmem.h"
 #include "mozilla/ipc/CrossProcessMutex.h"
 #include "SharedTextureImage.h"
 #include "gfxImageSurface.h"
 #include "gfxSharedImageSurface.h"
 #include "yuv_convert.h"
 #include "gfxUtils.h"
@@ -49,38 +48,32 @@ ImageFactory::CreateImage(const ImageFor
                           BufferRecycleBin *aRecycleBin)
 {
   if (!aNumFormats) {
     return nullptr;
   }
   nsRefPtr<Image> img;
 #ifdef MOZ_WIDGET_GONK
   if (FormatInList(aFormats, aNumFormats, GRALLOC_PLANAR_YCBCR)) {
-    img = new GrallocPlanarYCbCrImage();
+    img = new GrallocImage();
     return img.forget();
   }
 #endif
   if (FormatInList(aFormats, aNumFormats, PLANAR_YCBCR)) {
     img = new PlanarYCbCrImage(aRecycleBin);
     return img.forget();
   }
   if (FormatInList(aFormats, aNumFormats, CAIRO_SURFACE)) {
     img = new CairoImage();
     return img.forget();
   }
   if (FormatInList(aFormats, aNumFormats, SHARED_TEXTURE)) {
     img = new SharedTextureImage();
     return img.forget();
   }
-#ifdef MOZ_WIDGET_GONK
-  if (FormatInList(aFormats, aNumFormats, GONK_IO_SURFACE)) {
-    img = new GonkIOSurfaceImage();
-    return img.forget();
-  }
-#endif
 #ifdef XP_WIN
   if (FormatInList(aFormats, aNumFormats, D3D9_RGB32_TEXTURE)) {
     img = new D3D9SurfaceImage();
     return img.forget();
   }
 #endif
   return nullptr;
 }
--- a/gfx/layers/ImageTypes.h
+++ b/gfx/layers/ImageTypes.h
@@ -12,19 +12,19 @@ enum ImageFormat {
   /**
    * The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
    * support this format, because the Ogg video decoder depends on it.
    * The maximum image width and height is 16384.
    */
   PLANAR_YCBCR,
 
   /**
-   * The GRALLOC_PLANAR_YCBCR format creates a GrallocPlanarYCbCrImage, a
-   * subtype of PlanarYCbCrImage. It takes a PlanarYCbCrImage data and can be
-   * used as a texture by Gonk backend directly.
+   * The GRALLOC_PLANAR_YCBCR format creates a GrallocImage, a subtype of
+   * PlanarYCbCrImage. It takes a PlanarYCbCrImage data or the raw gralloc
+   * data and can be used as a texture by Gonk backend directly.
    */
   GRALLOC_PLANAR_YCBCR,
 
   /**
    * The SHARED_RGB format creates a DeprecatedSharedRGBImage, which stores RGB data in
    * shared memory. Some Android hardware video decoders require this format.
    * Currently only used on Android.
    */
@@ -41,23 +41,16 @@ enum ImageFormat {
    *
    * Images in CAIRO_SURFACE format should only be created and
    * manipulated on the main thread, since the underlying cairo surface
    * is main-thread-only.
    */
   CAIRO_SURFACE,
 
   /**
-   * The GONK_IO_SURFACE format creates a GonkIOSurfaceImage.
-   *
-   * It wraps an GraphicBuffer object and binds it directly to a GL texture.
-   */
-  GONK_IO_SURFACE,
-
-  /**
    * An bitmap image that can be shared with a remote process.
    */
   REMOTE_IMAGE_BITMAP,
 
   /**
    * A OpenGL texture that can be shared across threads or processes
    */
   SHARED_TEXTURE,
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -8,17 +8,16 @@
 #include "BasicLayers.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "SharedTextureImage.h"
 #include "ImageContainer.h" // For PlanarYCbCrImage
 #include "mozilla/layers/SharedRGBImage.h"
 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
 
 #ifdef MOZ_WIDGET_GONK
-#include "GonkIOSurfaceImage.h"
 #include "GrallocImages.h"
 #endif
 
 namespace mozilla {
 namespace layers {
 
 /* static */ TemporaryRef<ImageClient>
 ImageClient::CreateImageClient(CompositableType aCompositableHostType,
@@ -147,41 +146,27 @@ DeprecatedImageClientSingle::UpdateImage
     AutoLockDeprecatedTextureClient lock(mDeprecatedTextureClient);
 
     SurfaceDescriptor desc;
     if (!static_cast<DeprecatedSharedRGBImage*>(image)->ToSurfaceDescriptor(desc)) {
       return false;
     }
     mDeprecatedTextureClient->SetDescriptor(desc);
 #ifdef MOZ_WIDGET_GONK
-  } else if (image->GetFormat() == GONK_IO_SURFACE &&
-             EnsureDeprecatedTextureClient(TEXTURE_SHARED_GL_EXTERNAL)) {
-    nsIntRect rect(0, 0,
-                   image->GetSize().width,
-                   image->GetSize().height);
-    UpdatePictureRect(rect);
-
-    AutoLockDeprecatedTextureClient lock(mDeprecatedTextureClient);
-
-    SurfaceDescriptor desc = static_cast<GonkIOSurfaceImage*>(image)->GetSurfaceDescriptor();
-    if (!IsSurfaceDescriptorValid(desc)) {
-      return false;
-    }
-    mDeprecatedTextureClient->SetDescriptor(desc);
   } else if (image->GetFormat() == GRALLOC_PLANAR_YCBCR) {
     EnsureDeprecatedTextureClient(TEXTURE_SHARED_GL_EXTERNAL);
 
     nsIntRect rect(0, 0,
                    image->GetSize().width,
                    image->GetSize().height);
     UpdatePictureRect(rect);
 
     AutoLockDeprecatedTextureClient lock(mDeprecatedTextureClient);
 
-    SurfaceDescriptor desc = static_cast<GrallocPlanarYCbCrImage*>(image)->GetSurfaceDescriptor();
+    SurfaceDescriptor desc = static_cast<GrallocImage*>(image)->GetSurfaceDescriptor();
     if (!IsSurfaceDescriptorValid(desc)) {
       return false;
     }
     mDeprecatedTextureClient->SetDescriptor(desc);
 #endif
   } else {
     nsRefPtr<gfxASurface> surface = image->GetAsSurface();
     MOZ_ASSERT(surface);
@@ -248,21 +233,18 @@ ImageClient::CreateImage(const uint32_t 
     switch (aFormats[i]) {
       case PLANAR_YCBCR:
         img = new DeprecatedSharedPlanarYCbCrImage(GetForwarder());
         return img.forget();
       case SHARED_RGB:
         img = new DeprecatedSharedRGBImage(GetForwarder());
         return img.forget();
 #ifdef MOZ_WIDGET_GONK
-      case GONK_IO_SURFACE:
-        img = new GonkIOSurfaceImage();
-        return img.forget();
       case GRALLOC_PLANAR_YCBCR:
-        img = new GrallocPlanarYCbCrImage();
+        img = new GrallocImage();
         return img.forget();
 #endif
     }
   }
   return nullptr;
 }
 
 }
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -267,30 +267,32 @@ AsyncPanZoomController::ReceiveInputEven
 
   switch (aEvent.eventStructType) {
   case NS_TOUCH_EVENT: {
     nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aOutEvent);
     const nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches;
     for (uint32_t i = 0; i < touches.Length(); ++i) {
       nsIDOMTouch* touch = touches[i];
       if (touch) {
-        CSSPoint refPoint = WidgetSpaceToCompensatedViewportSpace(
+        CSSPoint refCSSPoint = WidgetSpaceToCompensatedViewportSpace(
           ScreenPoint::FromUnknownPoint(gfx::Point(
             touch->mRefPoint.x, touch->mRefPoint.y)),
           currentResolution);
+        LayoutDevicePoint refPoint = refCSSPoint * mFrameMetrics.mDevPixelsPerCSSPixel;
         touch->mRefPoint = nsIntPoint(refPoint.x, refPoint.y);
       }
     }
     break;
   }
   default: {
-    CSSPoint refPoint = WidgetSpaceToCompensatedViewportSpace(
+    CSSPoint refCSSPoint = WidgetSpaceToCompensatedViewportSpace(
       ScreenPoint::FromUnknownPoint(gfx::Point(
         aOutEvent->refPoint.x, aOutEvent->refPoint.y)),
       currentResolution);
+    LayoutDevicePoint refPoint = refCSSPoint * mFrameMetrics.mDevPixelsPerCSSPixel;
     aOutEvent->refPoint = nsIntPoint(refPoint.x, refPoint.y);
     break;
   }
   }
 
   return status;
 }
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'thebes'
 
 EXPORTS += [
     'CopyableCanvasLayer.h',
     'D3D9SurfaceImage.h',
     'FrameMetrics.h',
-    'GonkIOSurfaceImage.h',
+    'GrallocImages.h',
     'ImageContainer.h',
     'ImageLayers.h',
     'ImageTypes.h',
     'LayerSorter.h',
     'LayerTreeInvalidation.h',
     'Layers.h',
     'LayersLogging.h',
     'ReadbackLayer.h',
@@ -161,17 +161,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
 # has full system permissions there.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     EXPORTS.mozilla.layers += [
         'ipc/ShadowLayerUtilsGralloc.h',
     ]
     CPP_SOURCES += [
         'ShadowLayerUtilsGralloc.cpp',
         'GrallocImages.cpp',
-        'GonkIOSurfaceImage.cpp',
     ]
 
 CPP_SOURCES += [
     'AsyncCompositionManager.cpp',
     'AsyncPanZoomController.cpp',
     'Axis.cpp',
     'BasicCanvasLayer.cpp',
     'BasicColorLayer.cpp',
--- a/gfx/skia/Makefile.in
+++ b/gfx/skia/Makefile.in
@@ -54,17 +54,17 @@ VPATH += \
 	$(srcdir)/src/effects \
 	$(srcdir)/src/effects/gradients \
 	$(srcdir)/src/utils \
 	$(srcdir)/src/utils/android \
 	$(srcdir)/src/utils/mac \
 	$(srcdir)/src/sfnt \
 	$(NULL)
 
-ifeq (android,$(MOZ_WIDGET_TOOLKIT))
+ifeq ($(MOZ_WIDGET_TOOLKIT),$(findstring $(MOZ_WIDGET_TOOLKIT),android gonk))
 OS_CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(CAIRO_FT_CFLAGS)
 DEFINES += -DSK_FONTHOST_CAIRO_STANDALONE=0
 endif
 
 ifeq (gtk2,$(MOZ_WIDGET_TOOLKIT))
 OS_CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_PANGO_CFLAGS) $(CAIRO_FT_CFLAGS)
 endif
 
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -202,24 +202,19 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt
             'include/ports/SkTypeface_cairo.h',
         ]
         CPP_SOURCES += [
             'SkThread_pthread.cpp',
             'SkThreadUtils_pthread.cpp',
             'SkThreadUtils_pthread_linux.cpp',
             'SkTime_Unix.cpp',
         ]
-elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
-    CPP_SOURCES += [
-        'SkThread_pthread.cpp',
-    ]
-
 # Separate 'if' from above, since the else below applies to all != 'android'
 # toolkits.
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'):
     EXPORTS.skia += [
         'include/ports/SkTypeface_cairo.h',
     ]
     CPP_SOURCES += [