Merge m-c to f-t
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 24 Jan 2015 09:10:16 -0800
changeset 225539 9860f0457f9f42a62756e7ad2a48eb33868449e5
parent 225538 7e64e2068d8abf5a33a3f44f512dfde19900a73a (current diff)
parent 225534 7148aa99ad67af6c8f14466c5577f8ec406b3c6a (diff)
child 225546 0666fb58ab632a7a5553bf769f3b3ef7030b7371
push id28166
push userryanvm@gmail.com
push dateSun, 25 Jan 2015 00:23:32 +0000
treeherdermozilla-central@0666fb58ab63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to f-t
dom/webidl/InstallPhaseEvent.webidl
security/manager/ssl/public/nsIStreamCipher.idl
security/manager/ssl/src/nsStreamCipher.cpp
security/manager/ssl/src/nsStreamCipher.h
testing/web-platform/meta/FileAPI/Blob-close.html.ini
testing/web-platform/meta/FileAPI/Blob-constructor.html.ini
testing/web-platform/meta/FileAPI/Blob-slice.html.ini
testing/web-platform/meta/FileAPI/BlobURL/test1_manual.html.ini
testing/web-platform/meta/FileAPI/BlobURL/test2_manual.html.ini
testing/web-platform/meta/FileAPI/BlobURL/test3_manual.html.ini
testing/web-platform/meta/FileAPI/File-constructor.html.ini
testing/web-platform/meta/FileAPI/FileReader-interface/filereader_abort.html.ini
testing/web-platform/meta/FileAPI/FileReader/test_errors_manual.html.ini
testing/web-platform/meta/XMLHttpRequest/withcredentials-set.htm.ini
testing/web-platform/meta/XMLHttpRequest/withcredentials-wrong-state.htm.ini
testing/web-platform/meta/selectors-api/tests/submissions/Opera/Element-matches.html.ini
testing/web-platform/meta/selectors-api/tests/submissions/Opera/ParentNode-find-findAll.html.ini
testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/onmessage.html.ini
testing/web-platform/tests/FileAPI/Blob-XHR-revoke.html
testing/web-platform/tests/FileAPI/Blob-close.html
testing/web-platform/tests/FileAPI/Blob-constructor.html
testing/web-platform/tests/FileAPI/Blob-slice.html
testing/web-platform/tests/FileAPI/BlobURL/test1_manual.html
testing/web-platform/tests/FileAPI/BlobURL/test2_manual.html
testing/web-platform/tests/FileAPI/BlobURL/test3_manual.html
testing/web-platform/tests/FileAPI/File-constructor.html
testing/web-platform/tests/FileAPI/FileReader-interface/.gitkeep
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_abort.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_error.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_file-manual.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_file_img-manual.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_readAsArrayBuffer.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_readAsDataURL.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_readAsText.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_readystate.html
testing/web-platform/tests/FileAPI/FileReader-interface/filereader_result.html
testing/web-platform/tests/FileAPI/FileReader-interface/support/blue-100x100.png
testing/web-platform/tests/FileAPI/FileReader/Determining-Encoding.html
testing/web-platform/tests/FileAPI/FileReader/test_errors_manual.html
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/enum.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/module.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/nonnullableany.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/nonnullableobjects.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/raises.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/scopedname.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/sequenceAsAttribute.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/special-omittable.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/invalid/idl/stringconstants.idl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/readme.txt
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/allowany.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/array.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/attributes.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/callback.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/caller.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/constants.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/constructor.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/dictionary-inherits.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/dictionary.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/documentation-dos.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/documentation.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/enum.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/equivalent-decl.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/exception-inheritance.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/exception.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/getter-setter.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/identifier-qualified-names.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/implements.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/indexed-properties.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/inherits-getter.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/interface-inherits.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/iterator.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/namedconstructor.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/nointerfaceobject.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/nullable.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/nullableobjects.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/operation-optional-arg.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/overloading.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/overridebuiltins.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/partial-interface.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/primitives.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/prototyperoot.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/putforwards.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/reg-operations.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/replaceable.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/sequence.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/serializer.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/static.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/stringifier-attribute.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/stringifier-custom.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/stringifier.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/treatasnull.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/treatasundefined.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/typedef.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/typesuffixes.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/uniontype.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/idl/variadic-operations.widl
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/allowany.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/array.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/attributes.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/callback.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/caller.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/constants.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/constructor.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/dictionary-inherits.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/dictionary.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/documentation-dos.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/documentation.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/enum.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/equivalent-decl.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/exception-inheritance.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/exception.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/getter-setter.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/identifier-qualified-names.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/implements.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/indexed-properties.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/inherits-getter.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/interface-inherits.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/iterator.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/module.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/namedconstructor.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/namespaceobject.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/nointerfaceobject.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/nullable.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/nullableobjects.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/operation-optional-arg.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/overloading.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/overridebuiltins.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/partial-interface.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/primitives.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/prototyperoot.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/putforwards.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/reg-operations.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/replaceable.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/sequence.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/serializer.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/special-omittable.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/static.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/stringifier-attribute.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/stringifier-custom.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/stringifier.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/treatasnull.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/treatasundefined.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/typedef.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/typesuffixes.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/uniontype.widlprocxml
testing/web-platform/tests/WebIDL/tests/submissions/W3C/valid/xml/variadic-operations.widlprocxml
testing/web-platform/tests/XMLHttpRequest/withcredentials-set.htm
testing/web-platform/tests/XMLHttpRequest/withcredentials-wrong-state.htm
testing/web-platform/tests/conformance-checkers/html/elements/picture/build.py
testing/web-platform/tests/conformance-checkers/html/media-queries/generate.py
testing/web-platform/tests/conformance-checkers/html/mime-types/generate.py
testing/web-platform/tests/conformance-checkers/html/tools/ins-del-datetime.py
testing/web-platform/tests/conformance-checkers/html/tools/meta-extensions
testing/web-platform/tests/conformance-checkers/html/tools/meta-name.py
testing/web-platform/tests/conformance-checkers/html/tools/url.py
testing/web-platform/tests/selectors-api/tests/submissions/Opera/Element-matches.html
testing/web-platform/tests/selectors-api/tests/submissions/Opera/Element-matches.js
testing/web-platform/tests/selectors-api/tests/submissions/Opera/ParentNode-find-findAll.html
testing/web-platform/tests/selectors-api/tests/submissions/Opera/ParentNode-find-findAll.js
testing/web-platform/tests/selectors-api/tests/submissions/Opera/level2-lib.js
testing/web-platform/tests/workers/WorkerGlobalScope_EventTarget.htm
testing/web-platform/tests/workers/WorkerGlobalScope_XMLHttpRequest.htm
testing/web-platform/tests/workers/WorkerGlobalScope_addEventListener.htm
testing/web-platform/tests/workers/WorkerGlobalScope_nested_Worker.htm
testing/web-platform/tests/workers/WorkerGlobalScope_removeEventListener.htm
testing/web-platform/tests/workers/interfaces/DedicatedWorkerGlobalScope/onmessage.html
testing/web-platform/tests/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/return-value.html
testing/web-platform/tests/workers/support/EventListener.js
testing/web-platform/tests/workers/support/WorkerNested.js
testing/web-platform/tests/workers/support/XMLHttpRequest.js
testing/web-platform/tests/workers/support/XMLHttpRequest.txt
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bugs 1101331 - See if a CLOBBER helps the situation any.
+bug 1114669 removes nsIStreamCipher.idl, which requires a clobber according to bug 1114669
--- a/accessible/jsat/TraversalRules.jsm
+++ b/accessible/jsat/TraversalRules.jsm
@@ -97,17 +97,18 @@ var gSimpleTraversalRoles =
    Roles.HEADER,
    Roles.HEADING,
    Roles.SLIDER,
    Roles.SPINBUTTON,
    Roles.OPTION,
    Roles.LISTITEM,
    Roles.GRID_CELL,
    Roles.COLUMNHEADER,
-   Roles.ROWHEADER];
+   Roles.ROWHEADER,
+   Roles.STATUSBAR];
 
 var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
   // An object is simple, if it either has a single child lineage,
   // or has a flat subtree.
   function isSingleLineage(acc) {
     for (let child = acc; child; child = child.firstChild) {
       if (Utils.visibleChildCount(child) > 1) {
         return false;
@@ -146,16 +147,17 @@ var gSimpleMatchFunc = function gSimpleM
     return Utils.isListItemDecorator(aAccessible) ?
       Filters.IGNORE : Filters.MATCH;
   case Roles.GRAPHIC:
     return TraversalRules._shouldSkipImage(aAccessible);
   case Roles.HEADER:
   case Roles.HEADING:
   case Roles.COLUMNHEADER:
   case Roles.ROWHEADER:
+  case Roles.STATUSBAR:
     if ((aAccessible.childCount > 0 || aAccessible.name) &&
         (isSingleLineage(aAccessible) || isFlatSubtree(aAccessible))) {
       return Filters.MATCH | Filters.IGNORE_SUBTREE;
     }
     return Filters.IGNORE;
   case Roles.GRID_CELL:
     return isSingleLineage(aAccessible) || isFlatSubtree(aAccessible) ?
       Filters.MATCH | Filters.IGNORE_SUBTREE : Filters.IGNORE;
--- a/accessible/tests/mochitest/jsat/doc_traversal.html
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -136,10 +136,12 @@
     </tfoot>
     <tbody>
       <tr>
         <td>Dirt</td>
         <td>Messy Stuff</td>
       </tr>
     </tbody>
   </table>
+  <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+  <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_output.html
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -472,16 +472,27 @@ https://bugzilla.mozilla.org/show_bug.cg
         }, {
           accOrElmOrID: "labelled-combobox",
           expectedUtterance: [[{"string": "stateCollapsed"},
             {"string": "stateHasPopup"}, {"string": "combobox"}, "Never",
             "Intervals"], ["Intervals", "Never", {"string": "stateCollapsed"},
             {"string": "stateHasPopup"}, {"string": "combobox"}]],
           expectedBraille: [[{"string": "comboboxAbbr"}, "Never", "Intervals"],
             ["Intervals", "Never", {"string": "comboboxAbbr"}]]
+        }, {
+          accOrElmOrID: "statusbar-1",
+          expectedUtterance: [["Last sync:", "2 days ago"],
+                              ["Last sync:", "2 days ago"]],
+          expectedBraille: [["Last sync:", "2 days ago"],
+                            ["Last sync:", "2 days ago"]]
+        }, {
+          accOrElmOrID: "statusbar-2",
+          expectedUtterance: [["Last sync: 30min ago"],
+                              ["Last sync: 30min ago"]],
+          expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
         }];
 
         // Test all possible utterance order preference values.
         tests.forEach(function run(test) {
           var utteranceOrderValues = [0, 1];
           utteranceOrderValues.forEach(
             function testUtteranceOrder(utteranceOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
@@ -627,11 +638,13 @@ https://bugzilla.mozilla.org/show_bug.cg
         <option id="combobox-option" value="30">30 min</option>
         <option value="null">Manual</option>
       </select>
       <select id="labelled-combobox" aria-label="Intervals">
         <option value="15">Every 15 min</option>
         <option value="30">Every 30 min</option>
         <option value="null" selected>Never</option>
       </select>
+      <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+      <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
     </div>
   </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_traversal.html
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -117,17 +117,17 @@
                               '4. Standard Lisp', 'link-0', ' Lisp',
                               'checkbox-1-5', ' LeLisp', '• JavaScript',
                               'heading-5', 'image-2', 'image-3',
                               'Not actually an image', 'link-1', 'anchor-1',
                               'link-2', 'anchor-2', 'link-3', '3', '1', '4',
                               '1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
                               '5 8', 'gridcell4', 'Just an innocuous separator',
                               'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
-                              'Dirt', 'Messy Stuff']);
+                              'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2']);
 
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function () {
       /* We open a new browser because we need to test with a top-level content
          document. */
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -188,17 +188,18 @@ const WorkerSandbox = Class({
 
     // Handle messages send by this script:
     setListeners(this, console);
 
     // Inject `addon` global into target document if document is trusted,
     // `addon` in document is equivalent to `self` in content script.
     if (requiresAddonGlobal(worker)) {
       Object.defineProperty(getUnsafeWindow(window), 'addon', {
-          value: content.self
+          value: content.self,
+          configurable: true
         }
       );
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
     if (!getTabForContentWindow(window)) {
--- a/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
+++ b/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
@@ -233,17 +233,18 @@ const WorkerSandbox = EventEmitter.compo
       }
     });
 
     // Inject `addon` global into target document if document is trusted,
     // `addon` in document is equivalent to `self` in content script.
     if (worker._injectInDocument) {
       let win = window.wrappedJSObject ? window.wrappedJSObject : window;
       Object.defineProperty(win, "addon", {
-          value: content.self
+          value: content.self,
+          configurable: true
         }
       );
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
     if (!getTabForContentWindow(window)) {
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
@@ -111,17 +111,17 @@
   <project name="platform/libcore" path="libcore" revision="e195beab082c09217318fc19250caeaf4c1bd800"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="feeb36c2bd4adfe285f98f5de92e0f3771b2c115"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="0e7c060db684b409616fe67ea433ef19f5634c60"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="dcb7c6ba2c6aec23a76d95094c4cbc17eeca5d44"/>
   <project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
   <project name="platform/system/media" path="system/media" revision="7ff72c2ea2496fa50b5e8a915e56e901c3ccd240"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
   <project name="platform/system/netd" path="system/netd" revision="3ae56364946d4a5bf5a5f83f12f9a45a30398e33"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="2e43efe1b30d0b98574d293059556aebd2f46454"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -113,17 +113,17 @@
   <project name="platform/libnativehelper" path="libnativehelper" revision="4792069e90385889b0638e97ae62c67cdf274e22"/>
   <project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
   <project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="02c32feb2fe97037be0ac4dace3a6a5025ac895d"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="dcb7c6ba2c6aec23a76d95094c4cbc17eeca5d44"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
   <project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
@@ -111,17 +111,17 @@
   <project name="platform/libcore" path="libcore" revision="9877ade9617bb0db6e59aa2a54719a9bc92600f3"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="46c96ace65eb1ccab05bf15b9bf8e53e443039af"/>
   <project name="platform/ndk" path="ndk" revision="cb5519af32ae7b4a9c334913a612462ecd04c5d0"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="0e7c060db684b409616fe67ea433ef19f5634c60"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="6aa61f8557a22039a30b42b7f283996381fd625d"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="dcb7c6ba2c6aec23a76d95094c4cbc17eeca5d44"/>
   <project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
   <project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
   <project name="platform/system/netd" path="system/netd" revision="36704b0da24debcab8090156568ac236315036bb"/>
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="f3cedd7fd9b1649aa5107d466be9078bb7602af6"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e06971db7acf7a35c32eb74d675a4e12e288e6be">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
@@ -106,17 +106,17 @@
   <project name="platform/libcore" path="libcore" revision="baf7d8068dd501cfa338d3a8b1b87216d6ce0571"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="50c4430e32849530ced32680fd6ee98963b3f7ac"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="0e7c060db684b409616fe67ea433ef19f5634c60"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="dcb7c6ba2c6aec23a76d95094c4cbc17eeca5d44"/>
   <project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
   <project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
   <project name="platform/external/curl" path="external/curl" revision="e68addd988448959ea8157c5de637346b4180c33"/>
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/system/media" path="system/media" revision="c1332c21c608f4932a6d7e83450411cde53315ef"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "f74795eade46f4613741dd9e16fc43a905b40d2b", 
+        "git_revision": "21bf307d0fdbbcc346f565850a682f3463be7039", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "a0f2cafbae71c95e9bc97d253ecacbc079e1e138", 
+    "revision": "460e395536ecfcca43ba76d58fb55df1b489df4d", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,21 +12,21 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -113,17 +113,17 @@
   <project name="platform/libnativehelper" path="libnativehelper" revision="4792069e90385889b0638e97ae62c67cdf274e22"/>
   <project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
   <project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="02c32feb2fe97037be0ac4dace3a6a5025ac895d"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="d3e2de81952c45d6ed658cdf367a6e7283b9c3ce"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="dcb7c6ba2c6aec23a76d95094c4cbc17eeca5d44"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
   <project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8fcd25d64f0f67d1a6f7037a4c83ce6d95466770"/>
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f74795eade46f4613741dd9e16fc43a905b40d2b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21bf307d0fdbbcc346f565850a682f3463be7039"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c6ca06a0b2f8046c7db82b15268092b164e823a"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -211,16 +211,20 @@ toolbar[customizing] > .overflow-button 
 #main-window[inFullscreen] > #titlebar,
 #main-window[inFullscreen] .titlebar-placeholder,
 #main-window:not([tabsintitlebar]) .titlebar-placeholder {
   display: none;
 }
 
 #titlebar {
   -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
+
+%ifdef XP_MACOSX
+  -moz-window-dragging: drag;
+%endif
 }
 
 #titlebar-spacer {
   pointer-events: none;
 }
 
 #main-window[tabsintitlebar] #titlebar-buttonbox {
   position: relative;
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -2388,29 +2388,34 @@ DebuggerController.Breakpoints.DOM = new
 DebuggerController.Tracer = new Tracer();
 DebuggerController.HitCounts = new HitCounts();
 
 /**
  * Export some properties to the global scope for easier access.
  */
 Object.defineProperties(window, {
   "gTarget": {
-    get: function() DebuggerController._target
+    get: function() DebuggerController._target,
+    configurable: true
   },
   "gHostType": {
-    get: function() DebuggerView._hostType
+    get: function() DebuggerView._hostType,
+    configurable: true
   },
   "gClient": {
-    get: function() DebuggerController.client
+    get: function() DebuggerController.client,
+    configurable: true
   },
   "gThreadClient": {
-    get: function() DebuggerController.activeThread
+    get: function() DebuggerController.activeThread,
+    configurable: true
   },
   "gCallStackPageSize": {
-    get: function() CALL_STACK_PAGE_SIZE
+    get: function() CALL_STACK_PAGE_SIZE,
+    configurable: true
   }
 });
 
 /**
  * Helper method for debugging.
  * @param string
  */
 function dumpn(str) {
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -810,17 +810,18 @@ EventEmitter.decorate(this);
 NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
 NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
 
 /**
  * Export some properties to the global scope for easier access.
  */
 Object.defineProperties(window, {
   "gNetwork": {
-    get: function() NetMonitorController.NetworkEventsHandler
+    get: function() NetMonitorController.NetworkEventsHandler,
+    configurable: true
   }
 });
 
 /**
  * Makes sure certain properties are available on all objects in a data store.
  *
  * @param array aDataStore
  *        A list of objects for which to check the availability of properties.
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -70,20 +70,16 @@
 
 #main-window {
   -moz-appearance: none;
   background-color: #eeeeee;
 }
 
 /** Begin titlebar **/
 
-#titlebar {
-  -moz-window-dragging: drag;
-}
-
 #titlebar-buttonbox > .titlebar-button {
   display: none;
 }
 
 /* NB: these would be -moz-margin-start/end if it wasn't for the fact that OS X
  * doesn't reverse the order of the items in the titlebar in RTL mode. */
 .titlebar-placeholder[type="caption-buttons"],
 #titlebar-buttonbox {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13182,17 +13182,17 @@ nsDocShell::OnLinkClick(nsIContent* aCon
                         const char16_t* aTargetSpec,
                         const nsAString& aFileName,
                         nsIInputStream* aPostDataStream,
                         nsIInputStream* aHeadersDataStream,
                         bool aIsTrusted)
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
-  if (!IsOKToLoadURI(aURI)) {
+  if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
     return NS_OK;
   }
 
   // On history navigation through Back/Forward buttons, don't execute
   // automatic JavaScript redirection such as |anchorElement.click()| or
   // |formElement.submit()|.
   //
   // XXX |formElement.submit()| bypasses this checkpoint because it calls
@@ -13238,17 +13238,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nullptr;
   }
   if (aRequest) {
     *aRequest = nullptr;
   }
 
-  if (!IsOKToLoadURI(aURI)) {
+  if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
     return NS_OK;
   }
 
   // XXX When the linking node was HTMLFormElement, it is synchronous event.
   //     That is, the caller of this method is not |OnLinkClickEvent::Run()|
   //     but |HTMLFormElement::SubmitSubmission(...)|.
   if (nsGkAtoms::form == aContent->Tag() && ShouldBlockLoadingForBackButton()) {
     return NS_OK;
--- a/docshell/test/browser/browser_onbeforeunload_navigation.js
+++ b/docshell/test/browser/browser_onbeforeunload_navigation.js
@@ -108,16 +108,39 @@ var testFns = [
   function(e) {
     e.target.location.reload();
     return "stop";
   },
   function(e) {
     e.target.location.replace('otherpage-location-replaced.html');
     return "stop";
   },
+  function(e) {
+    var link = e.target.createElement('a');
+    link.href = "otherpage.html";
+    e.target.body.appendChild(link);
+    link.click();
+    return "stop";
+  },
+  function(e) {
+    var link = e.target.createElement('a');
+    link.href = "otherpage.html";
+    link.setAttribute("target", "_blank");
+    e.target.body.appendChild(link);
+    link.click();
+    return "stop";
+  },
+  function(e) {
+    var link = e.target.createElement('a');
+    link.href = e.target.location.href;
+    e.target.body.appendChild(link);
+    link.setAttribute("target", "somearbitrarywindow");
+    link.click();
+    return "stop";
+  },
 ];
 
 function runNextTest() {
   currentTest++;
   if (currentTest >= testFns.length) {
     if (!stayingOnPage) {
       finish();
       return;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -782,16 +782,24 @@ nsOuterWindowProxy::defineProperty(JSCon
   if (IsArrayIndex(index)) {
     // Spec says to Reject whether this is a supported index or not,
     // since we have no indexed setter or indexed creator.  That means
     // throwing in strict mode (FIXME: Bug 828137), doing nothing in
     // non-strict mode.
     return true;
   }
 
+  // For now, allow chrome code to define non-configurable properties
+  // on windows, until we sort out what exactly the addon SDK is
+  // doing.  In the meantime, this still allows us to test web compat
+  // behavior.
+  if (false && desc.isPermanent() && !nsContentUtils::IsCallerChrome()) {
+    return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW);
+  }
+
   return js::Wrapper::defineProperty(cx, proxy, id, desc);
 }
 
 bool
 nsOuterWindowProxy::ownPropertyKeys(JSContext *cx,
                                     JS::Handle<JSObject*> proxy,
                                     JS::AutoIdVector &props) const
 {
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -251,16 +251,17 @@ skip-if = buildapp == 'mulet'
 [test_bug913761.html]
 [test_bug976673.html]
 [test_bug978522.html]
 [test_bug979109.html]
 [test_bug989665.html]
 [test_bug999456.html]
 [test_bug1022229.html]
 [test_bug1043106.html]
+[test_bug1060938.html]
 [test_bug1064481.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_consoleEmptyStack.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_dialogArguments.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s
 [test_document.all_unqualified.html]
@@ -762,8 +763,10 @@ support-files = file_bug503473-frame.sjs
 skip-if = buildapp == 'b2g' || e10s
 support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
 [test_bug1025933.html]
 [test_element.matches.html]
 [test_user_select.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1081686.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
+[test_window_define_nonconfigurable.html]
+skip-if = true # bug 1107443 - code for newly-added test was disabled
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1060938.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1060938
+-->
+  <head>
+    <meta charset="utf-8">
+    <title> Test for Bug 1060938 </title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"> </script>
+    <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"> </script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  </head>
+  <body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1060938"> Mozilla Bug 1060938 </a>
+  <p id="display"></p>
+
+  <pre id="test">
+  <script type="application/javascript">
+
+    /** Test for Bug 1060938 **/
+    // test: Element.removeAttributeNode()
+
+    parent = document.getElementsByTagName("p")[0];
+    parent.setAttributeNS("www.test1.com", "ID", "Test1");
+    parent.setAttributeNS("www.test2.com", "Class", "Test2");
+    parent.setAttribute("id", "www.test3.com");
+    parent.className = "www.test4.com";
+
+    allAttributes = parent.attributes;
+
+    function removeAttr(iter){
+      var removed_attribute = allAttributes[0];
+      is(removed_attribute, parent.removeAttributeNode(removed_attribute),
+          "(" + iter + ")" + " Returned attribute and remove attribute should be same.");
+    }
+
+    removeAttr(1);
+    removeAttr(2);
+    removeAttr(3);
+    removeAttr(4);
+
+  </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_define_nonconfigurable.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1107443
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1107443</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1107443 **/
+  try {
+    Object.defineProperty(window, "nosuchprop", { value: 5 });
+    throw "didn't throw";
+  } catch (e) {
+    ise(e instanceof TypeError, true,
+        "defineProperty(window) with a non-configurable property should " +
+        "throw a TypeError, instead got: " + e);
+    ise(Object.getOwnPropertyDescriptor(window, "nosuchprop"), undefined,
+        'Window should not have property after an attempt to define it failed');
+  }
+
+  Object.defineProperty(window, "nosuchprop", { value: 7, configurable: true });
+  var desc = Object.getOwnPropertyDescriptor(window, "nosuchprop");
+  ise(typeof(desc), "object", "Should have a property now");
+  ise(desc.configurable, true, "Property should be configurable");
+  ise(desc.writable, false, "Property should be readonly");
+  ise(desc.value, 7, "Property should have the right value");
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107443">Mozilla Bug 1107443</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -404,16 +404,21 @@ DOMInterfaces = {
 
 'Exception': {
     'headerFile': 'mozilla/dom/DOMException.h',
     'binaryNames': {
         'message': 'messageMoz',
     },
 },
 
+'ExtendableEvent': {
+    'headerFile': 'mozilla/dom/ServiceWorkerEvents.h',
+    'nativeType': 'mozilla::dom::workers::ExtendableEvent',
+},
+
 'FileList': {
     'headerFile': 'mozilla/dom/File.h',
 },
 
 'FileReader': {
     'nativeType': 'nsDOMFileReader',
     'implicitJSContext': [ 'readAsArrayBuffer' ],
 },
@@ -600,21 +605,16 @@ DOMInterfaces = {
 {
     'nativeType': 'nsIInputStream',
     'notflattened': True
 },
 {
     'workers': True,
 }],
 
-'InstallPhaseEvent': {
-    'headerFile': 'ServiceWorkerEvents.h',
-    'nativeType': 'mozilla::dom::workers::InstallPhaseEvent',
-},
-
 'InstallEvent': {
     'headerFile': 'ServiceWorkerEvents.h',
     'nativeType': 'mozilla::dom::workers::InstallEvent',
 },
 
 'KeyEvent': {
     'concrete': False
 },
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -58,8 +58,11 @@ MSG_DEF(MSG_HEADERS_IMMUTABLE, 0, "Heade
 MSG_DEF(MSG_INVALID_HEADER_NAME, 1, "{0} is an invalid header name.")
 MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, "{0} is an invalid header value.")
 MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, "Headers require name/value tuples when being initialized by a sequence.")
 MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.")
 MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.")
 MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, "Invalid request method {0}.")
 MSG_DEF(MSG_REQUEST_BODY_CONSUMED_ERROR, 0, "Request body has already been consumed.")
 MSG_DEF(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR, 0, "Response statusText may not contain newline or carriage return.")
+MSG_DEF(MSG_FETCH_FAILED, 0, "NetworkError when attempting to fetch resource.")
+MSG_DEF(MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD, 0, "HEAD or GET Request cannot have a body.")
+MSG_DEF(MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW, 0, "Not allowed to define a non-configurable property on the WindowProxy object")
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -181,16 +181,28 @@ GetCurrentJSStack()
   if (!stack) {
     return nullptr;
   }
 
   // Note that CreateStack only returns JS frames, so we're done here.
   return stack.forget();
 }
 
+AutoForceSetExceptionOnContext::AutoForceSetExceptionOnContext(JSContext* aCx)
+  : mCx(aCx)
+{
+  mOldValue = JS::ContextOptionsRef(mCx).autoJSAPIOwnsErrorReporting();
+  JS::ContextOptionsRef(mCx).setAutoJSAPIOwnsErrorReporting(true);
+}
+
+AutoForceSetExceptionOnContext::~AutoForceSetExceptionOnContext()
+{
+  JS::ContextOptionsRef(mCx).setAutoJSAPIOwnsErrorReporting(mOldValue);
+}
+
 namespace exceptions {
 
 class StackFrame : public nsIStackFrame
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(StackFrame)
   NS_DECL_NSISTACKFRAME
--- a/dom/bindings/Exceptions.h
+++ b/dom/bindings/Exceptions.h
@@ -39,16 +39,29 @@ ThrowExceptionObject(JSContext* aCx, nsI
 // Create an exception object for the given nsresult and message but
 // don't set it pending on aCx.  This never returns null.
 already_AddRefed<Exception>
 CreateException(JSContext* aCx, nsresult aRv, const char* aMessage = nullptr);
 
 already_AddRefed<nsIStackFrame>
 GetCurrentJSStack();
 
+// Throwing a TypeError on an ErrorResult may result in SpiderMonkey using its
+// own error reporting mechanism instead of just setting the exception on the
+// context.  This happens if no script is running. Bug 1107777 adds a flag that
+// forcibly turns this behaviour off. This is a stack helper to set the flag.
+class MOZ_STACK_CLASS AutoForceSetExceptionOnContext {
+private:
+  JSContext* mCx;
+  bool mOldValue;
+public:
+  explicit AutoForceSetExceptionOnContext(JSContext* aCx);
+  ~AutoForceSetExceptionOnContext();
+};
+
 // Internal stuff not intended to be widely used.
 namespace exceptions {
 
 // aMaxDepth can be used to define a maximal depth for the stack trace. If the
 // value is -1, a default maximal depth will be selected.
 already_AddRefed<nsIStackFrame>
 CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
 
--- a/dom/bindings/ToJSValue.cpp
+++ b/dom/bindings/ToJSValue.cpp
@@ -61,16 +61,17 @@ ToJSValue(JSContext* aCx,
 }
 
 bool
 ToJSValue(JSContext* aCx,
           ErrorResult& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   MOZ_ASSERT(aArgument.Failed());
+  AutoForceSetExceptionOnContext forceExn(aCx);
   DebugOnly<bool> throwResult = ThrowMethodFailedWithDetails(aCx, aArgument, "", "");
   MOZ_ASSERT(!throwResult);
   DebugOnly<bool> getPendingResult = JS_GetPendingException(aCx, aValue);
   MOZ_ASSERT(getPendingResult);
   JS_ClearPendingException(aCx);
   return true;
 }
 
--- a/dom/bindings/test/test_barewordGetsWindow.html
+++ b/dom/bindings/test/test_barewordGetsWindow.html
@@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     var desc = Object.getOwnPropertyDescriptor(frames[0], "document");
     if (!desc || !desc.get) {
       todo(false, "This test does nothing so far, but will once Window is on WebIDL bindings");
       SimpleTest.finish();
       return;
     }
     get = desc.get;
     ok(get, "Couldn't find document getter");
-    Object.defineProperty(frames[0], "foo", { get: get });
+    Object.defineProperty(frames[0], "foo", { get: get, configurable: true });
 
     var barewordFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = foo; return doc.documentElement; })");
     var qualifiedFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = window.document; return doc.documentElement; })");
     document.querySelector("iframe").onload = function () {
       // interp
       is(barewordFunc(1).textContent, "OLD", "Bareword should see own inner 1");
       is(qualifiedFunc(1).textContent, "NEW",
          "Qualified should see current inner 1");
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -61,16 +61,37 @@ GetOrigin(nsIPrincipal* aPrincipal, nsAS
   if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
     nsAutoString tmp;
     aRv = nsContentUtils::GetUTFOrigin(aPrincipal, tmp);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
     aOrigin = tmp;
+    if (aOrigin.EqualsASCII("null")) {
+      nsCOMPtr<nsIURI> uri;
+      aRv = aPrincipal->GetURI(getter_AddRefs(uri));
+      if (NS_WARN_IF(aRv.Failed())) {
+        return;
+      }
+
+      if (NS_WARN_IF(!uri)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+
+      nsAutoCString spec;
+      aRv = uri->GetSpec(spec);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return;
+      }
+
+      aOrigin = NS_ConvertUTF8toUTF16(spec);
+    }
+
     return;
   }
 
   uint32_t appId = aPrincipal->GetAppId();
 
   // If we are in "app code", use manifest URL as unique origin since
   // multiple apps can share the same origin but not same broadcast messages.
   nsresult rv;
@@ -433,16 +454,19 @@ BroadcastChannel::Constructor(const Glob
     }
 
     if (NS_WARN_IF(isNullPrincipal)) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     GetOrigin(principal, origin, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
 
     aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
     nsIDocument* doc = window->GetExtantDoc();
     // No bfcache when BroadcastChannel is used.
--- a/dom/canvas/test/_webgl-conformance.ini
+++ b/dom/canvas/test/_webgl-conformance.ini
@@ -520,16 +520,17 @@ skip-if = (os == 'b2g')
 [webgl-conformance/_wrappers/test_conformance__extensions__ext-texture-filter-anisotropic.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__oes-texture-float.html]
 fail-if = (os == 'linux')
 [webgl-conformance/_wrappers/test_conformance__extensions__oes-vertex-array-object.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-renderer-info.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-shaders.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-etc1.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
+fail-if = (os == 'mac' && os_version == '10.10')
 [webgl-conformance/_wrappers/test_conformance__extensions__ext-sRGB.html]
 [webgl-conformance/_wrappers/test_conformance__extensions__ext-shader-texture-lod.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function-abs.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function-acos.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function-asin.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function-atan.html]
 [webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function-atan-xy.html]
@@ -699,16 +700,17 @@ fail-if = (os == 'linux')
 [webgl-conformance/_wrappers/test_conformance__misc__error-reporting.html]
 [webgl-conformance/_wrappers/test_conformance__misc__instanceof-test.html]
 [webgl-conformance/_wrappers/test_conformance__misc__invalid-passed-params.html]
 skip-if = (os == 'android') || (os == 'b2g') || (os == 'linux')
 [webgl-conformance/_wrappers/test_conformance__misc__is-object.html]
 [webgl-conformance/_wrappers/test_conformance__misc__null-object-behaviour.html]
 [webgl-conformance/_wrappers/test_conformance__misc__functions-returning-strings.html]
 [webgl-conformance/_wrappers/test_conformance__misc__object-deletion-behaviour.html]
+fail-if = (os == 'mac' && os_version == '10.10')
 [webgl-conformance/_wrappers/test_conformance__misc__shader-precision-format.html]
 [webgl-conformance/_wrappers/test_conformance__misc__type-conversion-test.html]
 skip-if = (os == 'android') || (os == 'b2g') || (os == 'linux')
 [webgl-conformance/_wrappers/test_conformance__misc__uninitialized-test.html]
 skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__misc__webgl-specific.html]
 [webgl-conformance/_wrappers/test_conformance__programs__get-active-test.html]
 [webgl-conformance/_wrappers/test_conformance__programs__gl-bind-attrib-location-test.html]
@@ -727,26 +729,29 @@ skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__renderbuffers__renderbuffer-initialization.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__draw-arrays-out-of-bounds.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__draw-elements-out-of-bounds.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__gl-clear.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__gl-drawelements.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__gl-scissor-test.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__more-than-65536-indices.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__point-size.html]
+fail-if = (os == 'mac' && os_version == '10.10')
 [webgl-conformance/_wrappers/test_conformance__rendering__triangle.html]
 [webgl-conformance/_wrappers/test_conformance__rendering__line-loop-tri-fan.html]
+fail-if = (os == 'mac' && os_version == '10.10')
 [webgl-conformance/_wrappers/test_conformance__state__gl-enable-enum-test.html]
 [webgl-conformance/_wrappers/test_conformance__state__gl-enum-tests.html]
 [webgl-conformance/_wrappers/test_conformance__state__gl-get-calls.html]
 [webgl-conformance/_wrappers/test_conformance__state__gl-geterror.html]
 [webgl-conformance/_wrappers/test_conformance__state__gl-getstring.html]
 [webgl-conformance/_wrappers/test_conformance__state__gl-object-get-calls.html]
 [webgl-conformance/_wrappers/test_conformance__textures__compressed-tex-image.html]
 [webgl-conformance/_wrappers/test_conformance__textures__copy-tex-image-and-sub-image-2d.html]
+fail-if = (os == 'mac' && os_version == '10.10')
 [webgl-conformance/_wrappers/test_conformance__textures__gl-pixelstorei.html]
 [webgl-conformance/_wrappers/test_conformance__textures__gl-teximage.html]
 skip-if = (os == 'android') || (os == 'b2g') || (os == 'linux')
 [webgl-conformance/_wrappers/test_conformance__textures__origin-clean-conformance.html]
 [webgl-conformance/_wrappers/test_conformance__textures__tex-image-and-sub-image-2d-with-array-buffer-view.html]
 [webgl-conformance/_wrappers/test_conformance__textures__tex-image-and-sub-image-2d-with-canvas.html]
 [webgl-conformance/_wrappers/test_conformance__textures__tex-image-and-sub-image-2d-with-image-data.html]
 [webgl-conformance/_wrappers/test_conformance__textures__tex-image-and-sub-image-2d-with-image.html]
--- a/dom/canvas/test/webgl-conformance/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conformance/mochitest-errata.ini
@@ -83,14 +83,29 @@ skip-if = (os == 'b2g')
 # Failures after enabling color_buffer_[half_]float.
 fail-if = (os == 'linux')
 
 ########################################################################
 # Mac
 [_wrappers/test_conformance__canvas__drawingbuffer-static-canvas-test.html]
 # Intermittent crash on OSX.
 skip-if = os == 'mac'
+[_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
+# Fails on OS X 10.10
+fail-if = (os == 'mac' && os_version == '10.10')
+[_wrappers/test_conformance__misc__object-deletion-behaviour.html]
+# Fails on OS X 10.10
+fail-if = (os == 'mac' && os_version == '10.10')
+[_wrappers/test_conformance__rendering__line-loop-tri-fan.html]
+# Fails on OS X 10.10
+fail-if = (os == 'mac' && os_version == '10.10')
+[_wrappers/test_conformance__rendering__point-size.html]
+# Fails on OS X 10.10
+fail-if = (os == 'mac' && os_version == '10.10')
+[_wrappers/test_conformance__textures__copy-tex-image-and-sub-image-2d.html]
+# Fails on OS X 10.10
+fail-if = (os == 'mac' && os_version == '10.10')
 
 ########################################################################
 # Win
 [_wrappers/test_conformance__textures__texture-npot-video.html]
 # Intermittant crash on Windows
 skip-if = os == 'win'
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -14,16 +14,17 @@
 
 #include "nsDOMString.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/FetchDriver.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/URLSearchParams.h"
@@ -261,19 +262,25 @@ MainThreadFetchResolver::MainThreadFetch
 }
 
 void
 MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
 {
   NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
   AssertIsOnMainThread();
 
-  nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
-  mResponse = new Response(go, aResponse);
-  mPromise->MaybeResolve(mResponse);
+  if (aResponse->Type() != ResponseType::Error) {
+    nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
+    mResponse = new Response(go, aResponse);
+    mPromise->MaybeResolve(mResponse);
+  } else {
+    ErrorResult result;
+    result.ThrowTypeError(MSG_FETCH_FAILED);
+    mPromise->MaybeReject(result);
+  }
 }
 
 MainThreadFetchResolver::~MainThreadFetchResolver()
 {
   NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
 }
 
 class WorkerFetchResponseRunnable MOZ_FINAL : public WorkerRunnable
@@ -291,21 +298,28 @@ public:
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
 
-    nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
-    mResolver->mResponse = new Response(global, mInternalResponse);
+    nsRefPtr<Promise> promise = mResolver->mFetchPromise.forget();
+
+    if (mInternalResponse->Type() != ResponseType::Error) {
+      nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
+      mResolver->mResponse = new Response(global, mInternalResponse);
 
-    nsRefPtr<Promise> promise = mResolver->mFetchPromise.forget();
-    promise->MaybeResolve(mResolver->mResponse);
+      promise->MaybeResolve(mResolver->mResponse);
+    } else {
+      ErrorResult result;
+      result.ThrowTypeError(MSG_FETCH_FAILED);
+      promise->MaybeReject(result);
+    }
 
     return true;
   }
 };
 
 class WorkerFetchResponseEndRunnable MOZ_FINAL : public WorkerRunnable
 {
   nsRefPtr<WorkerFetchResolver> mResolver;
@@ -317,17 +331,16 @@ public:
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
-    MOZ_ASSERT(mResolver->mResponse);
 
     mResolver->CleanUp(aCx);
     return true;
   }
 };
 
 void
 WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
@@ -719,17 +732,17 @@ public:
     : mFetchBody(aFetchBody)
   { }
 
   NS_IMETHOD
   OnStreamComplete(nsIStreamLoader* aLoader,
                    nsISupports* aCtxt,
                    nsresult aStatus,
                    uint32_t aResultLength,
-                   const uint8_t* aResult)
+                   const uint8_t* aResult) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     // If the binding requested cancel, we don't need to call
     // ContinueConsumeBody, since that is the originator.
     if (aStatus == NS_BINDING_ABORTED) {
       return NS_OK;
     }
@@ -1061,19 +1074,19 @@ FetchBody<Derived>::ContinueConsumeBody(
   // Don't warn here since we warned above.
   if (NS_FAILED(aStatus)) {
     return;
   }
 
   // Finish successfully consuming body according to type.
   MOZ_ASSERT(aResult);
 
-  AutoJSAPI api;
-  api.Init(DerivedClass()->GetParentObject());
-  JSContext* cx = api.cx();
+  AutoJSAPI jsapi;
+  jsapi.Init(DerivedClass()->GetParentObject());
+  JSContext* cx = jsapi.cx();
 
   switch (mConsumeType) {
     case CONSUME_ARRAYBUFFER: {
       JS::Rooted<JSObject*> arrayBuffer(cx);
       arrayBuffer = JS_NewArrayBufferWithContents(cx, aResultLength, reinterpret_cast<void *>(aResult));
       if (!arrayBuffer) {
         JS_ClearPendingException(cx);
         localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
@@ -1110,23 +1123,30 @@ FetchBody<Derived>::ContinueConsumeBody(
       decoder.AppendText(reinterpret_cast<char*>(aResult), aResultLength);
 
       nsString& decoded = decoder.GetText();
       if (mConsumeType == CONSUME_TEXT) {
         localPromise->MaybeResolve(decoded);
         return;
       }
 
+      AutoForceSetExceptionOnContext forceExn(cx);
       JS::Rooted<JS::Value> json(cx);
       if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
+        if (!JS_IsExceptionPending(cx)) {
+          localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
+          return;
+        }
+
         JS::Rooted<JS::Value> exn(cx);
-        if (JS_GetPendingException(cx, &exn)) {
-          JS_ClearPendingException(cx);
-          localPromise->MaybeReject(cx, exn);
-        }
+        DebugOnly<bool> gotException = JS_GetPendingException(cx, &exn);
+        MOZ_ASSERT(gotException);
+
+        JS_ClearPendingException(cx);
+        localPromise->MaybeReject(cx, exn);
         return;
       }
 
       localPromise->MaybeResolve(cx, json);
       return;
     }
   }
 
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -311,40 +311,16 @@ FetchDriver::HttpFetch(bool aCORSFlag, b
     return rv;
   }
 
   // Step 2 deals with letting ServiceWorkers intercept requests. This is
   // handled by Necko after the channel is opened.
   // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
   // set based on the Request's flag.
 
-  // From here on we create a channel and set its properties with the
-  // information from the InternalRequest. This is an implementation detail.
-  MOZ_ASSERT(mLoadGroup);
-  nsCOMPtr<nsIChannel> chan;
-  rv = NS_NewChannel(getter_AddRefs(chan),
-                     uri,
-                     mPrincipal,
-                     nsILoadInfo::SEC_NORMAL,
-                     mRequest->GetContext(),
-                     mLoadGroup,
-                     nullptr, /* aCallbacks */
-                     nsIRequest::LOAD_NORMAL,
-                     ios);
-  mLoadGroup = nullptr;
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    FailWithNetworkError();
-    return rv;
-  }
-
-  // Insert ourselves into the notification callbacks chain so we can handle
-  // cross-origin redirects.
-  chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
-  chan->SetNotificationCallbacks(this);
-
   // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
   // true..." is handled by the CORS proxy.
   //
   // Step 3.2 "Set request's skip service worker flag." This isn't required
   // since Necko will fall back to the network if the ServiceWorker does not
   // respond with a valid Response.
   //
   // NS_StartCORSPreflight() will automatically kick off the original request
@@ -355,16 +331,46 @@ FetchDriver::HttpFetch(bool aCORSFlag, b
   // is include, or request's credentials mode is same-origin and the CORS flag
   // is unset, and unset otherwise."
   bool useCredentials = false;
   if (mRequest->GetCredentialsMode() == RequestCredentials::Include ||
       (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && !aCORSFlag)) {
     useCredentials = true;
   }
 
+  // This is effectivetly the opposite of the use credentials flag in "HTTP
+  // network or cache fetch" in the spec and decides whether to transmit
+  // cookies and other identifying information. LOAD_ANONYMOUS also prevents
+  // new cookies sent by the server from being stored.
+  const nsLoadFlags credentialsFlag = useCredentials ? 0 : nsIRequest::LOAD_ANONYMOUS;
+
+  // From here on we create a channel and set its properties with the
+  // information from the InternalRequest. This is an implementation detail.
+  MOZ_ASSERT(mLoadGroup);
+  nsCOMPtr<nsIChannel> chan;
+  rv = NS_NewChannel(getter_AddRefs(chan),
+                     uri,
+                     mPrincipal,
+                     nsILoadInfo::SEC_NORMAL,
+                     mRequest->GetContext(),
+                     mLoadGroup,
+                     nullptr, /* aCallbacks */
+                     nsIRequest::LOAD_NORMAL | credentialsFlag,
+                     ios);
+  mLoadGroup = nullptr;
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    return rv;
+  }
+
+  // Insert ourselves into the notification callbacks chain so we can handle
+  // cross-origin redirects.
+  chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
+  chan->SetNotificationCallbacks(this);
+
   // FIXME(nsm): Bug 1120715.
   // Step 3.4 "If request's cache mode is default and request's header list
   // contains a header named `If-Modified-Since`, `If-None-Match`,
   // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
   // to no-store."
 
   // Step 3.5 begins "HTTP network or cache fetch".
   // HTTP network or cache fetch
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -213,16 +213,25 @@ Request::Constructor(const GlobalObject&
   }
 
   requestHeaders->Fill(*headers, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (aInit.mBody.WasPassed()) {
+    // HEAD and GET are not allowed to have a body.
+    nsAutoCString method;
+    request->GetMethod(method);
+    // method is guaranteed to be uppercase due to step 14.2 above.
+    if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
+      aRv.ThrowTypeError(MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD);
+      return nullptr;
+    }
+
     const OwningArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
     nsCOMPtr<nsIInputStream> stream;
     nsCString contentType;
     aRv = ExtractByteStreamFromBody(bodyInit,
                                     getter_AddRefs(stream), contentType);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
--- a/dom/indexedDB/test/extensions/bootstrap.js
+++ b/dom/indexedDB/test/extensions/bootstrap.js
@@ -6,17 +6,17 @@
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function testForExpectedSymbols(stage, data) {
   const expectedSymbols = [ "IDBKeyRange", "indexedDB" ];
-  for each (var symbol in expectedSymbols) {
+  for (var symbol of expectedSymbols) {
     Services.prefs.setBoolPref("indexeddbtest.bootstrap." + stage + "." +
                                symbol, symbol in this);
   }
 }
 
 function GlobalObjectsComponent() {
   this.wrappedJSObject = this;
 }
--- a/dom/indexedDB/test/test_file_array.html
+++ b/dom/indexedDB/test/test_file_array.html
@@ -42,25 +42,25 @@
 
     is(event.type, "upgradeneeded", "Got correct event type");
 
     let db = event.target.result;
     db.onerror = errorHandler;
 
     let objectStore = db.createObjectStore(objectStoreName, { });
 
-    for each (let data in objectStoreData) {
+    for (let data of objectStoreData) {
       objectStore.add(data.blobs, data.key);
     }
 
     event = yield undefined;
 
     is(event.type, "success", "Got correct event type");
 
-    for each (let data in objectStoreData) {
+    for (let data of objectStoreData) {
       objectStore = db.transaction([objectStoreName])
                       .objectStore(objectStoreName);
 
       request = objectStore.get(data.key);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       verifyBlobArray(event.target.result, data.blobs, data.expectedFileIds);
--- a/dom/indexedDB/test/test_file_cross_database_copying.html
+++ b/dom/indexedDB/test/test_file_cross_database_copying.html
@@ -19,17 +19,17 @@
       { name: window.location.pathname + "2" }
     ];
 
     const objectStoreName = "Blobs";
 
     const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
 
     let databases = [];
-    for each (let info in databaseInfo) {
+    for (let info of databaseInfo) {
       let request = indexedDB.open(info.name, 1);
       request.onerror = errorHandler;
       request.onupgradeneeded = grabEventAndContinueHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       is(event.type, "upgradeneeded", "Got correct event type");
 
@@ -42,17 +42,17 @@
       event = yield undefined;
 
       is(event.type, "success", "Got correct event type");
 
       databases.push(db);
     }
 
     let refResult;
-    for each (let db in databases) {
+    for (let db of databases) {
       let request = db.transaction([objectStoreName])
                       .objectStore(objectStoreName).get(fileData.key);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let result = event.target.result;
       verifyBlob(result, fileData.file, 1);
       yield undefined;
--- a/dom/indexedDB/test/test_file_sharing.html
+++ b/dom/indexedDB/test/test_file_sharing.html
@@ -29,27 +29,27 @@
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     is(event.type, "upgradeneeded", "Got correct event type");
 
     let db = event.target.result;
     db.onerror = errorHandler;
 
-    for each (let info in objectStoreInfo) {
+    for (let info of objectStoreInfo) {
       let objectStore = db.createObjectStore(info.name, info.options);
       objectStore.add(fileData.file, fileData.key);
     }
 
     event = yield undefined;
 
     is(event.type, "success", "Got correct event type");
 
     let refResult;
-    for each (let info in objectStoreInfo) {
+    for (let info of objectStoreInfo) {
       let objectStore = db.transaction([info.name])
                           .objectStore(info.name);
 
       request = objectStore.get(fileData.key);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let result = event.target.result;
--- a/dom/indexedDB/test/test_filehandle_serialization.html
+++ b/dom/indexedDB/test/test_filehandle_serialization.html
@@ -19,17 +19,17 @@
       { name: window.location.pathname + "2" }
     ];
 
     const objectStoreName = "Blobs";
 
     const testFile = getRandomFile("random.bin", 100000);
 
     let databases = [];
-    for each (let info in databaseInfo) {
+    for (let info of databaseInfo) {
       let request = indexedDB.open(info.name, 1);
       request.onerror = errorHandler;
       request.onupgradeneeded = grabEventAndContinueHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       is(event.type, "upgradeneeded", "Got correct event type");
 
--- a/dom/indexedDB/test/test_globalObjects_other.xul
+++ b/dom/indexedDB/test/test_globalObjects_other.xul
@@ -24,18 +24,18 @@
 
     Cu.import("resource://gre/modules/AddonManager.jsm");
     AddonManager.getAddonByID("indexedDB-test@mozilla.org",
                               grabEventAndContinueHandler);
     let addon = yield undefined;
     addon.uninstall();
 
     Cu.import("resource://gre/modules/Services.jsm");
-    for each (var stage in [ "install", "startup", "shutdown", "uninstall" ]) {
-      for each (var symbol in [ "IDBKeyRange", "indexedDB" ]) {
+    for (var stage of [ "install", "startup", "shutdown", "uninstall" ]) {
+      for (var symbol of [ "IDBKeyRange", "indexedDB" ]) {
         let pref;
         try {
           pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage +
                                             "." + symbol);
         }
         catch(ex) {
           pref = false;
         }
--- a/dom/indexedDB/test/unit/test_add_put.js
+++ b/dom/indexedDB/test/unit/test_add_put.js
@@ -11,21 +11,21 @@ function testSteps()
   let openRequest = indexedDB.open(name, 1);
   openRequest.onerror = errorHandler;
   openRequest.onupgradeneeded = grabEventAndContinueHandler;
   openRequest.onsuccess = unexpectedSuccessHandler;
   let event = yield undefined;
   let db = event.target.result;
   let trans = event.target.transaction;
 
-  for each (let autoincrement in [true, false]) {
-    for each (let keypath in [false, true, "missing", "invalid"]) {
-      for each (let method in ["put", "add"]) {
-        for each (let explicit in [true, false, undefined, "invalid"]) {
-          for each (let existing in [true, false]) {
+  for (let autoincrement of [true, false]) {
+    for (let keypath of [false, true, "missing", "invalid"]) {
+      for (let method of ["put", "add"]) {
+        for (let explicit of [true, false, undefined, "invalid"]) {
+          for (let existing of [true, false]) {
             let speccedNoKey = (keypath == false || keypath == "missing") &&
                                !explicit;
 
             // We can't do 'existing' checks if we use autogenerated key
             if (speccedNoKey && autoincrement && existing) {
               continue;
             }
 
--- a/dom/indexedDB/test/unit/test_count.js
+++ b/dom/indexedDB/test/unit/test_count.js
@@ -39,17 +39,17 @@ function testSteps()
 
   let db = event.target.result;
   db.onerror = errorHandler;
 
   let objectStore = db.createObjectStore(objectStoreName, { });
   objectStore.createIndex(indexData.name, indexData.keyPath,
                           indexData.options);
 
-  for each (let data in objectStoreData) {
+  for (let data of objectStoreData) {
     objectStore.add(data.value, data.key);
   }
 
   event = yield undefined;
 
   is(event.type, "success", "Got correct event type");
 
   objectStore = db.transaction(db.objectStoreNames)
--- a/dom/indexedDB/test/unit/test_index_update_delete.js
+++ b/dom/indexedDB/test/unit/test_index_update_delete.js
@@ -13,50 +13,50 @@ function testSteps()
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   let event = yield undefined;
 
   let db = event.target.result;
   db.onerror = errorHandler;
 
-  for each (let autoIncrement in [false, true]) {
+  for (let autoIncrement of [false, true]) {
     let objectStore =
       db.createObjectStore(autoIncrement, { keyPath: "id",
                                             autoIncrement: autoIncrement });
 
     for (let i = 0; i < 10; i++) {
       objectStore.add({ id: i, index: i });
     }
 
-    for each (let unique in [false, true]) {
+    for (let unique of [false, true]) {
       objectStore.createIndex(unique, "index", { unique: unique });
     }
 
     for (let i = 10; i < 20; i++) {
       objectStore.add({ id: i, index: i });
     }
   }
 
   event = yield undefined;
   is(event.type, "success", "expect a success event");
 
-  for each (let autoIncrement in [false, true]) {
+  for (let autoIncrement of [false, true]) {
     let objectStore = db.transaction(autoIncrement)
                         .objectStore(autoIncrement);
 
     objectStore.count().onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     is(event.target.result, 20, "Correct number of entries in objectStore");
 
     let objectStoreCount = event.target.result;
     let indexCount = event.target.result;
 
-    for each (let unique in [false, true]) {
+    for (let unique of [false, true]) {
       let index = db.transaction(autoIncrement, "readwrite")
                     .objectStore(autoIncrement)
                     .index(unique);
 
       index.count().onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       is(event.target.result, indexCount,
--- a/dom/indexedDB/test/unit/test_lowDiskSpace.js
+++ b/dom/indexedDB/test/unit/test_lowDiskSpace.js
@@ -335,17 +335,17 @@ function testSteps()
     is(event.type, "upgradeneeded", "Upgrading database");
 
     let db = event.target.result;
     db.onerror = errorHandler;
 
     let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions);
     let index = objectStore.createIndex(indexName, indexName, indexOptions);
 
-    for each (let data in dbData) {
+    for (let data of dbData) {
       objectStore.add(data);
     }
 
     request.onupgradeneeded = unexpectedSuccessHandler;
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(event.type, "success", "Upgraded database");
--- a/dom/indexedDB/test/unit/test_open_empty_db.js
+++ b/dom/indexedDB/test/unit/test_open_empty_db.js
@@ -11,17 +11,17 @@ function testSteps()
     //"",
     null,
     undefined,
     this.window ? window.location.pathname : "Splendid Test"
   ];
 
   const version = 1;
 
-  for each (let name in names) {
+  for (let name of names) {
     let request = indexedDB.open(name, version);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     if (name === null) {
       name = "null";
     }
--- a/dom/indexedDB/test/unit/test_optionalArguments.js
+++ b/dom/indexedDB/test/unit/test_optionalArguments.js
@@ -32,17 +32,17 @@ function testSteps()
   is(event.type, "upgradeneeded", "Got upgradeneeded event");
 
   let db = event.target.result;
   db.onerror = errorHandler;
 
   let objectStore = db.createObjectStore(osName, { keyPath: "ssn" });
   objectStore.createIndex(indexName, "weight", { unique: false });
 
-  for each (let i in data) {
+  for (let i of data) {
     objectStore.add(i);
   }
 
   event = yield undefined;
 
   is(event.type, "success", "Got success event");
 
   try {
@@ -222,17 +222,17 @@ function testSteps()
 
   objectStore.count().onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   is(event.target.result, 0, "Correct count");
 
   event.target.transaction.oncomplete = grabEventAndContinueHandler;
 
-  for each (let i in data) {
+  for (let i of data) {
     objectStore.add(i);
   }
 
   yield undefined;
 
   objectStore = db.transaction(osName).objectStore(osName);
 
   objectStore.count().onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/unit/test_unique_index_update.js
+++ b/dom/indexedDB/test/unit/test_unique_index_update.js
@@ -11,31 +11,31 @@ function testSteps()
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   let event = yield undefined;
 
   let db = event.target.result;
 
-  for each (let autoIncrement in [false, true]) {
+  for (let autoIncrement of [false, true]) {
     let objectStore =
       db.createObjectStore(autoIncrement, { keyPath: "id",
                                             autoIncrement: autoIncrement });
     objectStore.createIndex("", "index", { unique: true });
 
     for (let i = 0; i < 10; i++) {
       objectStore.add({ id: i, index: i });
     }
   }
 
   event = yield undefined;
   is(event.type, "success", "expect a success event");
 
-  for each (let autoIncrement in [false, true]) {
+  for (let autoIncrement of [false, true]) {
     objectStore = db.transaction(autoIncrement, "readwrite")
                     .objectStore(autoIncrement);
 
     request = objectStore.put({ id: 5, index: 6 });
     request.onsuccess = unexpectedSuccessHandler;
     request.addEventListener("error", new ExpectError("ConstraintError", true));
     event = yield undefined;
 
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -12,17 +12,17 @@ interface nsIURI;
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   [noscript] void UnregisterSucceeded(in bool aState);
   [noscript] void UnregisterFailed();
 };
 
-[builtinclass, uuid(78fdfe7c-0e2c-4157-ac6e-3952b61df36a)]
+[builtinclass, uuid(430f02bf-63d0-44ab-9a59-7bd49c608949)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
@@ -45,18 +45,18 @@ interface nsIServiceWorkerManager : nsIS
 
   // Returns a Promise
   nsISupports getReadyPromise(in nsIDOMWindow aWindow);
 
   // Remove ready pending Promise
   void removeReadyPromise(in nsIDOMWindow aWindow);
 
   // aTarget MUST be a ServiceWorkerRegistration.
-  [noscript] void AddRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
-  [noscript] void RemoveRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
+  [noscript] void AddRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
+  [noscript] void RemoveRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
 
   /**
    * Call this to request that document `aDoc` be controlled by a ServiceWorker
    * if a registration exists for it's scope.
    *
    * This MUST only be called once per document!
    */
   [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -111,16 +111,22 @@ parent:
     /*
      * Creates a new remoted nsIWidget connection for windowed plugins
      * in e10s mode. This is always initiated from the child in response
      * to windowed plugin creation.
      */
     sync PPluginWidget();
 
     /**
+     * Return native data of root widget
+     */
+    sync GetWidgetNativeData() returns (WindowsHandle value);
+
+parent:
+    /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element.
      */
     MoveFocus(bool forward);
 
     Event(RemoteDOMEvent aEvent);
 
     sync CreateWindow(PBrowser aNewTab,
@@ -298,21 +304,16 @@ parent:
     sync GetDPI() returns (float value);
 
     /**
      * Gets the default scaling factor of the screen corresponding to this browser.
      */
     sync GetDefaultScale() returns (double value);
 
     /**
-     * Return native data of root widget
-     */
-    sync GetWidgetNativeData() returns (WindowsHandle value);
-
-    /**
      * Set the native cursor.
      * @param value
      *   The widget cursor to set.
      * @param force
      *   Invalidate any locally cached cursor settings and force an
      *   update.
      */
     SetCursor(uint32_t value, bool force);
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -144,17 +144,17 @@ public:
   NS_IMETHOD GetPluginName(nsACString& aPluginName) MOZ_OVERRIDE;
 
   NS_IMETHOD TerminateScript() MOZ_OVERRIDE;
   NS_IMETHOD BeginStartingDebugger() MOZ_OVERRIDE;
   NS_IMETHOD EndStartingDebugger() MOZ_OVERRIDE;
   NS_IMETHOD TerminatePlugin() MOZ_OVERRIDE;
   NS_IMETHOD TerminateProcess() MOZ_OVERRIDE;
 
-  NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult);
+  NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) MOZ_OVERRIDE;
 
   void Clear() { mContentParent = nullptr; mActor = nullptr; }
 
   void SetHangData(const HangData& aHangData) { mHangData = aHangData; }
 
 private:
   ~HangMonitoredProcess() {}
 
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -86,19 +86,19 @@ public:
   virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;
 
   // Return true if the media layer supports seeking.
   virtual bool IsTransportSeekable() = 0;
 
   // Return true if the transport layer supports seeking.
   virtual bool IsMediaSeekable() = 0;
 
-  virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant) = 0;
+  virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) = 0;
   virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) = 0;
-  virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant) = 0;
+  virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) = 0;
 
   virtual void RemoveMediaTracks() = 0;
 
   // Set the media end time in microseconds
   virtual void SetMediaEndTime(int64_t aTime) = 0;
 
   // Make the decoder state machine update the playback position. Called by
   // the reader on the decoder thread (Assertions for this checked by
@@ -160,78 +160,78 @@ public:
 };
 
 class MetadataContainer
 {
 protected:
   MetadataContainer(AbstractMediaDecoder* aDecoder,
                     nsAutoPtr<MediaInfo> aInfo,
                     nsAutoPtr<MetadataTags> aTags,
-                    bool aRestoredFromDromant)
+                    bool aRestoredFromDormant)
     : mDecoder(aDecoder),
       mInfo(aInfo),
       mTags(aTags),
-      mRestoredFromDromant(aRestoredFromDromant)
+      mRestoredFromDormant(aRestoredFromDormant)
   {}
 
   nsRefPtr<AbstractMediaDecoder> mDecoder;
   nsAutoPtr<MediaInfo>  mInfo;
   nsAutoPtr<MetadataTags> mTags;
-  bool mRestoredFromDromant;
+  bool mRestoredFromDormant;
 };
 
 class MetadataEventRunner : public nsRunnable, private MetadataContainer
 {
 public:
   MetadataEventRunner(AbstractMediaDecoder* aDecoder,
                       nsAutoPtr<MediaInfo> aInfo,
                       nsAutoPtr<MetadataTags> aTags,
-                      bool aRestoredFromDromant = false)
-    : MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDromant)
+                      bool aRestoredFromDormant = false)
+    : MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDormant)
   {}
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
-    mDecoder->MetadataLoaded(mInfo, mTags, mRestoredFromDromant);
+    mDecoder->MetadataLoaded(mInfo, mTags, mRestoredFromDormant);
     return NS_OK;
   }
 };
 
 class FirstFrameLoadedEventRunner : public nsRunnable, private MetadataContainer
 {
 public:
   FirstFrameLoadedEventRunner(AbstractMediaDecoder* aDecoder,
                               nsAutoPtr<MediaInfo> aInfo,
-                              bool aRestoredFromDromant = false)
-    : MetadataContainer(aDecoder, aInfo, nsAutoPtr<MetadataTags>(nullptr), aRestoredFromDromant)
+                              bool aRestoredFromDormant = false)
+    : MetadataContainer(aDecoder, aInfo, nsAutoPtr<MetadataTags>(nullptr), aRestoredFromDormant)
   {}
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
-    mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDromant);
+    mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDormant);
     return NS_OK;
   }
 };
 
 class MetadataUpdatedEventRunner : public nsRunnable, private MetadataContainer
 {
 public:
   MetadataUpdatedEventRunner(AbstractMediaDecoder* aDecoder,
                              nsAutoPtr<MediaInfo> aInfo,
                              nsAutoPtr<MetadataTags> aTags,
-                             bool aRestoredFromDromant = false)
-    : MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDromant)
+                             bool aRestoredFromDormant = false)
+    : MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDormant)
   {}
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
     nsAutoPtr<MediaInfo> info(new MediaInfo());
     *info = *mInfo;
-    mDecoder->MetadataLoaded(info, mTags, mRestoredFromDromant);
-    mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDromant);
+    mDecoder->MetadataLoaded(info, mTags, mRestoredFromDormant);
+    mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDormant);
     return NS_OK;
   }
 };
 
 class RemoveMediaTracksEventRunner : public nsRunnable
 {
 public:
   explicit RemoveMediaTracksEventRunner(AbstractMediaDecoder* aDecoder)
--- a/dom/media/IdpProxy.jsm
+++ b/dom/media/IdpProxy.jsm
@@ -54,17 +54,18 @@ IdpChannel.prototype = {
   _sandboxReady: function(aCallback, aSandbox) {
     // Inject a message channel into the subframe.
     try {
       this.messagechannel = new aSandbox._frame.contentWindow.MessageChannel();
       Object.defineProperty(
         aSandbox._frame.contentWindow.wrappedJSObject,
         "rtcwebIdentityPort",
         {
-          value: this.messagechannel.port2
+          value: this.messagechannel.port2,
+          configurable: true
         }
       );
     } catch (e) {
       this.close();
       aCallback(e); // oops, the IdP proxy overwrote this.. bad
       return;
     }
     this.messagechannel.port1.onmessage = function(msg) {
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -712,17 +712,17 @@ MediaDecoder::IsExpectingMoreData()
   }
 
   // Otherwise, we should be getting data unless the stream is suspended.
   return !mResource->IsSuspended();
 }
 
 void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
                                   nsAutoPtr<MetadataTags> aTags,
-                                  bool aRestoredFromDromant)
+                                  bool aRestoredFromDormant)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown) {
     return;
   }
 
   DECODER_LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
@@ -742,40 +742,40 @@ void MediaDecoder::MetadataLoaded(nsAuto
 
   mInfo = aInfo.forget();
   ConstructMediaTracks();
 
   if (mOwner) {
     // Make sure the element and the frame (if any) are told about
     // our new size.
     Invalidate();
-    if (!aRestoredFromDromant) {
+    if (!aRestoredFromDormant) {
       mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
     }
   }
 }
 
 void MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
-                                    bool aRestoredFromDromant)
+                                    bool aRestoredFromDormant)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown) {
     return;
   }
 
   DECODER_LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
               aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
               aInfo->HasAudio(), aInfo->HasVideo());
 
   mInfo = aInfo.forget();
 
   if (mOwner) {
     Invalidate();
-    if (!aRestoredFromDromant) {
+    if (!aRestoredFromDormant) {
       mOwner->FirstFrameLoaded();
     }
   }
 
   // This can run cache callbacks.
   mResource->EnsureCacheUpToDate();
 
   // The element can run javascript via events
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -772,22 +772,22 @@ public:
   // May be called by the reader to notify this decoder that the metadata from
   // the media file has been read. Call on the decode thread only.
   void OnReadMetadataCompleted() MOZ_OVERRIDE { }
 
   // Called when the metadata from the media file has been loaded by the
   // state machine. Call on the main thread only.
   virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
                               nsAutoPtr<MetadataTags> aTags,
-                              bool aRestoredFromDromant) MOZ_OVERRIDE;
+                              bool aRestoredFromDormant) MOZ_OVERRIDE;
 
   // Called when the first audio and/or video from the media file has been loaded
   // by the state machine. Call on the main thread only.
   virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
-                                bool aRestoredFromDromant) MOZ_OVERRIDE;
+                                bool aRestoredFromDormant) MOZ_OVERRIDE;
 
   // Called from MetadataLoaded(). Creates audio tracks and adds them to its
   // owner's audio track list, and implies to video tracks respectively.
   // Call on the main thread only.
   void ConstructMediaTracks();
 
   // Removes all audio tracks and video tracks that are previously added into
   // the track list. Call on the main thread only.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -349,27 +349,30 @@ void MediaDecoderStateMachine::SendStrea
   } else {
     // Write the entire packet.
     offset = 0;
   }
 
   if (offset >= aAudio->mFrames)
     return;
 
+  size_t framesToWrite = aAudio->mFrames - offset;
+
   aAudio->EnsureAudioBuffer();
   nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
   AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
   nsAutoTArray<const AudioDataValue*,2> channels;
   for (uint32_t i = 0; i < aAudio->mChannels; ++i) {
     channels.AppendElement(bufferData + i*aAudio->mFrames + offset);
   }
-  aOutput->AppendFrames(buffer.forget(), channels, aAudio->mFrames);
-  VERBOSE_LOG("writing %d frames of data to MediaStream for AudioData at %lld",
-              aAudio->mFrames - int32_t(offset), aAudio->mTime);
-  aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset);
+  aOutput->AppendFrames(buffer.forget(), channels, framesToWrite);
+  VERBOSE_LOG("writing %u frames of data to MediaStream for AudioData at %lld",
+              static_cast<unsigned>(framesToWrite),
+              aAudio->mTime);
+  aStream->mAudioFramesWritten += framesToWrite;
   aOutput->ApplyVolume(mVolume);
 }
 
 static void WriteVideoToMediaStream(MediaStream* aStream,
                                     layers::Image* aImage,
                                     int64_t aEndMicroseconds,
                                     int64_t aStartMicroseconds,
                                     const IntSize& aIntrinsicSize,
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -481,17 +481,18 @@ MediaSource::QueueAsyncSimpleEvent(const
 
 void
 MediaSource::DurationChange(double aOldDuration, double aNewDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("MediaSource(%p)::DurationChange(aOldDuration=%f, aNewDuration=%f)", this, aOldDuration, aNewDuration);
 
   if (aNewDuration < aOldDuration) {
-    mSourceBuffers->RangeRemoval(aNewDuration, aOldDuration);
+    // Remove all buffered data from aNewDuration.
+    mSourceBuffers->RangeRemoval(aNewDuration, PositiveInfinity<double>());
   }
   // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
 }
 
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -235,16 +235,19 @@ MediaSourceDecoder::DoSetMediaSourceDura
 {
   if (aDuration >= 0) {
     mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
     mMediaSourceDuration = aDuration;
   } else {
     mDecoderStateMachine->SetDuration(INT64_MAX);
     mMediaSourceDuration = PositiveInfinity<double>();
   }
+  if (mReader) {
+    mReader->SetMediaSourceDuration(mMediaSourceDuration);
+  }
 }
 
 void
 MediaSourceDecoder::ScheduleDurationChange(double aOldDuration,
                                            double aNewDuration,
                                            MSRangeRemovalAction aAction)
 {
   if (aAction == MSRangeRemovalAction::SKIP) {
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -51,16 +51,17 @@ MediaSourceReader::MediaSourceReader(Med
   , mPendingSeekTime(-1)
   , mWaitingForSeekData(false)
   , mAudioIsSeeking(false)
   , mVideoIsSeeking(false)
   , mTimeThreshold(-1)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
   , mEnded(false)
+  , mMediaSourceDuration(0)
   , mHasEssentialTrackBuffers(false)
 #ifdef MOZ_FMP4
   , mSharedDecoderManager(new SharedDecoderManager())
 #endif
 {
 }
 
 void
@@ -118,23 +119,33 @@ MediaSourceReader::RequestAudioData()
     mAudioPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
   if (mAudioIsSeeking) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called mid-seek. Rejecting.", this);
     mAudioPromise.Reject(CANCELED, __func__);
     return p;
   }
-  if (SwitchAudioReader(mLastAudioTime)) {
-    mAudioReader->Seek(mLastAudioTime, 0)
-                ->Then(GetTaskQueue(), __func__, this,
-                       &MediaSourceReader::RequestAudioDataComplete,
-                       &MediaSourceReader::RequestAudioDataFailed);
-  } else {
-    RequestAudioDataComplete(0);
+  SwitchReaderResult ret = SwitchAudioReader(mLastAudioTime);
+  switch (ret) {
+    case READER_NEW:
+      mAudioReader->Seek(mLastAudioTime, 0)
+                  ->Then(GetTaskQueue(), __func__, this,
+                         &MediaSourceReader::RequestAudioDataComplete,
+                         &MediaSourceReader::RequestAudioDataFailed);
+      break;
+    case READER_ERROR:
+      if (mLastAudioTime) {
+        CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
+        break;
+      }
+      // Fallback to using current reader
+    default:
+      RequestAudioDataComplete(0);
+      break;
   }
   return p;
 }
 
 void
 MediaSourceReader::RequestAudioDataComplete(int64_t aTime)
 {
   mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
@@ -215,33 +226,25 @@ MediaSourceReader::OnAudioNotDecoded(Not
   MOZ_ASSERT(aReason == END_OF_STREAM);
   if (mAudioReader) {
     AdjustEndTime(&mLastAudioTime, mAudioReader);
   }
 
   // See if we can find a different reader that can pick up where we left off. We use the
   // EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
   // 1065207.
-  if (SwitchAudioReader(mLastAudioTime, EOS_FUZZ_US)) {
+  if (SwitchAudioReader(mLastAudioTime, EOS_FUZZ_US) == READER_NEW) {
     mAudioReader->Seek(mLastAudioTime, 0)
                 ->Then(GetTaskQueue(), __func__, this,
                        &MediaSourceReader::RequestAudioDataComplete,
                        &MediaSourceReader::RequestAudioDataFailed);
     return;
   }
 
-  // If the entire MediaSource is done, generate an EndOfStream.
-  if (IsEnded()) {
-    mAudioPromise.Reject(END_OF_STREAM, __func__);
-    return;
-  }
-
-  // We don't have the data the caller wants. Tell that we're waiting for JS to
-  // give us more data.
-  mAudioPromise.Reject(WAITING_FOR_DATA, __func__);
+  CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
 }
 
 
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
 {
   nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
   MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
@@ -256,25 +259,35 @@ MediaSourceReader::RequestVideoData(bool
     mDropAudioBeforeThreshold = true;
     mDropVideoBeforeThreshold = true;
   }
   if (mVideoIsSeeking) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called mid-seek. Rejecting.", this);
     mVideoPromise.Reject(CANCELED, __func__);
     return p;
   }
-  if (SwitchVideoReader(mLastVideoTime)) {
-    mVideoReader->Seek(mLastVideoTime, 0)
-                ->Then(GetTaskQueue(), __func__, this,
-                       &MediaSourceReader::RequestVideoDataComplete,
-                       &MediaSourceReader::RequestVideoDataFailed);
-  } else {
-    mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
-                ->Then(GetTaskQueue(), __func__, this,
-                       &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
+  SwitchReaderResult ret = SwitchVideoReader(mLastVideoTime);
+  switch (ret) {
+    case READER_NEW:
+      mVideoReader->Seek(mLastVideoTime, 0)
+                  ->Then(GetTaskQueue(), __func__, this,
+                         &MediaSourceReader::RequestVideoDataComplete,
+                         &MediaSourceReader::RequestVideoDataFailed);
+      break;
+    case READER_ERROR:
+      if (mLastVideoTime) {
+        CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
+        break;
+      }
+      // Fallback to using current reader.
+    default:
+      mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
+                  ->Then(GetTaskQueue(), __func__, this,
+                         &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
+      break;
   }
 
   return p;
 }
 
 void
 MediaSourceReader::RequestVideoDataComplete(int64_t aTime)
 {
@@ -330,33 +343,47 @@ MediaSourceReader::OnVideoNotDecoded(Not
   MOZ_ASSERT(aReason == END_OF_STREAM);
   if (mVideoReader) {
     AdjustEndTime(&mLastVideoTime, mVideoReader);
   }
 
   // See if we can find a different reader that can pick up where we left off. We use the
   // EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
   // 1065207.
-  if (SwitchVideoReader(mLastVideoTime, EOS_FUZZ_US)) {
+  if (SwitchVideoReader(mLastVideoTime, EOS_FUZZ_US) == READER_NEW) {
     mVideoReader->Seek(mLastVideoTime, 0)
                 ->Then(GetTaskQueue(), __func__, this,
                        &MediaSourceReader::RequestVideoDataComplete,
                        &MediaSourceReader::RequestVideoDataFailed);
     return;
   }
 
+  CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
+}
+
+void
+MediaSourceReader::CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime)
+{
   // If the entire MediaSource is done, generate an EndOfStream.
-  if (IsEnded()) {
-    mVideoPromise.Reject(END_OF_STREAM, __func__);
+  if (IsNearEnd(aTime)) {
+    if (aType == MediaData::AUDIO_DATA) {
+      mAudioPromise.Reject(END_OF_STREAM, __func__);
+    } else {
+      mVideoPromise.Reject(END_OF_STREAM, __func__);
+    }
     return;
   }
 
-  // We don't have the data the caller wants. Tell that we're waiting for JS to
-  // give us more data.
-  mVideoPromise.Reject(WAITING_FOR_DATA, __func__);
+  if (aType == MediaData::AUDIO_DATA) {
+    // We don't have the data the caller wants. Tell that we're waiting for JS to
+    // give us more data.
+    mAudioPromise.Reject(WAITING_FOR_DATA, __func__);
+  } else {
+    mVideoPromise.Reject(WAITING_FOR_DATA, __func__);
+  }
 }
 
 nsRefPtr<ShutdownPromise>
 MediaSourceReader::Shutdown()
 {
   mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
 
   MOZ_ASSERT(mMediaSourceShutdownPromise.IsEmpty());
@@ -442,50 +469,50 @@ bool
 MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
 {
   TrackBuffer* trackBuffer = aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
   MOZ_ASSERT(trackBuffer);
   nsRefPtr<MediaDecoderReader> reader = SelectReader(aTarget, EOS_FUZZ_US, trackBuffer->Decoders());
   return !!reader;
 }
 
-bool
+MediaSourceReader::SwitchReaderResult
 MediaSourceReader::SwitchAudioReader(int64_t aTarget, int64_t aError)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   // XXX: Can't handle adding an audio track after ReadMetadata.
   if (!mAudioTrack) {
-    return false;
+    return READER_ERROR;
   }
   nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, aError, mAudioTrack->Decoders());
   if (newReader && newReader != mAudioReader) {
     mAudioReader->SetIdle();
     mAudioReader = newReader;
     MSE_DEBUGV("MediaSourceReader(%p)::SwitchAudioReader switched reader to %p", this, mAudioReader.get());
-    return true;
+    return READER_NEW;
   }
-  return false;
+  return newReader ? READER_EXISTING : READER_ERROR;
 }
 
-bool
+MediaSourceReader::SwitchReaderResult
 MediaSourceReader::SwitchVideoReader(int64_t aTarget, int64_t aError)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   // XXX: Can't handle adding a video track after ReadMetadata.
   if (!mVideoTrack) {
-    return false;
+    return READER_ERROR;
   }
   nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, aError, mVideoTrack->Decoders());
   if (newReader && newReader != mVideoReader) {
     mVideoReader->SetIdle();
     mVideoReader = newReader;
     MSE_DEBUGV("MediaSourceReader(%p)::SwitchVideoReader switched reader to %p", this, mVideoReader.get());
-    return true;
+    return READER_NEW;
   }
-  return false;
+  return newReader ? READER_EXISTING : READER_ERROR;
 }
 
 bool
 MediaSourceReader::IsDormantNeeded()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mVideoReader) {
     return mVideoReader->IsDormantNeeded();
@@ -915,16 +942,30 @@ MediaSourceReader::Ended()
 
 bool
 MediaSourceReader::IsEnded()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   return mEnded;
 }
 
+bool
+MediaSourceReader::IsNearEnd(int64_t aTime)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  return mEnded && aTime >= (mMediaSourceDuration * USECS_PER_S - EOS_FUZZ_US);
+}
+
+void
+MediaSourceReader::SetMediaSourceDuration(double aDuration)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  mMediaSourceDuration = aDuration;
+}
+
 #ifdef MOZ_EME
 nsresult
 MediaSourceReader::SetCDMProxy(CDMProxy* aProxy)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   mCDMProxy = aProxy;
   for (size_t i = 0; i < mTrackBuffers.Length(); i++) {
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -124,16 +124,20 @@ public:
   // Return true if all of the active tracks contain data for the specified time.
   bool TrackBuffersContainTime(int64_t aTime);
 
   // Mark the reader to indicate that EndOfStream has been called on our MediaSource
   void Ended();
 
   // Return true if the Ended method has been called
   bool IsEnded();
+  bool IsNearEnd(int64_t aTime /* microseconds */);
+
+  // Set the duration of the attached mediasource element.
+  void SetMediaSourceDuration(double aDuration /* seconds */);
 
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
   virtual bool IsAsync() const MOZ_OVERRIDE {
     return (!mAudioReader || mAudioReader->IsAsync()) &&
            (!mVideoReader || mVideoReader->IsAsync());
@@ -141,22 +145,30 @@ public:
 
   // Returns true if aReader is a currently active audio or video
   bool IsActiveReader(MediaDecoderReader* aReader);
 
 private:
   // Switch the current audio/video reader to the reader that
   // contains aTarget (or up to aError after target). Both
   // aTarget and aError are in microseconds.
-  bool SwitchAudioReader(int64_t aTarget, int64_t aError = 0);
-  bool SwitchVideoReader(int64_t aTarget, int64_t aError = 0);
+  enum SwitchReaderResult {
+    READER_ERROR = -1,
+    READER_EXISTING = 0,
+    READER_NEW = 1,
+  };
+  SwitchReaderResult SwitchAudioReader(int64_t aTarget, int64_t aError = 0);
+  SwitchReaderResult SwitchVideoReader(int64_t aTarget, int64_t aError = 0);
   void RequestAudioDataComplete(int64_t aTime);
   void RequestAudioDataFailed(nsresult aResult);
   void RequestVideoDataComplete(int64_t aTime);
   void RequestVideoDataFailed(nsresult aResult);
+  // Will reject the MediaPromise with END_OF_STREAM if mediasource has ended
+  // or with WAIT_FOR_DATA otherwise.
+  void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */);
 
   // Return a reader from the set available in aTrackDecoders that has data
   // available in the range requested by aTarget.
   already_AddRefed<MediaDecoderReader> SelectReader(int64_t aTarget,
                                                     int64_t aError,
                                                     const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
 
@@ -197,16 +209,17 @@ private:
   bool mAudioIsSeeking;
   bool mVideoIsSeeking;
 
   int64_t mTimeThreshold;
   bool mDropAudioBeforeThreshold;
   bool mDropVideoBeforeThreshold;
 
   bool mEnded;
+  double mMediaSourceDuration;
 
   bool mHasEssentialTrackBuffers;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mMediaSourceShutdownPromise;
 #ifdef MOZ_FMP4
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 #endif
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -31,20 +31,47 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
+// RangeRemoval must be synchronous if appendBuffer is also synchronous.
+// While waiting for bug 1118589 to land, ensure RangeRemoval is synchronous
+#define APPENDBUFFER_IS_SYNCHRONOUS
+
 namespace mozilla {
 
 namespace dom {
 
+class RangeRemovalRunnable : public nsRunnable {
+public:
+  RangeRemovalRunnable(SourceBuffer* aSourceBuffer,
+                     double aStart,
+                     double aEnd)
+  : mSourceBuffer(aSourceBuffer)
+  , mStart(aStart)
+  , mEnd(aEnd)
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
+
+    mSourceBuffer->DoRangeRemoval(mStart, mEnd);
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<SourceBuffer> mSourceBuffer;
+  double mStart;
+  double mEnd;
+};
+
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
@@ -160,29 +187,35 @@ SourceBuffer::Abort(ErrorResult& aRv)
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  if (mUpdating) {
-    // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
-    AbortUpdating();
-  }
+  Abort();
   mTrackBuffer->ResetParserState();
   mAppendWindowStart = 0;
   mAppendWindowEnd = PositiveInfinity<double>();
 
   MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
   mTrackBuffer->DiscardDecoder();
 }
 
 void
+SourceBuffer::Abort()
+{
+  if (mUpdating) {
+    // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
+    AbortUpdating();
+  }
+}
+
+void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd);
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
@@ -201,30 +234,55 @@ SourceBuffer::Remove(double aStart, doub
   }
   RangeRemoval(aStart, aEnd);
 }
 
 void
 SourceBuffer::RangeRemoval(double aStart, double aEnd)
 {
   StartUpdating();
-  /// TODO: Run coded frame removal algorithm.
+
+#if defined(APPENDBUFFER_IS_SYNCHRONOUS)
+  DoRangeRemoval(aStart, aEnd);
 
-  // Run the final step of the coded frame removal algorithm asynchronously
-  // to ensure the SourceBuffer's updating flag transition behaves as
-  // required by the spec.
+  // Run the final step of the buffer append algorithm asynchronously to
+  // ensure the SourceBuffer's updating flag transition behaves as required
+  // by the spec.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
   NS_DispatchToMainThread(event);
+#else
+  nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
+  NS_DispatchToMainThread(task);
+#endif
+}
+
+void
+SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
+{
+  if (!mUpdating) {
+    // abort was called in between.
+    return;
+  }
+  if (mTrackBuffer && !IsInfinite(aStart)) {
+    int64_t start = aStart * USECS_PER_S;
+    int64_t end = IsInfinite(aEnd) ? INT64_MAX : (int64_t)(aEnd * USECS_PER_S);
+    mTrackBuffer->RangeRemoval(start, end);
+  }
+
+#if !defined(APPENDBUFFER_IS_SYNCHRONOUS)
+  StopUpdating();
+#endif
 }
 
 void
 SourceBuffer::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("SourceBuffer(%p)::Detach", this);
+  Abort();
   if (mTrackBuffer) {
     mTrackBuffer->Detach();
   }
   mTrackBuffer = nullptr;
   mMediaSource = nullptr;
 }
 
 void
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -74,16 +74,17 @@ public:
   }
 
   void SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv);
 
   void AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv);
   void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
 
   void Abort(ErrorResult& aRv);
+  void Abort();
 
   void Remove(double aStart, double aEnd, ErrorResult& aRv);
   /** End WebIDL Methods. */
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SourceBuffer, DOMEventTargetHelper)
 
   SourceBuffer(MediaSource* aMediaSource, const nsACString& aType);
@@ -104,17 +105,19 @@ public:
 
   // Evict data in the source buffer in the given time range.
   void Evict(double aStart, double aEnd);
 
   double GetBufferedStart();
   double GetBufferedEnd();
 
   // Runs the range removal algorithm as defined by the MSE spec.
+  // RangeRemoval will queue a call to DoRangeRemoval.
   void RangeRemoval(double aStart, double aEnd);
+  void DoRangeRemoval(double aStart, double aEnd);
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   ~SourceBuffer();
 
--- a/dom/media/mediasource/SourceBufferDecoder.cpp
+++ b/dom/media/mediasource/SourceBufferDecoder.cpp
@@ -35,16 +35,18 @@ NS_IMPL_ISUPPORTS0(SourceBufferDecoder)
 SourceBufferDecoder::SourceBufferDecoder(MediaResource* aResource,
                                          AbstractMediaDecoder* aParentDecoder,
                                          int64_t aTimestampOffset)
   : mResource(aResource)
   , mParentDecoder(aParentDecoder)
   , mReader(nullptr)
   , mTimestampOffset(aTimestampOffset)
   , mMediaDuration(-1)
+  , mRealMediaDuration(0)
+  , mTrimmedOffset(-1)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(SourceBufferDecoder);
 }
 
 SourceBufferDecoder::~SourceBufferDecoder()
 {
   MOZ_COUNT_DTOR(SourceBufferDecoder);
@@ -89,24 +91,24 @@ SourceBufferDecoder::IsMediaSeekable()
 {
   MSE_DEBUG("SourceBufferDecoder(%p)::IsMediaSeekable UNIMPLEMENTED", this);
   return false;
 }
 
 void
 SourceBufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
                                     nsAutoPtr<MetadataTags> aTags,
-                                    bool aRestoredFromDromant)
+                                    bool aRestoredFromDormant)
 {
   MSE_DEBUG("SourceBufferDecoder(%p)::MetadataLoaded UNIMPLEMENTED", this);
 }
 
 void
 SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
-                                      bool aRestoredFromDromant)
+                                      bool aRestoredFromDormant)
 {
   MSE_DEBUG("SourceBufferDecoder(%p)::FirstFrameLoaded UNIMPLEMENTED", this);
 }
 
 void
 SourceBufferDecoder::QueueMetadata(int64_t aTime,
                                    nsAutoPtr<MediaInfo> aInfo,
                                    nsAutoPtr<MetadataTags> aTags)
@@ -186,16 +188,28 @@ SourceBufferDecoder::NotifyDecodedFrames
 
 void
 SourceBufferDecoder::SetMediaDuration(int64_t aDuration)
 {
   mMediaDuration = aDuration;
 }
 
 void
+SourceBufferDecoder::SetRealMediaDuration(int64_t aDuration)
+{
+  mRealMediaDuration = aDuration;
+}
+
+void
+SourceBufferDecoder::Trim(int64_t aDuration)
+{
+  mTrimmedOffset = (double)aDuration / USECS_PER_S;
+}
+
+void
 SourceBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
 {
   MSE_DEBUG("SourceBufferDecoder(%p)::UpdateEstimatedMediaDuration UNIMPLEMENTED", this);
 }
 
 void
 SourceBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
 {
@@ -224,33 +238,43 @@ SourceBufferDecoder::NotifyDataArrived(c
   // force parent decoder's state machine to recompute end time for
   // infinite length media.
   mParentDecoder->NotifyDataArrived(nullptr, 0, 0);
 }
 
 nsresult
 SourceBufferDecoder::GetBuffered(dom::TimeRanges* aBuffered)
 {
-  return mReader->GetBuffered(aBuffered);
+  nsresult rv = mReader->GetBuffered(aBuffered);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (!WasTrimmed()) {
+    return NS_OK;
+  }
+  nsRefPtr<dom::TimeRanges> tr = new dom::TimeRanges();
+  tr->Add(0, mTrimmedOffset);
+  aBuffered->Intersection(tr);
+  return NS_OK;
 }
 
 int64_t
 SourceBufferDecoder::ConvertToByteOffset(double aTime)
 {
   int64_t readerOffset = mReader->GetEvictionOffset(aTime);
   if (readerOffset >= 0) {
     return readerOffset;
   }
 
   // Uses a conversion based on (aTime/duration) * length.  For the
   // purposes of eviction this should be adequate since we have the
   // byte threshold as well to ensure data actually gets evicted and
   // we ensure we don't evict before the current playable point.
-  if (mMediaDuration <= 0) {
+  if (mRealMediaDuration <= 0) {
     return -1;
   }
   int64_t length = GetResource()->GetLength();
   MOZ_ASSERT(length > 0);
-  int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length;
+  int64_t offset = (aTime / (double(mRealMediaDuration) / USECS_PER_S)) * length;
   return offset;
 }
 
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -44,18 +44,18 @@ public:
   virtual bool OnStateMachineThread() const MOZ_FINAL MOZ_OVERRIDE;
   virtual int64_t GetTimestampOffset() const MOZ_FINAL MOZ_OVERRIDE { return mTimestampOffset; }
   virtual int64_t GetMediaDuration() MOZ_FINAL MOZ_OVERRIDE;
   virtual layers::ImageContainer* GetImageContainer() MOZ_FINAL MOZ_OVERRIDE;
   virtual MediaDecoderOwner* GetOwner() MOZ_FINAL MOZ_OVERRIDE;
   virtual SourceBufferResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE;
   virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE;
   virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
-  virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
-  virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
+  virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
+  virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
   virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
   virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE;
   virtual void NotifyWaitingForResourcesStatusChanged() MOZ_FINAL MOZ_OVERRIDE;
   virtual void OnReadMetadataCompleted() MOZ_FINAL MOZ_OVERRIDE;
   virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
   virtual void RemoveMediaTracks() MOZ_FINAL MOZ_OVERRIDE;
   virtual void SetMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
@@ -116,28 +116,54 @@ public:
     return mCDMProxy;
   }
 #endif
 
   // Given a time convert it into an approximate byte offset from the
   // cached data. Returns -1 if no such value is computable.
   int64_t ConvertToByteOffset(double aTime);
 
+  // All durations are in usecs.
+
+  // We can't at this stage, accurately remove coded frames.
+  // Trim is a work around that hides data located after a given time by
+  // preventing playback beyond the trim point.
+  // No data is actually removed.
+  // aDuration is were data will be trimmed from.
+  void Trim(int64_t aDuration);
+  bool WasTrimmed()
+  {
+    return mTrimmedOffset >= 0;
+  }
+
+  // returns the real duration of the resource, including trimmed data.
+  void SetRealMediaDuration(int64_t aDuration);
+  int64_t GetRealMediaDuration()
+  {
+    return mRealMediaDuration;
+  }
+
 private:
   virtual ~SourceBufferDecoder();
 
   // Our TrackBuffer's task queue, this is only non-null during initialization.
   RefPtr<MediaTaskQueue> mTaskQueue;
 
   nsRefPtr<MediaResource> mResource;
 
   AbstractMediaDecoder* mParentDecoder;
   nsRefPtr<MediaDecoderReader> mReader;
+  // in microseconds
   int64_t mTimestampOffset;
+  // mMediaDuration contains the apparent buffer duration, excluding trimmed data.
   int64_t mMediaDuration;
+  // mRealMediaDuration contains the real buffer duration, including trimmed data.
+  int64_t mRealMediaDuration;
+  // in seconds
+  double mTrimmedOffset;
 
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> mCDMProxy;
 #endif
 };
 
 } // namespace mozilla
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -30,17 +30,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 // Time in seconds to substract from the current time when deciding the
 // time point to evict data before in a decoder. This is used to help
-// precent evicting the current playback point.
+// prevent evicting the current playback point.
 #define MSE_EVICT_THRESHOLD_TIME 2.0
 
 namespace mozilla {
 
 TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
   : mParentDecoder(aParentDecoder)
   , mType(aType)
   , mLastStartTimestamp(0)
@@ -162,58 +162,59 @@ TrackBuffer::AppendData(const uint8_t* a
   int64_t start, end;
   if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
     start += aTimestampOffset;
     end += aTimestampOffset;
     if (mParser->IsMediaSegmentPresent(aData, aLength) &&
         mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
-         mDecoderPerSegment)) {
+         mDecoderPerSegment || mCurrentDecoder->WasTrimmed())) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       // This data is earlier in the timeline than data we have already
       // processed, so we must create a new decoder to handle the decoding.
       if (!decoders.NewDecoder(aTimestampOffset)) {
         return false;
       }
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
       const nsTArray<uint8_t>& initData = mParser->InitData();
-      AppendDataToCurrentResource(initData.Elements(), initData.Length());
+      AppendDataToCurrentResource(initData.Elements(), initData.Length(), end - start);
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
-  if (!AppendDataToCurrentResource(aData, aLength)) {
+  if (!AppendDataToCurrentResource(aData, aLength, end - start)) {
     return false;
   }
 
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   mParentDecoder->GetReader()->MaybeNotifyHaveData();
   return true;
 }
 
 bool
-TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
+TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength, uint32_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
     return false;
   }
 
   SourceBufferResource* resource = mCurrentDecoder->GetResource();
   int64_t appendOffset = resource->GetLength();
   resource->AppendData(aData, aLength);
+  mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
   // XXX: For future reference: NDA call must run on the main thread.
   mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
                                      aLength, appendOffset);
   mParentDecoder->NotifyBytesDownloaded();
   mParentDecoder->NotifyTimeRangesChanged();
 
   return true;
 }
@@ -283,18 +284,18 @@ TrackBuffer::EvictData(double aPlaybackT
     }
 
     MSE_DEBUG("TrackBuffer(%p)::EvictData decoder=%u/%u threshold=%u "
               "toEvict=%lld current=%s pastCurrent=%s",
               this, i, decoders.Length(), aThreshold, toEvict,
               onCurrent ? "true" : "false",
               pastCurrentDecoder ? "true" : "false");
 
-    if (pastCurrentDecoder
-        && !mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
+    if (pastCurrentDecoder &&
+        !mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
       // Remove data from older decoders than the current one.
       // Don't remove data if it is currently active.
       MSE_DEBUG("TrackBuffer(%p)::EvictData evicting all before start "
                 "bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
                 this, buffered->GetStartTime(), buffered->GetEndTime(),
                 aPlaybackTime, decoders[i]->GetResource()->GetSize());
       toEvict -= decoders[i]->GetResource()->EvictAll();
     } else {
@@ -715,9 +716,51 @@ TrackBuffer::RemoveDecoder(SourceBufferD
 
     if (mCurrentDecoder == aDecoder) {
       DiscardDecoder();
     }
   }
   aDecoder->GetReader()->GetTaskQueue()->Dispatch(task);
 }
 
+bool
+TrackBuffer::RangeRemoval(int64_t aStart, int64_t aEnd)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+
+  nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
+  int64_t bufferedEnd = Buffered(buffered) * USECS_PER_S;
+  int64_t bufferedStart = buffered->GetStartTime() * USECS_PER_S;
+
+  if (bufferedStart < 0 || aStart > bufferedEnd || aEnd < bufferedStart) {
+    // Nothing to remove.
+    return false;
+  }
+  if (aEnd < bufferedEnd) {
+    // TODO. We only handle trimming.
+    NS_WARNING("TrackBuffer::RangeRemoval unsupported arguments. "
+               "Can only handle trimming");
+    return false;
+  }
+
+  nsTArray<SourceBufferDecoder*> decoders;
+  decoders.AppendElements(mInitializedDecoders);
+
+  // Only trimming existing buffers.
+  for (size_t i = 0; i < decoders.Length(); ++i) {
+    decoders[i]->Trim(aStart);
+    if (aStart <= buffered->GetStartTime()) {
+      // We've completely emptied it, can clear the data.
+      int64_t size = decoders[i]->GetResource()->GetSize();
+      decoders[i]->GetResource()->EvictData(size, size);
+      if (decoders[i] == mCurrentDecoder ||
+          mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
+        continue;
+      }
+      MSE_DEBUG("TrackBuffer(%p):RangeRemoval remove empty decoders=%d", this, i);
+      RemoveDecoder(decoders[i]);
+    }
+  }
+  return true;
+}
+
 } // namespace mozilla
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -87,16 +87,23 @@ public:
   // http://w3c.github.io/media-source/#sourcebuffer-reset-parser-state
   void ResetParserState();
 
   // Returns a reference to mInitializedDecoders, used by MediaSourceReader
   // to select decoders.
   // TODO: Refactor to a cleaner interface between TrackBuffer and MediaSourceReader.
   const nsTArray<nsRefPtr<SourceBufferDecoder>>& Decoders();
 
+  // Runs MSE range removal algorithm.
+  // http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
+  // Implementation is only partial, we can only trim a buffer.
+  // Returns true if data was evicted.
+  // Times are in microseconds.
+  bool RangeRemoval(int64_t aStart, int64_t aEnd);
+
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
@@ -108,17 +115,18 @@ private:
   // returns it. The new decoder must be queued using QueueInitializeDecoder
   // for initialization.
   // The decoder is not considered initialized until it is added to
   // mInitializedDecoders.
   already_AddRefed<SourceBufferDecoder> NewDecoder(int64_t aTimestampOffset /* microseconds */);
 
   // Helper for AppendData, ensures NotifyDataArrived is called whenever
   // data is appended to the current decoder's SourceBufferResource.
-  bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
+  bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength,
+                                   uint32_t aDuration /* microseconds */);
 
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
 
   // Runs decoder initialization including calling ReadMetadata.  Runs as an
   // event on the decode thread pool.
   void InitializeDecoder(SourceBufferDecoder* aDecoder);
 
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -53,20 +53,20 @@ bool
 MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio()
 {
   return (mCanOffloadAudio && !mFallbackToStateMachine && !mOutputStreams.Length() &&
       mInitialPlaybackRate == 1.0);
 }
 
 void
 MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
-                                        bool aRestoredFromDromant)
+                                        bool aRestoredFromDormant)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MediaDecoder::FirstFrameLoaded(aInfo, aRestoredFromDromant);
+  MediaDecoder::FirstFrameLoaded(aInfo, aRestoredFromDormant);
 
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (!CheckDecoderCanOffloadAudio()) {
     DECODER_LOG(PR_LOG_DEBUG, ("In %s Offload Audio check failed",
         __PRETTY_FUNCTION__));
     return;
   }
 
--- a/dom/media/omx/MediaOmxCommonDecoder.h
+++ b/dom/media/omx/MediaOmxCommonDecoder.h
@@ -19,17 +19,17 @@ class AudioOffloadPlayerBase;
 class MediaOmxCommonReader;
 
 class MediaOmxCommonDecoder : public MediaDecoder
 {
 public:
   MediaOmxCommonDecoder();
 
   virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
-                                bool aRestoredFromDromant);
+                                bool aRestoredFromDormant);
   virtual void ChangeState(PlayState aState);
   virtual void ApplyStateToStateMachine(PlayState aState);
   virtual void SetVolume(double aVolume);
   virtual void PlaybackPositionChanged();
   virtual void UpdateReadyStateForData();
   virtual void SetElementVisibility(bool aIsVisible);
   virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio);
   virtual bool CheckDecoderCanOffloadAudio();
--- a/dom/media/webaudio/BufferDecoder.cpp
+++ b/dom/media/webaudio/BufferDecoder.cpp
@@ -135,23 +135,23 @@ BufferDecoder::IsTransportSeekable()
 
 bool
 BufferDecoder::IsMediaSeekable()
 {
   return false;
 }
 
 void
-BufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant)
+BufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant)
 {
   // ignore
 }
 
 void
-BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant)
+BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant)
 {
   // ignore
 }
 
 void
 BufferDecoder::QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags)
 {
   // ignore
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -54,19 +54,19 @@ public:
 
   virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
   virtual layers::ImageContainer* GetImageContainer() MOZ_FINAL MOZ_OVERRIDE;
 
   virtual bool IsTransportSeekable() MOZ_FINAL MOZ_OVERRIDE;
 
   virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
 
-  virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
+  virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
   virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
-  virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
+  virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
 
   virtual void RemoveMediaTracks() MOZ_FINAL MOZ_OVERRIDE;
 
   virtual void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
 
   virtual void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
 
   virtual void OnReadMetadataCompleted() MOZ_FINAL MOZ_OVERRIDE;
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -214,19 +214,23 @@ MediaEngineTabVideoSource::Draw() {
 
   if (innerWidth == 0 || innerHeight == 0) {
     return;
   }
 
   IntSize size;
   // maintain source aspect ratio
   if (mBufWidthMax/innerWidth < mBufHeightMax/innerHeight) {
-    size = IntSize(mBufWidthMax, (mBufWidthMax * ((float) innerHeight/innerWidth)));
+    // adjust width to be divisible by 4 to work around bug 1125393
+    int32_t width = mBufWidthMax - (mBufWidthMax % 4);
+    size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
   } else {
-    size = IntSize((mBufHeightMax * ((float) innerWidth/innerHeight)), mBufHeightMax);
+    int32_t tmpWidth = (mBufHeightMax * ((float) innerWidth/innerHeight));
+    int32_t width =  tmpWidth - (tmpWidth % 4);
+    size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
   }
 
   gfxImageFormat format = gfxImageFormat::RGB24;
   uint32_t stride = gfxASurface::FormatStrideForWidth(format, size.width);
 
   if (mDataSize < static_cast<size_t>(stride * size.height)) {
     mDataSize = stride * size.height;
     mData = static_cast<unsigned char*>(malloc(mDataSize));
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -657,17 +657,16 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
     return NS_ERROR_FAILURE;
   }
 
 #if defined(XP_WIN)
   void** pvalue = (void**)value;
   nsViewManager* vm = mPluginFrame->PresContext()->GetPresShell()->GetViewManager();
   if (!vm)
     return NS_ERROR_FAILURE;
-#if defined(XP_WIN)
   // This property is provided to allow a "windowless" plugin to determine the window it is drawing
   // in, so it can translate mouse coordinates it receives directly from the operating system
   // to coordinates relative to itself.
 
   // The original code (outside this #if) returns the document's window, which is OK if the window the "windowless" plugin
   // is drawing into has the same origin as the document's window, but this is not the case for "windowless" plugins inside of scrolling DIVs etc
 
   // To make sure "windowless" plugins always get the right origin for translating mouse coordinates, this code
@@ -676,17 +675,18 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
   // Given that this HWND may not be that of the document's window, there is a slight risk
   // of confusing a plugin that is using this HWND for illicit purposes, but since the documentation
   // does not suggest this HWND IS that of the document window, rather that of the window
   // the plugin is drawn in, this seems like a safe fix.
 
   // we only attempt to get the nearest window if this really is a "windowless" plugin so as not
   // to change any behaviour for the much more common windowed plugins,
   // though why this method would even be being called for a windowed plugin escapes me.
-  if (mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) {
+  if (XRE_GetProcessType() != GeckoProcessType_Content &&
+      mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) {
     // it turns out that flash also uses this window for determining focus, and is currently
     // unable to show a caret correctly if we return the enclosing window. Therefore for
     // now we only return the enclosing window when there is an actual offset which
     // would otherwise cause coordinates to be offset incorrectly. (i.e.
     // if the enclosing window if offset from the document window)
     //
     // fixing both the caret and ability to interact issues for a windowless control in a non document aligned windw
     // does not seem to be possible without a change to the flash plugin
@@ -701,22 +701,21 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
         // in the case the two windows are offset from eachother, we do go ahead and return the correct enclosing window
         // so that mouse co-ordinates are not messed up.
         *pvalue = (void*)win->GetNativeData(NS_NATIVE_WINDOW);
         if (*pvalue)
           return NS_OK;
       }
     }
   }
-#endif
   // simply return the topmost document window
   nsCOMPtr<nsIWidget> widget;
   vm->GetRootWidget(getter_AddRefs(widget));
   if (widget) {
-    *pvalue = (void*)widget->GetNativeData(NS_NATIVE_WINDOW);
+    *pvalue = (void*)widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW);
   } else {
     NS_ASSERTION(widget, "couldn't get doc's widget in getting doc's window handle");
   }
 
   return NS_OK;
 #elif (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)) && defined(MOZ_X11)
   // X11 window managers want the toplevel window for WM_TRANSIENT_FOR.
   nsIWidget* win = mPluginFrame->GetNearestWidget();
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -60,16 +60,17 @@ using mozilla::dom::CrashReporterChild;
 using mozilla::dom::PCrashReporterChild;
 
 #if defined(XP_WIN)
 const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
 const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
 #endif
 
 namespace {
+// see PluginModuleChild::GetChrome()
 PluginModuleChild* gChromeInstance = nullptr;
 nsTArray<PluginModuleChild*>* gAllInstances;
 }
 
 #ifdef MOZ_WIDGET_QT
 typedef void (*_gtk_init_fn)(int argc, char **argv);
 static _gtk_init_fn s_gtk_init = nullptr;
 static PRLibrary *sGtkLib = nullptr;
@@ -186,16 +187,19 @@ PluginModuleChild::~PluginModuleChild()
         gChromeInstance = nullptr;
     }
 }
 
 // static
 PluginModuleChild*
 PluginModuleChild::GetChrome()
 {
+    // A special PluginModuleChild instance that talks to the chrome process
+    // during startup and shutdown. Synchronous messages to or from this actor
+    // should be avoided because they may lead to hangs.
     MOZ_ASSERT(gChromeInstance);
     return gChromeInstance;
 }
 
 bool
 PluginModuleChild::CommonInit(base::ProcessHandle aParentProcessHandle,
                               MessageLoop* aIOLoop,
                               IPC::Channel* aChannel)
@@ -225,17 +229,16 @@ PluginModuleChild::InitForContent(base::
 {
     if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
         return false;
     }
 
     mTransport = aChannel;
 
     mLibrary = GetChrome()->mLibrary;
-    mQuirks = GetChrome()->mQuirks;
     mFunctions = GetChrome()->mFunctions;
 
     return true;
 }
 
 bool
 PluginModuleChild::RecvDisableFlashProtectedMode()
 {
@@ -2057,17 +2060,23 @@ PPluginInstanceChild*
 PluginModuleChild::AllocPPluginInstanceChild(const nsCString& aMimeType,
                                              const uint16_t& aMode,
                                              const InfallibleTArray<nsCString>& aNames,
                                              const InfallibleTArray<nsCString>& aValues)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
 
-    InitQuirksModes(aMimeType);
+    // In e10s, gChromeInstance hands out quirks to instances, but never
+    // allocates an instance on its own. Make sure it gets the latest copy
+    // of quirks once we have them. Also note, with process-per-tab, we may
+    // have multiple PluginModuleChilds in the same plugin process, so only
+    // initialize this once in gChromeInstance, which is a singleton.
+    GetChrome()->InitQuirksModes(aMimeType);
+    mQuirks = GetChrome()->mQuirks;
 
 #ifdef XP_WIN
     if ((mQuirks & QUIRK_FLASH_HOOK_GETWINDOWINFO) &&
         !sGetWindowInfoPtrStub) {
         sUser32Intercept.Init("user32.dll");
         sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(PMCGetWindowInfoHook),
                                  (void**) &sGetWindowInfoPtrStub);
     }
--- a/dom/requestsync/RequestSyncWifiService.h
+++ b/dom/requestsync/RequestSyncWifiService.h
@@ -19,17 +19,17 @@ class RequestSyncWifiService MOZ_FINAL :
 {
 public:
   NS_DECL_ISUPPORTS
 
   static void Init();
 
   static already_AddRefed<RequestSyncWifiService> GetInstance();
 
-  void Notify(const hal::NetworkInformation& aNetworkInfo);
+  void Notify(const hal::NetworkInformation& aNetworkInfo) MOZ_OVERRIDE;
 
 private:
   RequestSyncWifiService()
     : mIsWifi(false)
   {}
 
   ~RequestSyncWifiService()
   {}
--- a/dom/smil/test/test_smilChangeAfterFrozen.xhtml
+++ b/dom/smil/test/test_smilChangeAfterFrozen.xhtml
@@ -375,28 +375,30 @@ function testPercentUnitChangeOnLength()
 
   gSvg.setCurrentTime(0); // Force synchronous sample so animation takes effect
   // Due to bug 627594 (SVGLength.value for percent value lengths doesn't
   // reflect updated viewport until reflow) the following will fail.
   // Check that it does indeed fail so that when that bug is fixed this test
   // can be updated.
   todo_is(gCircle.cy.animVal.value, 100,
      "Checking animated length=100% after animating but before reflow");
-  gSvg.forceRedraw();
+  // force a layout flush (Bug 627594)
+  gSvg.getCTM();
   // Even after doing a reflow though we'll still fail due to bug 508206
   // (Relative units used in animation don't update immediately)
   todo_is(gCircle.cy.animVal.value, 100,
      "Checking animated length=100% after animating but before resampling");
   gSvg.setCurrentTime(0);
   // Now we should be up to date
   is(gCircle.cy.animVal.value, 100,
      "Checking animated length=100% after animating");
 
   gSvg.setAttribute("height", "50px"); // Change: height: 50px
-  gSvg.forceRedraw(); // Bug 627594
+  // force a layout flush (Bug 627594)
+  gSvg.getCTM();
   gSvg.setCurrentTime(0); // Bug 508206
   is(gCircle.cy.animVal.value, 50,
      "Checking animated length=100% after updating context");
 
   gSvg.setAttribute("height", oldHeight);
   gCircle.removeChild(gCircle.firstChild);
 }
 
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -300,25 +300,19 @@ SVGSVGElement::UnsuspendRedraw(uint32_t 
 /* void unsuspendRedrawAll (); */
 void
 SVGSVGElement::UnsuspendRedrawAll()
 {
   // no-op
 }
 
 void
-SVGSVGElement::ForceRedraw(ErrorResult& rv)
+SVGSVGElement::ForceRedraw()
 {
-  nsIDocument* doc = GetComposedDoc();
-  if (!doc) {
-    rv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  doc->FlushPendingNotifications(Flush_Display);
+  // no-op
 }
 
 void
 SVGSVGElement::PauseAnimations()
 {
   if (mTimedDocumentRoot) {
     mTimedDocumentRoot->Pause(nsSMILTimeContainer::PAUSE_SCRIPT);
   }
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -241,17 +241,17 @@ public:
   bool UseCurrentView();
   float CurrentScale();
   void SetCurrentScale(float aCurrentScale);
   already_AddRefed<nsISVGPoint> CurrentTranslate();
   void SetCurrentTranslate(float x, float y);
   uint32_t SuspendRedraw(uint32_t max_wait_milliseconds);
   void UnsuspendRedraw(uint32_t suspend_handle_id);
   void UnsuspendRedrawAll();
-  void ForceRedraw(ErrorResult& rv);
+  void ForceRedraw();
   void PauseAnimations();
   void UnpauseAnimations();
   bool AnimationsPaused();
   float GetCurrentTime();
   void SetCurrentTime(float seconds);
   void DeselectAll();
   already_AddRefed<DOMSVGNumber> CreateSVGNumber();
   already_AddRefed<DOMSVGLength> CreateSVGLength();
--- a/dom/tests/mochitest/fetch/worker_test_fetch_basic.js
+++ b/dom/tests/mochitest/fetch/worker_test_fetch_basic.js
@@ -18,17 +18,19 @@ function testAboutURL() {
     is(res.headers.get('content-type'), 'text/html;charset=utf-8',
        "about:blank content-type should be text/html;charset=utf-8");
     return res.text().then(function(v) {
       is(v, "", "about:blank body should be empty");
     });
   });
 
   var p2 = fetch('about:config').then(function(res) {
-    is(res.type, "error", "about:config should fail");
+    ok(false, "about:config should fail");
+  }, function(e) {
+    ok(e instanceof TypeError, "about:config should fail");
   });
 
   return Promise.all([p1, p2]);
 }
 
 function testDataURL() {
   return fetch("data:text/plain;charset=UTF-8,Hello").then(function(res) {
     ok(true, "Data URL fetch should resolve");
--- a/dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
+++ b/dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
@@ -38,18 +38,19 @@ function testURL() {
 }
 
 var failFiles = [['ftp://localhost' + path + 'file_XHR_pass1.xml', 'GET']];
 
 function testURLFail() {
   var promises = [];
   failFiles.forEach(function(entry) {
     var p = fetch(entry[0]).then(function(res) {
-      ok(res.type === "error", "Response should be an error for " + entry[0]);
-      is(res.status, 0, "Response status should be 0 for " + entry[0]);
+      ok(false, "Response should be an error for " + entry[0]);
+    }, function(e) {
+      ok(e instanceof TypeError, "Response should be an error for " + entry[0]);
     });
     promises.push(p);
   });
 
   return Promise.all(promises);
 }
 
 function testRequestGET() {
@@ -103,31 +104,30 @@ function testResponses() {
         is(res.status, 200, "status should match");
         return res.json().then((v) => {
           is(JSON.stringify(v), jsonBody, "json response should match");
         });
       });
       resolve(p);
     }),
 
-    // FIXME(nsm): Enable once Bug 1107777 and Bug 1072144 have been fixed.
-    //new Promise((resolve, reject) => {
-    //  var req = new Request(path + 'responseIdentical.sjs', {
-    //                          method: 'POST',
-    //                          body: '{',
-    //                        });
-    //  var p = fetch(req).then((res) => {
-    //    is(res.status, 200, "wrong status");
-    //    return res.json().then(
-    //      (v) => ok(false, "expected json parse failure"),
-    //      (e) => ok(true, "expected json parse failure")
-    //    );
-    //  });
-    //  resolve(p);
-    //}),
+    new Promise((resolve, reject) => {
+      var req = new Request(path + 'responseIdentical.sjs', {
+                              method: 'POST',
+                              body: '{',
+                            });
+      var p = fetch(req).then((res) => {
+        is(res.status, 200, "wrong status");
+        return res.json().then(
+          (v) => ok(false, "expected json parse failure"),
+          (e) => ok(true, "expected json parse failure")
+        );
+      });
+      resolve(p);
+    }),
   ];
 
   return Promise.all(fetches);
 }
 
 function testBlob() {
   return fetch(path + '/file_XHR_binary2.bin').then((r) => {
     ok(r.status, 200, "status should match");
--- a/dom/tests/mochitest/fetch/worker_test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/worker_test_fetch_cors.js
@@ -7,29 +7,27 @@ if (typeof ok !== "function") {
 if (typeof is !== "function") {
   function is(a, b, msg) {
     postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
   }
 }
 
 var path = "/tests/dom/base/test/";
 
-function isNetworkError(response) {
-  return response.type == "error" && response.status === 0 && response.statusText === "";
-}
-
 function isOpaqueResponse(response) {
   return response.type == "opaque" && response.status === 0 && response.statusText === "";
 }
 
 function testModeSameOrigin() {
   // Fetch spec Section 4, step 4, "request's mode is same-origin".
   var req = new Request("http://example.com", { mode: "same-origin" });
   return fetch(req).then(function(res) {
-    ok(isNetworkError(res), "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
+    ok(false, "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
+  }, function(e) {
+    ok(e instanceof TypeError, "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
   });
 }
 
 function testNoCorsCtor() {
   // Request constructor Step 19.1
   var simpleMethods = ["GET", "HEAD", "POST"];
   for (var i = 0; i < simpleMethods.length; ++i) {
     var r = new Request("http://example.com", { method: simpleMethods[i], mode: "no-cors" });
@@ -61,16 +59,111 @@ function testModeNoCors() {
   // Fetch spec, section 4, step 4, response tainting should be set opaque, so
   // that fetching leads to an opaque filtered response in step 8.
   var r = new Request("http://example.com" + corsServerPath + "status=200&allowOrigin=*", { mode: "no-cors" });
   return fetch(r).then(function(res) {
     ok(isOpaqueResponse(res), "no-cors Request fetch should result in opaque response");
   });
 }
 
+function testSameOriginCredentials() {
+  var cookieStr = "type=chocolatechip";
+  var tests = [
+              {
+                // Initialize by setting a cookie.
+                pass: 1,
+                setCookie: cookieStr,
+                withCred: "same-origin",
+              },
+              {
+                // Default mode is "omit".
+                pass: 1,
+                noCookie: 1,
+              },
+              {
+                pass: 1,
+                noCookie: 1,
+                withCred: "omit",
+              },
+              {
+                pass: 1,
+                cookie: cookieStr,
+                withCred: "same-origin",
+              },
+              {
+                pass: 1,
+                cookie: cookieStr,
+                withCred: "include",
+              },
+              ];
+
+  var finalPromiseResolve, finalPromiseReject;
+  var finalPromise = new Promise(function(res, rej) {
+    finalPromiseResolve = res;
+    finalPromiseReject = rej;
+  });
+
+  function makeRequest(test) {
+    req = {
+      // Add a default query param just to make formatting the actual params
+      // easier.
+      url: corsServerPath + "a=b",
+      method: test.method,
+      headers: test.headers,
+      withCred: test.withCred,
+    };
+
+    if (test.setCookie)
+      req.url += "&setCookie=" + escape(test.setCookie);
+    if (test.cookie)
+      req.url += "&cookie=" + escape(test.cookie);
+    if (test.noCookie)
+      req.url += "&noCookie";
+
+    return new Request(req.url, { method: req.method,
+                                  headers: req.headers,
+                                  credentials: req.withCred });
+  }
+
+  function testResponse(res, test) {
+    ok(test.pass, "Expected test to pass " + test.toSource());
+    is(res.status, 200, "wrong status in test for " + test.toSource());
+    is(res.statusText, "OK", "wrong status text for " + test.toSource());
+    return res.text().then(function(v) {
+      is(v, "<res>hello pass</res>\n",
+       "wrong text in test for " + test.toSource());
+    });
+  }
+
+  function runATest(tests, i) {
+    var test = tests[i];
+    var request = makeRequest(test);
+    console.log(request.url);
+    fetch(request).then(function(res) {
+      testResponse(res, test);
+      if (i < tests.length-1) {
+        runATest(tests, i+1);
+      } else {
+        finalPromiseResolve();
+      }
+    }, function(e) {
+      ok(!test.pass, "Expected test to fail " + test.toSource());
+      ok(e instanceof TypeError, "Test should fail " + test.toSource());
+      if (i < tests.length-1) {
+        runATest(tests, i+1);
+      } else {
+        finalPromiseResolve();
+      }
+    });
+  }
+
+  runATest(tests, 0);
+  return finalPromise;
+}
+
 function testModeCors() {
   var tests = [// Plain request
                { pass: 1,
                  method: "GET",
                  noAllowPreflight: 1,
                },
 
                // undefined username
@@ -655,153 +748,152 @@ function testModeCors() {
       req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
     if (test.preflightBody)
       req.url += "&preflightBody=" + escape(test.preflightBody);
 
     var request = new Request(req.url, { method: req.method, mode: "cors",
                                          headers: req.headers, body: req.body });
     fetches.push((function(test, request) {
       return fetch(request).then(function(res) {
-      dump("Response for " + request.url + "\n");
-        if (test.pass) {
-          ok(!isNetworkError(res),
-            "shouldn't have failed in test for " + test.toSource());
-          if (test.status) {
-            is(res.status, test.status, "wrong status in test for " + test.toSource());
-            is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource());
-          }
-          else {
-            is(res.status, 200, "wrong status in test for " + test.toSource());
-            is(res.statusText, "OK", "wrong status text for " + test.toSource());
-          }
-          if (test.responseHeaders) {
-            for (header in test.responseHeaders) {
-              if (test.expectedResponseHeaders.indexOf(header) == -1) {
-                is(res.headers.has(header), false,
-                   "|Headers.has()|wrong response header (" + header + ") in test for " +
-                   test.toSource());
-              }
-              else {
-                is(res.headers.get(header), test.responseHeaders[header],
-                   "|Headers.get()|wrong response header (" + header + ") in test for " +
-                   test.toSource());
-              }
-            }
-          }
-
-          return res.text().then(function(v) {
-            if (test.method !== "HEAD") {
-              is(v, "<res>hello pass</res>\n",
-                 "wrong responseText in test for " + test.toSource());
+        ok(test.pass, "Expected test to pass for " + test.toSource());
+        if (test.status) {
+          is(res.status, test.status, "wrong status in test for " + test.toSource());
+          is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource());
+        }
+        else {
+          is(res.status, 200, "wrong status in test for " + test.toSource());
+          is(res.statusText, "OK", "wrong status text for " + test.toSource());
+        }
+        if (test.responseHeaders) {
+          for (header in test.responseHeaders) {
+            if (test.expectedResponseHeaders.indexOf(header) == -1) {
+              is(res.headers.has(header), false,
+                 "|Headers.has()|wrong response header (" + header + ") in test for " +
+                 test.toSource());
             }
             else {
-              is(v, "",
-                 "wrong responseText in HEAD test for " + test.toSource());
-            }
-          });
-        }
-        else {
-          ok(isNetworkError(res),
-            "should have failed in test for " + test.toSource());
-          is(res.status, 0, "wrong status in test for " + test.toSource());
-          is(res.statusText, "", "wrong status text for " + test.toSource());
-          if (test.responseHeaders) {
-            for (header in test.responseHeaders) {
-              is(res.headers.get(header), null,
-                 "wrong response header (" + header + ") in test for " +
+              is(res.headers.get(header), test.responseHeaders[header],
+                 "|Headers.get()|wrong response header (" + header + ") in test for " +
                  test.toSource());
             }
           }
+        }
 
-          return res.text().then(function(v) {
+        return res.text().then(function(v) {
+          if (test.method !== "HEAD") {
+            is(v, "<res>hello pass</res>\n",
+               "wrong responseText in test for " + test.toSource());
+          }
+          else {
             is(v, "",
-               "wrong responseText in test for " + test.toSource());
-          });
-        }
+               "wrong responseText in HEAD test for " + test.toSource());
+          }
+        });
+      }, function(e) {
+          ok(!test.pass, "Expected test failure for " + test.toSource());
+          ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
       });
     })(test, request));
   }
 
   return Promise.all(fetches);
 }
 
-function testCredentials() {
+function testCrossOriginCredentials() {
   var tests = [
            { pass: 1,
              method: "GET",
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
            },
            { pass: 0,
              method: "GET",
-             withCred: 1,
+             withCred: "include",
              allowCred: 0,
            },
            { pass: 0,
              method: "GET",
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
              origin: "*",
            },
            { pass: 1,
              method: "GET",
-             withCred: 0,
+             withCred: "omit",
              allowCred: 1,
              origin: "*",
            },
            { pass: 1,
              method: "GET",
              setCookie: "a=1",
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
            },
            { pass: 1,
              method: "GET",
              cookie: "a=1",
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
            },
            { pass: 1,
              method: "GET",
              noCookie: 1,
-             withCred: 0,
+             withCred: "omit",
              allowCred: 1,
            },
            { pass: 0,
              method: "GET",
              noCookie: 1,
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
            },
            { pass: 1,
              method: "GET",
              setCookie: "a=2",
-             withCred: 0,
+             withCred: "omit",
              allowCred: 1,
            },
            { pass: 1,
              method: "GET",
              cookie: "a=1",
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
            },
            { pass: 1,
              method: "GET",
              setCookie: "a=2",
-             withCred: 1,
+             withCred: "include",
              allowCred: 1,
            },
            { pass: 1,
              method: "GET",
              cookie: "a=2",
-             withCred: 1,
+             withCred: "include",
+             allowCred: 1,
+           },
+           {
+             // When credentials mode is same-origin, but mode is cors, no
+             // cookie should be sent cross origin.
+             pass: 0,
+             method: "GET",
+             cookie: "a=2",
+             withCred: "same-origin",
              allowCred: 1,
            },
+           {
+             // When credentials mode is same-origin, but mode is cors, no
+             // cookie should be sent cross origin. This test checks the same
+             // thing as above, but uses the noCookie check on the server
+             // instead, and expects a valid response.
+             pass: 1,
+             method: "GET",
+             noCookie: 1,
+             withCred: "same-origin",
+           },
            ];
-           // FIXME(nsm): Add "same-origin" credentials test
 
   var baseURL = "http://example.org" + corsServerPath;
   var origin = "http://mochi.test:8888";
 
   var finalPromiseResolve, finalPromiseReject;
   var finalPromise = new Promise(function(res, rej) {
     finalPromiseResolve = res;
     finalPromiseReject = rej;
@@ -827,54 +919,51 @@ function testCredentials() {
 
     if ("allowHeaders" in test)
       req.url += "&allowHeaders=" + escape(test.allowHeaders);
     if ("allowMethods" in test)
       req.url += "&allowMethods=" + escape(test.allowMethods);
 
     return new Request(req.url, { method: req.method,
                                   headers: req.headers,
-                                  credentials: req.withCred ? "include" : "omit" });
+                                  credentials: req.withCred });
   }
 
   function testResponse(res, test) {
-    if (test.pass) {
-      is(isNetworkError(res), false,
-        "shouldn't have failed in test for " + test.toSource());
-      is(res.status, 200, "wrong status in test for " + test.toSource());
-      is(res.statusText, "OK", "wrong status text for " + test.toSource());
-      return res.text().then(function(v) {
-        is(v, "<res>hello pass</res>\n",
-         "wrong text in test for " + test.toSource());
-      });
-    }
-    else {
-      is(isNetworkError(res), true,
-        "should have failed in test for " + test.toSource());
-      return res.text().then(function(v) {
-        is(v, "",
-         "wrong text in test for " + test.toSource());
-      });
-    }
+    ok(test.pass, "Expected test to pass for " + test.toSource());
+    is(res.status, 200, "wrong status in test for " + test.toSource());
+    is(res.statusText, "OK", "wrong status text for " + test.toSource());
+    return res.text().then(function(v) {
+      is(v, "<res>hello pass</res>\n",
+       "wrong text in test for " + test.toSource());
+    });
   }
 
-  function runATest(i) {
+  function runATest(tests, i) {
     var test = tests[i];
     var request = makeRequest(test);
     fetch(request).then(function(res) {
       testResponse(res, test);
       if (i < tests.length-1) {
-        runATest(i+1);
+        runATest(tests, i+1);
       } else {
         finalPromiseResolve();
       }
-    }, finalPromiseReject);
+    }, function(e) {
+      ok(!test.pass, "Expected test failure for " + test.toSource());
+      ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
+      if (i < tests.length-1) {
+        runATest(tests, i+1);
+      } else {
+        finalPromiseResolve();
+      }
+    });
   }
 
-  runATest(0);
+  runATest(tests, 0);
   return finalPromise;
 }
 
 function testRedirects() {
   var origin = "http://mochi.test:8888";
 
   var tests = [
            { pass: 1,
@@ -1148,37 +1237,27 @@ function testRedirects() {
         req.url += "&body=" + escape(test.body);
     }
 
     var request = new Request(req.url, { method: req.method,
                                          headers: req.headers,
                                          body: req.body });
     fetches.push((function(request, test) {
       return fetch(request).then(function(res) {
-        if (test.pass) {
-          is(isNetworkError(res), false,
-            "shouldn't have failed in test for " + test.toSource());
-          is(res.status, 200, "wrong status in test for " + test.toSource());
-          is(res.statusText, "OK", "wrong status text for " + test.toSource());
-          is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
-          return res.text().then(function(v) {
-            is(v, "<res>hello pass</res>\n",
-               "wrong responseText in test for " + test.toSource());
-          });
-        }
-        else {
-          is(isNetworkError(res), true,
-            "should have failed in test for " + test.toSource());
-          is(res.status, 0, "wrong status in test for " + test.toSource());
-          is(res.statusText, "", "wrong status text for " + test.toSource());
-          return res.text().then(function(v) {
-            is(v, "",
-               "wrong responseText in test for " + test.toSource());
-          });
-        }
+        ok(test.pass, "Expected test to pass for " + test.toSource());
+        is(res.status, 200, "wrong status in test for " + test.toSource());
+        is(res.statusText, "OK", "wrong status text for " + test.toSource());
+        is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
+        return res.text().then(function(v) {
+          is(v, "<res>hello pass</res>\n",
+             "wrong responseText in test for " + test.toSource());
+        });
+      }, function(e) {
+        ok(!test.pass, "Expected test failure for " + test.toSource());
+        ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
       });
     })(request, test));
   }
 
   return Promise.all(fetches);
 }
 
 function runTest() {
@@ -1191,17 +1270,18 @@ function runTest() {
   }
 
   testNoCorsCtor();
 
   Promise.resolve()
     .then(testModeSameOrigin)
     .then(testModeNoCors)
     .then(testModeCors)
-    .then(testCredentials)
+    .then(testSameOriginCredentials)
+    .then(testCrossOriginCredentials)
     .then(testRedirects)
     // Put more promise based tests here.
     .then(done)
     .catch(function(e) {
       ok(false, "Some test failed " + e);
       done();
     });
 }
rename from dom/webidl/InstallPhaseEvent.webidl
rename to dom/webidl/ExtendableEvent.webidl
--- a/dom/webidl/InstallPhaseEvent.webidl
+++ b/dom/webidl/ExtendableEvent.webidl
@@ -2,17 +2,18 @@
 /* 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/.
  *
  * For more information on this interface, please see
  * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
  */
 
-// While not explicitly restricted to ServiceWorkerGlobalScope, it probably
-// should be. https://github.com/slightlyoff/ServiceWorker/issues/254
-[Constructor(DOMString type, optional EventInit eventInitDict),
- Func="mozilla::dom::workers::ServiceWorkerEventsVisible",
- Exposed=(ServiceWorker,Window)]
-interface InstallPhaseEvent : Event {
+[Constructor(DOMString type, optional ExtendableEventInit eventInitDict),
+ Exposed=ServiceWorker]
+interface ExtendableEvent : Event {
   // https://github.com/slightlyoff/ServiceWorker/issues/261
   void waitUntil(Promise<any> p);
 };
+
+dictionary ExtendableEventInit : EventInit {
+  // Defined for the forward compatibility across the derived events
+};
--- a/dom/webidl/InstallEvent.webidl
+++ b/dom/webidl/InstallEvent.webidl
@@ -2,30 +2,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * For more information on this interface, please see
  * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
  */
 
-// While not explicitly restricted to ServiceWorkerGlobalScope, it probably
-// should be. https://github.com/slightlyoff/ServiceWorker/issues/254
 [Constructor(DOMString type, optional InstallEventInit eventInitDict),
- Func="mozilla::dom::workers::ServiceWorkerEventsVisible",
- // XXXbz I have no idea where this should be exposed.  The spec makes
- // no sense.  But since it returns a ServiceWorker and that's only
- // exposed in Window, let's say Window.
- Exposed=Window]
-interface InstallEvent : InstallPhaseEvent {
-  // The currently active worker for this scope when this worker is asked to
-  // install itself.
-  // This may be null when a ServiceWorker is being installed for a previously
-  // uncontrolled scope.
-  // https://github.com/slightlyoff/ServiceWorker/issues/260
+ Exposed=ServiceWorker]
+interface InstallEvent : ExtendableEvent {
   readonly attribute ServiceWorker? activeWorker;
   void replace();
 };
 
 // Should be in the spec soon to satisfy conventions about events.
 // https://github.com/slightlyoff/ServiceWorker/issues/216.
 dictionary InstallEventInit : EventInit {
   ServiceWorker? activeWorker = null;
--- a/dom/webidl/SVGSVGElement.webidl
+++ b/dom/webidl/SVGSVGElement.webidl
@@ -23,20 +23,23 @@ interface SVGSVGElement : SVGGraphicsEle
   readonly attribute float pixelUnitToMillimeterY;
   readonly attribute float screenPixelToMillimeterX;
   readonly attribute float screenPixelToMillimeterY;
   readonly attribute boolean useCurrentView;
   // readonly attribute SVGViewSpec currentView;
            attribute float currentScale;
   readonly attribute SVGPoint currentTranslate;
 
+  [DependsOn=Nothing, Affects=Nothing]
   unsigned long suspendRedraw(unsigned long maxWaitMilliseconds);
+  [DependsOn=Nothing, Affects=Nothing]
   void unsuspendRedraw(unsigned long suspendHandleID);
+  [DependsOn=Nothing, Affects=Nothing]
   void unsuspendRedrawAll();
-  [Throws]
+  [DependsOn=Nothing, Affects=Nothing]
   void forceRedraw();
   void pauseAnimations();
   void unpauseAnimations();
   boolean animationsPaused();
   float getCurrentTime();
   void setCurrentTime(float seconds);
   // NodeList getIntersectionList(SVGRect rect, SVGElement referenceElement);
   // NodeList getEnclosureList(SVGRect rect, SVGElement referenceElement);
--- a/dom/webidl/ServiceWorker.webidl
+++ b/dom/webidl/ServiceWorker.webidl
@@ -5,19 +5,19 @@
  *
  * The origin of this IDL file is
  * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-obj
  *
  */
 
 // Still unclear what should be subclassed.
 // https://github.com/slightlyoff/ServiceWorker/issues/189
-[Pref="dom.serviceWorkers.enabled",
- // FIXME(nsm): Bug 1113522. Should also be exposed on Workers too.
- Exposed=Window]
+[Func="mozilla::dom::workers::ServiceWorkerVisible",
+ // FIXME(nsm): Bug 1113522. This is exposed to satisfy webidl constraints, but it won't actually work.
+ Exposed=(ServiceWorker,Window)]
 interface ServiceWorker : EventTarget {
   readonly attribute USVString scriptURL;
   readonly attribute ServiceWorkerState state;
 
   attribute EventHandler onstatechange;
 };
 
 ServiceWorker implements AbstractWorker;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -125,16 +125,17 @@ WEBIDL_FILES = [
     'DynamicsCompressorNode.webidl',
     'Element.webidl',
     'EngineeringMode.webidl',
     'Event.webidl',
     'EventHandler.webidl',
     'EventListener.webidl',
     'EventSource.webidl',
     'EventTarget.webidl',
+    'ExtendableEvent.webidl',
     'Fetch.webidl',
     'File.webidl',
     'FileList.webidl',
     'FileMode.webidl',
     'FileReader.webidl',
     'FileReaderSync.webidl',
     'FocusEvent.webidl',
     'FontFace.webidl',
@@ -240,17 +241,16 @@ WEBIDL_FILES = [
     'Identity.webidl',
     'ImageCapture.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputEvent.webidl',
     'InputMethod.webidl',
     'InspectorUtils.webidl',
     'InstallEvent.webidl',
-    'InstallPhaseEvent.webidl',
     'InterAppConnection.webidl',
     'InterAppConnectionRequest.webidl',
     'InterAppMessagePort.webidl',
     'KeyAlgorithm.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
--- a/dom/workers/ServiceWorker.cpp
+++ b/dom/workers/ServiceWorker.cpp
@@ -4,20 +4,38 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorker.h"
 
 #include "nsPIDOMWindow.h"
 #include "SharedWorker.h"
 #include "WorkerPrivate.h"
 
+#include "mozilla/Preferences.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 
 using namespace mozilla::dom;
-USING_WORKERS_NAMESPACE
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+bool
+ServiceWorkerVisible(JSContext* aCx, JSObject* aObj)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.serviceWorkers.enabled", false);
+  }
+
+  ServiceWorkerGlobalScope* scope = nullptr;
+  nsresult rv = UnwrapObject<prototypes::id::ServiceWorkerGlobalScope_workers,
+                             mozilla::dom::ServiceWorkerGlobalScopeBinding_workers::NativeType>(aObj, scope);
+  return NS_SUCCEEDED(rv);
+}
 
 ServiceWorker::ServiceWorker(nsPIDOMWindow* aWindow,
                              SharedWorker* aSharedWorker)
   : DOMEventTargetHelper(aWindow),
     mState(ServiceWorkerState::Installing),
     mSharedWorker(aSharedWorker)
 {
   AssertIsOnMainThread();
@@ -50,8 +68,12 @@ WorkerPrivate*
 ServiceWorker::GetWorkerPrivate() const
 {
   // At some point in the future, this may be optimized to terminate a worker
   // that hasn't been used in a certain amount of time or when there is memory
   // pressure or similar.
   MOZ_ASSERT(mSharedWorker);
   return mSharedWorker->GetWorkerPrivate();
 }
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
--- a/dom/workers/ServiceWorker.h
+++ b/dom/workers/ServiceWorker.h
@@ -16,16 +16,19 @@ namespace mozilla {
 namespace dom {
 
 class Promise;
 
 namespace workers {
 
 class SharedWorker;
 
+bool
+ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
+
 class ServiceWorker MOZ_FINAL : public DOMEventTargetHelper
 {
   friend class RuntimeService;
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorker, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(statechange)
@@ -36,21 +39,33 @@ public:
 
   ServiceWorkerState
   State() const
   {
     return mState;
   }
 
   void
+  SetState(ServiceWorkerState aState)
+  {
+    mState = aState;
+  }
+
+  void
   GetScriptURL(nsString& aURL) const
   {
     aURL = mURL;
   }
 
+  void
+  DispatchStateChange()
+  {
+    DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
+  }
+
   WorkerPrivate*
   GetWorkerPrivate() const;
 
 private:
   // This class can only be created from the RuntimeService.
   ServiceWorker(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker);
 
   // This class is reference-counted and will be destroyed from Release().
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -6,62 +6,52 @@
 
 #include "ServiceWorkerEvents.h"
 
 #include "nsContentUtils.h"
 
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/workers/bindings/ServiceWorker.h"
-#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 
 using namespace mozilla::dom;
 
 BEGIN_WORKERS_NAMESPACE
 
-bool
-ServiceWorkerEventsVisible(JSContext* aCx, JSObject* aObj)
-{
-  ServiceWorkerGlobalScope* scope = nullptr;
-  nsresult rv = UnwrapObject<prototypes::id::ServiceWorkerGlobalScope_workers,
-                             mozilla::dom::ServiceWorkerGlobalScopeBinding_workers::NativeType>(aObj, scope);
-  return NS_SUCCEEDED(rv) && scope;
-}
-
-InstallPhaseEvent::InstallPhaseEvent(EventTarget* aOwner)
+ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
   : Event(aOwner, nullptr, nullptr)
 {
 }
 
 void
-InstallPhaseEvent::WaitUntil(Promise& aPromise)
+ExtendableEvent::WaitUntil(Promise& aPromise)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   // Only first caller counts.
   if (EventPhase() == AT_TARGET && !mPromise) {
     mPromise = &aPromise;
   }
 }
 
-NS_IMPL_ADDREF_INHERITED(InstallPhaseEvent, Event)
-NS_IMPL_RELEASE_INHERITED(InstallPhaseEvent, Event)
+NS_IMPL_ADDREF_INHERITED(ExtendableEvent, Event)
+NS_IMPL_RELEASE_INHERITED(ExtendableEvent, Event)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InstallPhaseEvent)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ExtendableEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallPhaseEvent, Event, mPromise)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ExtendableEvent, Event, mPromise)
 
 InstallEvent::InstallEvent(EventTarget* aOwner)
-  : InstallPhaseEvent(aOwner)
+  : ExtendableEvent(aOwner)
   , mActivateImmediately(false)
 {
 }
 
-NS_IMPL_ADDREF_INHERITED(InstallEvent, InstallPhaseEvent)
-NS_IMPL_RELEASE_INHERITED(InstallEvent, InstallPhaseEvent)
+NS_IMPL_ADDREF_INHERITED(InstallEvent, ExtendableEvent)
+NS_IMPL_RELEASE_INHERITED(InstallEvent, ExtendableEvent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InstallEvent)
-NS_INTERFACE_MAP_END_INHERITING(InstallPhaseEvent)
+NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallEvent, InstallPhaseEvent, mActiveWorker)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallEvent, ExtendableEvent, mActiveWorker)
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -2,63 +2,60 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_serviceworkerevents_h__
 #define mozilla_dom_workers_serviceworkerevents_h__
 
 #include "mozilla/dom/Event.h"
-#include "mozilla/dom/InstallPhaseEventBinding.h"
+#include "mozilla/dom/ExtendableEventBinding.h"
 #include "mozilla/dom/InstallEventBinding.h"
 
 namespace mozilla {
 namespace dom {
   class Promise;
 } // namespace dom
 } // namespace mozilla
 
 BEGIN_WORKERS_NAMESPACE
 
 class ServiceWorker;
 
-bool
-ServiceWorkerEventsVisible(JSContext* aCx, JSObject* aObj);
-
-class InstallPhaseEvent : public Event
+class ExtendableEvent : public Event
 {
   nsRefPtr<Promise> mPromise;
 
 protected:
-  explicit InstallPhaseEvent(mozilla::dom::EventTarget* aOwner);
-  ~InstallPhaseEvent() {}
+  explicit ExtendableEvent(mozilla::dom::EventTarget* aOwner);
+  ~ExtendableEvent() {}
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallPhaseEvent, Event)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ExtendableEvent, Event)
   NS_FORWARD_TO_EVENT
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
   {
-    return mozilla::dom::InstallPhaseEventBinding::Wrap(aCx, this);
+    return mozilla::dom::ExtendableEventBinding::Wrap(aCx, this);
   }
 
-  static already_AddRefed<InstallPhaseEvent>
+  static already_AddRefed<ExtendableEvent>
   Constructor(mozilla::dom::EventTarget* aOwner,
               const nsAString& aType,
               const EventInit& aOptions)
   {
-    nsRefPtr<InstallPhaseEvent> e = new InstallPhaseEvent(aOwner);
+    nsRefPtr<ExtendableEvent> e = new ExtendableEvent(aOwner);
     bool trusted = e->Init(aOwner);
     e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
     e->SetTrusted(trusted);
     return e.forget();
   }
 
-  static already_AddRefed<InstallPhaseEvent>
+  static already_AddRefed<ExtendableEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const EventInit& aOptions,
               ErrorResult& aRv)
   {
     nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
     return Constructor(target, aType, aOptions);
   }
@@ -69,29 +66,29 @@ public:
   already_AddRefed<Promise>
   GetPromise() const
   {
     nsRefPtr<Promise> p = mPromise;
     return p.forget();
   }
 };
 
-class InstallEvent MOZ_FINAL : public InstallPhaseEvent
+class InstallEvent MOZ_FINAL : public ExtendableEvent
 {
   // FIXME(nsm): Bug 982787 will allow actually populating this.
   nsRefPtr<ServiceWorker> mActiveWorker;
   bool mActivateImmediately;
 
 protected:
   explicit InstallEvent(mozilla::dom::EventTarget* aOwner);
   ~InstallEvent() {}
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallEvent, InstallPhaseEvent)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallEvent, ExtendableEvent)
   NS_FORWARD_TO_EVENT
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
   {
     return mozilla::dom::InstallEventBinding::Wrap(aCx, this);
   }
 
   static already_AddRefed<InstallEvent>
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -14,26 +14,29 @@
 
 #include "jsapi.h"
 
 #include "mozilla/LoadContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/InstallEventBinding.h"
+#include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 
 #include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 
 #include "RuntimeService.h"
 #include "ServiceWorker.h"
 #include "ServiceWorkerClient.h"
+#include "ServiceWorkerContainer.h"
 #include "ServiceWorkerRegistration.h"
 #include "ServiceWorkerEvents.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #ifdef PostMessage
@@ -60,31 +63,30 @@ ServiceWorkerJob::Done(nsresult aStatus)
   }
 }
 
 void
 ServiceWorkerRegistrationInfo::Clear()
 {
   if (mInstallingWorker) {
     // FIXME(nsm): Terminate installing worker.
-    // Bug 1043701 Set state to redundant.
-    // Fire statechange.
+    mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
     mInstallingWorker = nullptr;
     // FIXME(nsm): Abort any inflight requests from installing worker.
   }
 
   if (mWaitingWorker) {
-    // FIXME(nsm): Bug 1043701 Set state to redundant.
+    mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     // Fire statechange.
     mWaitingWorker = nullptr;
     mWaitingToActivate = false;
   }
 
   if (mActiveWorker) {
-    // FIXME(nsm): Bug 1043701 Set state to redundant.
+    mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker = nullptr;
   }
 
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->InvalidateServiceWorkerRegistrationWorker(this,
                                                  WhichServiceWorker::INSTALLING_WORKER |
                                                  WhichServiceWorker::WAITING_WORKER |
@@ -229,16 +231,20 @@ public:
 
   virtual
   void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo)
   { }
 
   virtual
   void UpdateFailed(nsresult aStatus)
   { }
+
+  virtual
+  void UpdateFailed(const ErrorEventInit& aDesc)
+  { }
 };
 
 class ServiceWorkerResolveWindowPromiseOnUpdateCallback MOZ_FINAL : public ServiceWorkerUpdateFinishCallback
 {
   nsRefPtr<nsPIDOMWindow> mWindow;
   // The promise "returned" by the call to Update up to
   // navigator.serviceWorker.register().
   nsRefPtr<Promise> mPromise;
@@ -262,16 +268,94 @@ public:
     mPromise->MaybeResolve(swr);
   }
 
   void
   UpdateFailed(nsresult aStatus) MOZ_OVERRIDE
   {
     mPromise->MaybeReject(aStatus);
   }
+
+  void
+  UpdateFailed(const ErrorEventInit& aErrorDesc) MOZ_OVERRIDE
+  {
+    AutoJSAPI jsapi;
+    jsapi.Init(mWindow);
+
+    JSContext* cx = jsapi.cx();
+
+    JS::Rooted<JSString*> stack(cx, JS_GetEmptyString(JS_GetRuntime(cx)));
+
+    JS::Rooted<JS::Value> fnval(cx);
+    if (!ToJSValue(cx, aErrorDesc.mFilename, &fnval)) {
+      JS_ClearPendingException(cx);
+      mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      return;
+    }
+    JS::Rooted<JSString*> fn(cx, fnval.toString());
+
+    JS::Rooted<JS::Value> msgval(cx);
+    if (!ToJSValue(cx, aErrorDesc.mMessage, &msgval)) {
+      JS_ClearPendingException(cx);
+      mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      return;
+    }
+    JS::Rooted<JSString*> msg(cx, msgval.toString());
+
+    JS::Rooted<JS::Value> error(cx);
+    if (!JS::CreateError(cx, JSEXN_ERR, stack, fn, aErrorDesc.mLineno,
+                         aErrorDesc.mColno, nullptr, msg, &error)) {
+      JS_ClearPendingException(cx);
+      mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      return;
+    }
+
+    mPromise->MaybeReject(cx, error);
+  }
+};
+
+class ContinueUpdateRunnable MOZ_FINAL : public nsRunnable
+{
+  nsMainThreadPtrHandle<nsISupports> mJob;
+public:
+  explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<nsISupports> aJob)
+    : mJob(aJob)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run();
+};
+
+class CheckWorkerEvaluationAndContinueUpdateWorkerRunnable MOZ_FINAL : public WorkerRunnable
+{
+  const nsMainThreadPtrHandle<nsISupports> mJob;
+public:
+  CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+                                                       const nsMainThreadPtrHandle<nsISupports> aJob)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mJob(aJob)
+  { 
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
+      nsRefPtr<ContinueUpdateRunnable> r = new ContinueUpdateRunnable(mJob);
+      nsresult rv = NS_DispatchToMainThread(r);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch ContinueUpdateRunnable to main thread.");
+      }
+    }
+
+    return true;
+  }
 };
 
 class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob,
                                            public nsIStreamLoaderObserver
 {
   friend class FinishInstallRunnable;
 
   nsCString mScope;
@@ -320,17 +404,17 @@ public:
       nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
       nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
         swm->GetDomainInfo(mScope);
       MOZ_ASSERT(domainInfo);
       mRegistration = domainInfo->GetRegistration(mScope);
 
       if (mRegistration) {
         nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
-        if (newest && mScriptSpec.Equals(newest->GetScriptSpec()) &&
+        if (newest && mScriptSpec.Equals(newest->ScriptSpec()) &&
             mScriptSpec.Equals(mRegistration->mScriptSpec)) {
           mRegistration->mPendingUninstall = false;
           Succeed();
           Done(NS_OK);
           return;
         }
       } else {
         mRegistration = domainInfo->CreateNewRegistration(mScope);
@@ -373,20 +457,114 @@ public:
       Fail(NS_ERROR_DOM_NETWORK_ERR);
       return rv;
     }
 
 
     // FIXME(nsm): "Extract mime type..."
     // FIXME(nsm): Byte match to aString.
     NS_WARNING("Byte wise check is disabled, just using new one");
-    ContinueInstall();
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+      swm->GetDomainInfo(mRegistration->mScope);
+    MOZ_ASSERT(domainInfo);
+    MOZ_ASSERT(!domainInfo->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
+    domainInfo->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true);
+    // We have to create a ServiceWorker here simply to ensure there are no
+    // errors. Ideally we should just pass this worker on to ContinueInstall.
+    nsRefPtr<ServiceWorker> serviceWorker;
+    rv = swm->CreateServiceWorker(mRegistration->mScriptSpec,
+                                  mRegistration->mScope,
+                                  getter_AddRefs(serviceWorker));
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      Fail(NS_ERROR_DOM_ABORT_ERR);
+      return rv;
+    }
+
+    nsRefPtr<ServiceWorkerJob> upcasted = this;
+    nsMainThreadPtrHandle<nsISupports> handle(
+        new nsMainThreadPtrHolder<nsISupports>(upcasted));
+
+    nsRefPtr<CheckWorkerEvaluationAndContinueUpdateWorkerRunnable> r =
+      new CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(serviceWorker->GetWorkerPrivate(), handle);
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    bool ok = r->Dispatch(jsapi.cx());
+    if (NS_WARN_IF(!ok)) {
+      Fail(NS_ERROR_DOM_ABORT_ERR);
+      return rv;
+    }
+
     return NS_OK;
   }
 
+  // Public so our error handling code can use it.
+  void
+  Fail(const ErrorEventInit& aError)
+  {
+    MOZ_ASSERT(mCallback);
+    mCallback->UpdateFailed(aError);
+    mCallback = nullptr;
+    Done(NS_ERROR_DOM_JS_EXCEPTION);
+  }
+
+  // Public so our error handling code can continue with a successful worker.
+  void
+  ContinueInstall()
+  {
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+      swm->GetDomainInfo(mRegistration->mScope);
+    MOZ_ASSERT(domainInfo);
+    MOZ_ASSERT(domainInfo->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
+    domainInfo->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
+
+    if (mRegistration->mInstallingWorker) {
+      // FIXME(nsm): Terminate and stuff
+      mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
+    }
+
+    swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
+                                                   WhichServiceWorker::INSTALLING_WORKER);
+    mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec);
+    mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
+
+    Succeed();
+
+    nsRefPtr<QueueFireUpdateFoundRunnable> upr =
+      new QueueFireUpdateFoundRunnable(mRegistration);
+    NS_DispatchToMainThread(upr);
+
+    // XXXnsm this leads to double fetches right now, ideally we'll be able to
+    // use the persistent cache later.
+    nsRefPtr<ServiceWorkerJob> upcasted = this;
+    nsMainThreadPtrHandle<nsISupports> handle(
+        new nsMainThreadPtrHolder<nsISupports>(upcasted));
+
+    nsRefPtr<ServiceWorker> serviceWorker;
+    nsresult rv =
+      swm->CreateServiceWorker(mRegistration->mInstallingWorker->ScriptSpec(),
+                               mRegistration->mScope,
+                               getter_AddRefs(serviceWorker));
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ContinueAfterInstallEvent(false /* success */, false /* activate immediately */);
+      return;
+    }
+
+    nsRefPtr<InstallEventRunnable> r =
+      new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    r->Dispatch(jsapi.cx());
+  }
+
 private:
   void
   Update()
   {
     MOZ_ASSERT(mRegistration);
     nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::ContinueUpdate);
     NS_DispatchToMainThread(r);
@@ -395,17 +573,17 @@ private:
   // Aspects of (actually the whole algorithm) of [[Update]] after
   // "Run the following steps in parallel."
   void
   ContinueUpdate()
   {
     AssertIsOnMainThread();
     if (mRegistration->mInstallingWorker) {
       // FIXME(nsm): "Terminate installing worker".
-      // "Run the update state"
+      mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
       mRegistration->mInstallingWorker = nullptr;
     }
 
     // FIXME(nsm): Plug in FetchDriver when it is ready.
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NS_NewURI(getter_AddRefs(uri), mRegistration->mScriptSpec, nullptr, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return Fail(rv);
@@ -464,60 +642,16 @@ private:
   {
     MOZ_ASSERT(mCallback);
     mCallback->UpdateFailed(rv);
     mCallback = nullptr;
     Done(rv);
   }
 
   void
-  ContinueInstall()
-  {
-    if (mRegistration->mInstallingWorker) {
-      // FIXME(nsm): Terminate and stuff
-    }
-
-    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
-                                                   WhichServiceWorker::INSTALLING_WORKER);
-    mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration->mScriptSpec);
-    mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
-
-    Succeed();
-
-    nsRefPtr<QueueFireUpdateFoundRunnable> upr =
-      new QueueFireUpdateFoundRunnable(mRegistration);
-    NS_DispatchToMainThread(upr);
-
-    // XXXnsm this leads to double fetches right now, ideally we'll be able to
-    // use the persistent cache later.
-    nsRefPtr<ServiceWorkerJob> upcasted = this;
-    nsMainThreadPtrHandle<nsISupports> handle(
-        new nsMainThreadPtrHolder<nsISupports>(upcasted));
-
-    nsRefPtr<ServiceWorker> serviceWorker;
-    nsresult rv =
-      swm->CreateServiceWorker(mRegistration->mInstallingWorker->GetScriptSpec(),
-                               mRegistration->mScope,
-                               getter_AddRefs(serviceWorker));
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      // FIXME(nsm):
-      MOZ_CRASH("Shouldn't happen yet");
-    }
-
-    nsRefPtr<InstallEventRunnable> r =
-      new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
-
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    r->Dispatch(jsapi.cx());
-  }
-
-  void
   ContinueAfterInstallEvent(bool aSuccess, bool aActivateImmediately)
   {
     // By this point the callback should've been notified about success or fail
     // and nulled.
     MOZ_ASSERT(!mCallback);
 
     if (!mRegistration->mInstallingWorker) {
       NS_WARNING("mInstallingWorker was null.");
@@ -535,30 +669,46 @@ private:
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     // "If registration's waiting worker is not null"
     if (mRegistration->mWaitingWorker) {
       // FIXME(nsm): Terminate
       mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     }
+
+    // Although the spec first sets waiting worker and then updates its state,
+    // our ServiceWorkerInfo does not hold a list of associated ServiceWorker
+    // objects in content JS. This means if we want to fire an event on
+    // ServiceWorkerRegistration.installing, we need to do it first, before
+    // swapping it with waiting worker.
+    mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installed);
     mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
     mRegistration->mWaitingToActivate = false;
-    mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
 
     // FIXME(nsm): Bug 982711 Deal with activateImmediately.
     NS_WARN_IF_FALSE(!aActivateImmediately, "Immediate activation using replace() is not supported yet");
     mRegistration->TryToActivate();
     Done(NS_OK);
   }
 };
 
-NS_IMPL_ISUPPORTS_INHERITED(ServiceWorkerRegisterJob, ServiceWorkerJob, nsIStreamLoaderObserver)
+NS_IMPL_ISUPPORTS_INHERITED(ServiceWorkerRegisterJob, ServiceWorkerJob, nsIStreamLoaderObserver);
+
+NS_IMETHODIMP
+ContinueUpdateRunnable::Run()
+{
+  AssertIsOnMainThread();
+  nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
+  nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
+  upjob->ContinueInstall();
+  return NS_OK;
+}
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
                                const nsAString& aScope,
                                const nsAString& aScriptURL,
                                nsISupports** aPromise)
@@ -773,33 +923,39 @@ InstallEventRunnable::DispatchInstallEve
   event->SetTrusted(true);
 
   nsRefPtr<Promise> waitUntilPromise;
 
   ErrorResult result;
   result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
   nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
-  if (!result.Failed()) {
+  WidgetEvent* internalEvent = event->GetInternalNSEvent();
+  if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
     waitUntilPromise = event->GetPromise();
     if (!waitUntilPromise) {
       ErrorResult result;
       waitUntilPromise =
         Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
+      if (NS_WARN_IF(result.Failed())) {
+        return true;
+      }
     }
   } else {
     // Continue with a canceled install.
+    // Although the spec has different routines to deal with popping stuff
+    // off it's internal queues, we can reuse the ContinueAfterInstallEvent()
+    // logic.
     waitUntilPromise = Promise::Reject(sgo, aCx,
                                        JS::UndefinedHandleValue, result);
   }
 
   if (result.Failed()) {
     return false;
   }
-  // FIXME(nsm): handle script errors.
 
   nsRefPtr<FinishInstallHandler> handler =
     new FinishInstallHandler(mJob, event->ActivateImmediately());
   waitUntilPromise->AppendNativeHandler(handler);
   return true;
 }
 
 class FinishActivationRunnable MOZ_FINAL : public nsRunnable
@@ -882,27 +1038,28 @@ private:
   {
     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
     nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
 
     // FIXME(nsm): Set activeWorker to the correct thing.
     EventInit init;
     init.mBubbles = false;
     init.mCancelable = true;
-    nsRefPtr<InstallPhaseEvent> event =
-      InstallPhaseEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
+    nsRefPtr<ExtendableEvent> event =
+      ExtendableEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
 
     event->SetTrusted(true);
 
     nsRefPtr<Promise> waitUntilPromise;
 
     // FIXME(nsm): Install error handler for any listener errors.
     ErrorResult result;
     result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-    if (!result.Failed()) {
+    WidgetEvent* internalEvent = event->GetInternalNSEvent();
+    if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
       waitUntilPromise = event->GetPromise();
       if (!waitUntilPromise) {
         nsCOMPtr<nsIGlobalObject> global =
           do_QueryObject(aWorkerPrivate->GlobalScope());
         waitUntilPromise =
           Promise::Resolve(global,
                            aCx, JS::UndefinedHandleValue, result);
       }
@@ -968,22 +1125,22 @@ ServiceWorkerRegistrationInfo::Activate(
 
   // XXXnsm I have my doubts about this. Leaving the main thread means that
   // subsequent calls to Activate() not from a Register() call, i.e. due to all
   // controlled documents going away, may lead to two or more calls being
   // interleaved.
   MOZ_ASSERT(mActiveWorker);
   nsRefPtr<ServiceWorker> serviceWorker;
   nsresult rv =
-    swm->CreateServiceWorker(mActiveWorker->GetScriptSpec(),
+    swm->CreateServiceWorker(mActiveWorker->ScriptSpec(),
                              mScope,
                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    // FIXME(nsm): Do the same thing as when "activate" event failed to
-    // dispatch.
+    FinishActivate(false /* success */);
+    return;
   }
 
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(this));
 
   nsRefPtr<ActivateEventRunnable> r =
     new ActivateEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
 
@@ -1408,66 +1565,108 @@ ServiceWorkerManager::GetInstance()
   return concrete.forget();
 }
 
 void
 ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
 {
 }
 
-void
+bool
 ServiceWorkerManager::HandleError(JSContext* aCx,
-                                  const nsACString& aScope,
-                                  const nsAString& aWorkerURL,
+                                  const nsCString& aScope,
+                                  const nsString& aWorkerURL,
                                   nsString aMessage,
                                   nsString aFilename,
                                   nsString aLine,
                                   uint32_t aLineNumber,
                                   uint32_t aColumnNumber,
                                   uint32_t aFlags)
 {
   AssertIsOnMainThread();
 
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = NS_NewURI(getter_AddRefs(uri), aScope, nullptr, nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
+  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aScope);
+  MOZ_ASSERT(domainInfo);
+
+  if (!domainInfo->mSetOfScopesBeingUpdated.Contains(aScope)) {
+    return false;
   }
 
-  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(uri);
-  if (!domainInfo) {
-    return;
-  }
+  domainInfo->mSetOfScopesBeingUpdated.Remove(aScope);
 
-  nsCString scope;
-  scope.Assign(aScope);
-  nsRefPtr<ServiceWorkerRegistrationInfo> registration = domainInfo->GetRegistration(scope);
-  MOZ_ASSERT(registration);
+  ServiceWorkerJobQueue* queue = domainInfo->mJobQueues.Get(aScope);
+  MOZ_ASSERT(queue);
+  ServiceWorkerJob* job = queue->Peek();
+  ServiceWorkerRegisterJob* regJob = static_cast<ServiceWorkerRegisterJob*>(job);
+  MOZ_ASSERT(regJob);
 
   RootedDictionary<ErrorEventInit> init(aCx);
   init.mMessage = aMessage;
   init.mFilename = aFilename;
   init.mLineno = aLineNumber;
   init.mColno = aColumnNumber;
 
-  MOZ_CRASH("FIX THIS");
+  regJob->Fail(init);
+  return true;
 }
 
 void
 ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
 {
   MOZ_ASSERT(mActiveWorker);
   if (aSuccess) {
     mActiveWorker->UpdateState(ServiceWorkerState::Activated);
   } else {
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker = nullptr;
   }
 }
 
+void
+ServiceWorkerRegistrationInfo::QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
+                                                     ServiceWorkerState aState) const
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aInfo);
+  MOZ_ASSERT(aInfo == mInstallingWorker ||
+             aInfo == mWaitingWorker ||
+             aInfo == mActiveWorker);
+
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+    swm->GetDomainInfo(mScope);
+
+  if (domainInfo) {
+    WhichServiceWorker whichOne;
+    if (aInfo == mInstallingWorker) {
+      whichOne = WhichServiceWorker::INSTALLING_WORKER;
+    } else if (aInfo == mWaitingWorker) {
+      whichOne = WhichServiceWorker::WAITING_WORKER;
+    } else if (aInfo == mActiveWorker) {
+      whichOne = WhichServiceWorker::ACTIVE_WORKER;
+    } else {
+      MOZ_CRASH("Hit unexpected case");
+    }
+
+    // Refactor this iteration pattern across this and 2 other call-sites.
+    nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(domainInfo->mServiceWorkerRegistrations);
+    while (it.HasMore()) {
+      nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
+      nsAutoString regScope;
+      target->GetScope(regScope);
+      MOZ_ASSERT(!regScope.IsEmpty());
+
+      NS_ConvertUTF16toUTF8 utf8Scope(regScope);
+      if (utf8Scope.Equals(mScope)) {
+        target->QueueStateChangeEvent(whichOne, aState);
+      }
+    }
+  }
+}
+
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
                                                    const nsACString& aScriptSpec,
                                                    const nsACString& aScope,
                                                    ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWindow);
@@ -1684,89 +1883,100 @@ ServiceWorkerManager::GetScopeForUrl(con
       return NS_ERROR_FAILURE;
   }
 
   aScope = NS_ConvertUTF8toUTF16(r->mScope);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::AddRegistrationEventListener(nsIURI* aDocumentURI, nsIDOMEventTarget* aListener)
+ServiceWorkerManager::AddRegistrationEventListener(const nsAString& aScope, nsIDOMEventTarget* aListener)
 {
-  MOZ_ASSERT(aDocumentURI);
   AssertIsOnMainThread();
-  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDocumentURI);
+  nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
+  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(scope);
   if (!domainInfo) {
+    nsCOMPtr<nsIURI> scopeAsURI;
+    nsresult rv = NS_NewURI(getter_AddRefs(scopeAsURI), scope, nullptr, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     nsAutoCString domain;
-    nsresult rv = aDocumentURI->GetHost(domain);
+    rv = scopeAsURI->GetHost(domain);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     domainInfo = new ServiceWorkerDomainInfo;
     mDomainMap.Put(domain, domainInfo);
   }
 
   MOZ_ASSERT(domainInfo);
 
   // TODO: this is very very bad:
   ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
   MOZ_ASSERT(!domainInfo->mServiceWorkerRegistrations.Contains(registration));
+#ifdef DEBUG
+  // Ensure a registration is only listening for it's own scope.
+  nsAutoString regScope;
+  registration->GetScope(regScope);
+  MOZ_ASSERT(!regScope.IsEmpty());
+  MOZ_ASSERT(scope.Equals(NS_ConvertUTF16toUTF8(regScope)));
+#endif
   domainInfo->mServiceWorkerRegistrations.AppendElement(registration);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::RemoveRegistrationEventListener(nsIURI* aDocumentURI, nsIDOMEventTarget* aListener)
+ServiceWorkerManager::RemoveRegistrationEventListener(const nsAString& aScope, nsIDOMEventTarget* aListener)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aDocumentURI);
-  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDocumentURI);
+  nsCString scope = NS_ConvertUTF16toUTF8(aScope);
+  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(scope);
   if (!domainInfo) {
     return NS_OK;
   }
 
   ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
   MOZ_ASSERT(domainInfo->mServiceWorkerRegistrations.Contains(registration));
+#ifdef DEBUG
+  // Ensure a registration is unregistering for it's own scope.
+  nsAutoString regScope;
+  registration->GetScope(regScope);
+  MOZ_ASSERT(!regScope.IsEmpty());
+  MOZ_ASSERT(scope.Equals(NS_ConvertUTF16toUTF8(regScope)));
+#endif
   domainInfo->mServiceWorkerRegistrations.RemoveElement(registration);
   return NS_OK;
 }
 
 void
 ServiceWorkerManager::FireEventOnServiceWorkerRegistrations(
   ServiceWorkerRegistrationInfo* aRegistration,
   const nsAString& aName)
 {
   AssertIsOnMainThread();
   nsRefPtr<ServiceWorkerDomainInfo> domainInfo =
-    GetDomainInfo(aRegistration->mScriptSpec);
+    GetDomainInfo(aRegistration->mScope);
 
   if (domainInfo) {
     nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(domainInfo->mServiceWorkerRegistrations);
     while (it.HasMore()) {
       nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
-      nsIURI* targetURI = target->GetDocumentURI();
-      if (!targetURI) {
-        NS_WARNING("Controlled domain cannot have page with null URI!");
-        continue;
-      }
+      nsAutoString regScope;
+      target->GetScope(regScope);
+      MOZ_ASSERT(!regScope.IsEmpty());
 
-      nsCString path;
-      nsresult rv = targetURI->GetSpec(path);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        continue;
+      NS_ConvertUTF16toUTF8 utf8Scope(regScope);
+      if (utf8Scope.Equals(aRegistration->mScope)) {
+        nsresult rv = target->DispatchTrustedEvent(aName);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          // Warn only.
+        }
       }
-
-      nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path);
-      if (scope.IsEmpty() ||
-          !scope.Equals(aRegistration->mScope)) {
-        continue;
-      }
-
-      target->DispatchTrustedEvent(aName);
     }
   }
 }
 
 /*
  * This is used for installing, waiting and active.
  */
 NS_IMETHODIMP
@@ -1782,17 +1992,17 @@ ServiceWorkerManager::GetServiceWorkerFo
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   MOZ_ASSERT(doc);
 
   ///////////////////////////////////////////
   // Security check
-  nsCString scope = NS_ConvertUTF16toUTF8(aScope);
+  nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
   nsCOMPtr<nsIURI> scopeURI;
   // We pass nullptr as the base URI since scopes obtained from
   // ServiceWorkerRegistrations MUST be fully qualified URIs.
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
@@ -1827,23 +2037,24 @@ ServiceWorkerManager::GetServiceWorkerFo
   }
 
   if (!info) {
     return NS_ERROR_DOM_NOT_FOUND_ERR;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker;
   rv = CreateServiceWorkerForWindow(window,
-                                    info->GetScriptSpec(),
+                                    info->ScriptSpec(),
                                     registration->mScope,
                                     getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  serviceWorker->SetState(info->State());
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
 /*
  * The .controller is for the registration associated with the document when
  * the document was loaded.
  */
@@ -1871,17 +2082,17 @@ ServiceWorkerManager::GetDocumentControl
   // If the document is controlled, the current worker MUST be non-null.
   if (!registration->mActiveWorker) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
 
   nsRefPtr<ServiceWorker> serviceWorker;
   nsresult rv = CreateServiceWorkerForWindow(window,
-                                             registration->mActiveWorker->GetScriptSpec(),
+                                             registration->mActiveWorker->ScriptSpec(),
                                              registration->mScope,
                                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
@@ -1985,31 +2196,25 @@ ServiceWorkerManager::InvalidateServiceW
   AssertIsOnMainThread();
   nsRefPtr<ServiceWorkerDomainInfo> domainInfo =
     GetDomainInfo(aRegistration->mScriptSpec);
 
   if (domainInfo) {
     nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(domainInfo->mServiceWorkerRegistrations);
     while (it.HasMore()) {
       nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
-
-      nsIURI* targetURI = target->GetDocumentURI();
-      nsCString path;
-      nsresult rv = targetURI->GetSpec(path);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        continue;
-      }
+      nsAutoString regScope;
+      target->GetScope(regScope);
+      MOZ_ASSERT(!regScope.IsEmpty());
 
-      nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path);
-      if (scope.IsEmpty() ||
-          !scope.Equals(aRegistration->mScope)) {
-        continue;
+      NS_ConvertUTF16toUTF8 utf8Scope(regScope);
+
+      if (utf8Scope.Equals(aRegistration->mScope)) {
+        target->InvalidateWorkerReference(aWhichOnes);
       }
-
-      target->InvalidateWorkerReference(aWhichOnes);
     }
   }
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::Update(const nsAString& aScope)
 {
   NS_ConvertUTF16toUTF8 scope(aScope);
@@ -2077,16 +2282,55 @@ EnumControlledDocuments(nsISupports* aKe
   if (!document || !document->GetInnerWindow()) {
       return PL_DHASH_NEXT;
   }
 
   data->mDocuments->AppendElement(document->GetInnerWindow()->WindowID());
   return PL_DHASH_NEXT;
 }
 
+static PLDHashOperator
+FireControllerChangeOnMatchingDocument(nsISupports* aKey,
+                                       ServiceWorkerRegistrationInfo* aValue,
+                                       void* aData)
+{
+  AssertIsOnMainThread();
+
+  ServiceWorkerRegistrationInfo* contextReg = static_cast<ServiceWorkerRegistrationInfo*>(aData);
+  if (aValue != contextReg) {
+    return PL_DHASH_NEXT;
+  }
+
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aKey);
+  if (NS_WARN_IF(!doc)) {
+    return PL_DHASH_NEXT;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> w = doc->GetWindow();
+  MOZ_ASSERT(w);
+  auto* window = static_cast<nsGlobalWindow*>(w.get());
+  if (NS_WARN_IF(!window)) {
+    NS_WARNING("No valid nsGlobalWindow");
+    return PL_DHASH_NEXT;
+  }
+
+  ErrorResult result;
+  dom::Navigator* navigator = window->GetNavigator(result);
+  if (NS_WARN_IF(result.Failed())) {
+    return PL_DHASH_NEXT;
+  }
+
+  nsRefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
+  result = container->DispatchTrustedEvent(NS_LITERAL_STRING("controllerchange"));
+  if (result.Failed()) {
+    NS_WARNING("Failed to dispatch controllerchange event");
+  }
+
+  return PL_DHASH_NEXT;
+}
 } // anonymous namespace
 
 void
 ServiceWorkerManager::GetServicedClients(const nsCString& aScope,
                                      nsTArray<uint64_t>* aControlledDocuments)
 {
   nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aScope);
   nsRefPtr<ServiceWorkerRegistrationInfo> registration =
@@ -2101,11 +2345,14 @@ ServiceWorkerManager::GetServicedClients
 
   domainInfo->mControlledDocuments.EnumerateRead(EnumControlledDocuments,
                                                  &data);
 }
 
 void
 ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
 {
-  // FIXME(nsm): Fill this out.
+  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aRegistration->mScope);
+  MOZ_ASSERT(domainInfo);
+  domainInfo->mControlledDocuments.EnumerateRead(FireControllerChangeOnMatchingDocument,
+                                                 aRegistration);
 }
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -13,79 +13,33 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnum.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
 #include "mozilla/dom/ServiceWorkerCommon.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsTObserverArray.h"
-#include "nsClassHashtable.h"
 
 class nsIScriptError;
 
 namespace mozilla {
 namespace dom {
 
 class ServiceWorkerRegistration;
 
 namespace workers {
 
 class ServiceWorker;
-
-/*
- * Wherever the spec treats a worker instance and a description of said worker
- * as the same thing; i.e. "Resolve foo with
- * _GetNewestWorker(serviceWorkerRegistration)", we represent the description
- * by this class and spawn a ServiceWorker in the right global when required.
- */
-class ServiceWorkerInfo MOZ_FINAL
-{
-  nsCString mScriptSpec;
-  ServiceWorkerState mState;
-
-  ~ServiceWorkerInfo()
-  { }
-
-public:
-  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
-
-  const nsCString&
-  GetScriptSpec() const
-  {
-    return mScriptSpec;
-  }
-
-  explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
-    : mScriptSpec(aScriptSpec)
-    , mState(ServiceWorkerState::EndGuard_)
-  { }
-
-  void
-  UpdateState(ServiceWorkerState aState)
-  {
-#ifdef DEBUG
-    // Any state can directly transition to redundant, but everything else is
-    // ordered.
-    if (aState != ServiceWorkerState::Redundant) {
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
-      MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
-    }
-    // Activated can only go to redundant.
-    MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
-#endif
-    mState = aState;
-    // FIXME(nsm): Inform all relevant ServiceWorker instances.
-  }
-};
+class ServiceWorkerInfo;
 
 class ServiceWorkerJobQueue;
 
 class ServiceWorkerJob : public nsISupports
 {
   // The queue keeps the jobs alive, so they can hold a rawptr back to the
   // queue.
   ServiceWorkerJobQueue* mQueue;
@@ -112,32 +66,41 @@ class ServiceWorkerJobQueue MOZ_FINAL
 {
   friend class ServiceWorkerJob;
 
   nsTArray<nsRefPtr<ServiceWorkerJob>> mJobs;
 
 public:
   ~ServiceWorkerJobQueue()
   {
-    // FIXME(nsm): Clean up jobs.
-    MOZ_ASSERT(mJobs.IsEmpty());
+    if (!mJobs.IsEmpty()) {
+      NS_WARNING("Pending/running jobs still around on shutdown!");
+    }
   }
 
   void
   Append(ServiceWorkerJob* aJob)
   {
     MOZ_ASSERT(aJob);
     MOZ_ASSERT(!mJobs.Contains(aJob));
     bool wasEmpty = mJobs.IsEmpty();
     mJobs.AppendElement(aJob);
     if (wasEmpty) {
       aJob->Start();
     }
   }
 
+  // Only used by HandleError, keep it that way!
+  ServiceWorkerJob*
+  Peek()
+  {
+    MOZ_ASSERT(!mJobs.IsEmpty());
+    return mJobs[0];
+  }
+
 private:
   void
   Pop()
   {
     MOZ_ASSERT(!mJobs.IsEmpty());
     mJobs.RemoveElementAt(0);
     if (!mJobs.IsEmpty()) {
       mJobs[0]->Start();
@@ -220,16 +183,80 @@ public:
   void
   TryToActivate();
 
   void
   Activate();
 
   void
   FinishActivate(bool aSuccess);
+
+  void
+  QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
+                        ServiceWorkerState aState) const;
+};
+
+/*
+ * Wherever the spec treats a worker instance and a description of said worker
+ * as the same thing; i.e. "Resolve foo with
+ * _GetNewestWorker(serviceWorkerRegistration)", we represent the description
+ * by this class and spawn a ServiceWorker in the right global when required.
+ */
+class ServiceWorkerInfo MOZ_FINAL
+{
+private:
+  const ServiceWorkerRegistrationInfo* mRegistration;
+  nsCString mScriptSpec;
+  ServiceWorkerState mState;
+
+  ~ServiceWorkerInfo()
+  { }
+
+public:
+  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
+
+  const nsCString&
+  ScriptSpec() const
+  {
+    return mScriptSpec;
+  }
+
+  explicit ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
+                             const nsACString& aScriptSpec)
+    : mRegistration(aReg)
+    , mScriptSpec(aScriptSpec)
+    , mState(ServiceWorkerState::EndGuard_)
+  {
+    MOZ_ASSERT(mRegistration);
+  }
+
+  ServiceWorkerState
+  State() const
+  {
+    return mState;
+  }
+
+  void
+  UpdateState(ServiceWorkerState aState)
+  {
+#ifdef DEBUG
+    // Any state can directly transition to redundant, but everything else is
+    // ordered.
+    if (aState != ServiceWorkerState::Redundant) {
+      MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
+      MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
+      MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
+      MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
+    }
+    // Activated can only go to redundant.
+    MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
+#endif
+    mState = aState;
+    mRegistration->QueueStateChangeEvent(this, mState);
+  }
 };
 
 #define NS_SERVICEWORKERMANAGER_IMPL_IID                 \
 { /* f4f8755a-69ca-46e8-a65d-775745535990 */             \
   0xf4f8755a,                                            \
   0x69ca,                                                \
   0x46e8,                                                \
   { 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 }     \
@@ -288,16 +315,18 @@ public:
     nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mServiceWorkerRegistrationInfos;
 
     nsTObserverArray<ServiceWorkerRegistration*> mServiceWorkerRegistrations;
 
     nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
 
     nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
 
+    nsDataHashtable<nsCStringHashKey, bool> mSetOfScopesBeingUpdated;
+
     ServiceWorkerDomainInfo()
     { }
 
     already_AddRefed<ServiceWorkerRegistrationInfo>
     GetRegistration(const nsCString& aScope) const
     {
       nsRefPtr<ServiceWorkerRegistrationInfo> reg;
       mServiceWorkerRegistrationInfos.Get(aScope, getter_AddRefs(reg));
@@ -337,21 +366,22 @@ public:
     { }
   };
 
   nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
 
   void
   FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
 
-
-  void
+  // Returns true if the error was handled, false if normal worker error
+  // handling should continue.
+  bool
   HandleError(JSContext* aCx,
-              const nsACString& aScope,
-              const nsAString& aWorkerURL,
+              const nsCString& aScope,
+              const nsString& aWorkerURL,
               nsString aMessage,
               nsString aFilename,
               nsString aLine,
               uint32_t aLineNumber,
               uint32_t aColumnNumber,
               uint32_t aFlags);
 
   void
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -259,43 +259,58 @@ ServiceWorkerRegistration::InvalidateWor
     mWaitingWorker = nullptr;
   }
 
   if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
     mActiveWorker = nullptr;
   }
 }
 
+void
+ServiceWorkerRegistration::QueueStateChangeEvent(WhichServiceWorker aWhichOne,
+                                                 ServiceWorkerState aState) const
+{
+  nsRefPtr<ServiceWorker> worker;
+  if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) {
+    worker = mInstallingWorker;
+  } else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) {
+    worker = mWaitingWorker;
+  } else if (aWhichOne == WhichServiceWorker::ACTIVE_WORKER) {
+    worker = mActiveWorker;
+  } else {
+    MOZ_CRASH("Invalid case");
+  }
+
+  if (worker) {
+    worker->SetState(aState);
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(worker, &ServiceWorker::DispatchStateChange);
+    NS_DispatchToMainThread(r);
+  }
+}
+
 // XXXnsm, maybe this can be optimized to only add when a event handler is
 // registered.
 void
 ServiceWorkerRegistration::StartListeningForEvents()
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
   if (swm) {
-    swm->AddRegistrationEventListener(GetDocumentURI(), this);
+    swm->AddRegistrationEventListener(mScope, this);
     mListeningForEvents = true;
   }
 }
 
 void
 ServiceWorkerRegistration::StopListeningForEvents()
 {
   if (!mListeningForEvents) {
     return;
   }
 
   nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
   if (swm) {
-    swm->RemoveRegistrationEventListener(GetDocumentURI(), this);
+    swm->RemoveRegistrationEventListener(mScope, this);
     mListeningForEvents = false;
   }
 }
 
-nsIURI*
-ServiceWorkerRegistration::GetDocumentURI() const
-{
-  MOZ_ASSERT(GetOwner());
-  return GetOwner()->GetDocumentURI();
-}
-
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -50,22 +50,21 @@ public:
   {
     aScope = mScope;
   }
 
   already_AddRefed<Promise>
   Unregister(ErrorResult& aRv);
 
   // Useful methods for ServiceWorkerManager:
-
-  nsIURI*
-  GetDocumentURI() const;
+  void
+  InvalidateWorkerReference(WhichServiceWorker aWhichOnes);
 
   void
-  InvalidateWorkerReference(WhichServiceWorker aWhichOnes);
+  QueueStateChangeEvent(WhichServiceWorker aWhichOne, ServiceWorkerState aState) const;
 
   // DOMEventTargethelper
   virtual void DisconnectFromOwner() MOZ_OVERRIDE;
 
 private:
   ~ServiceWorkerRegistration();
 
   already_AddRefed<workers::ServiceWorker>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1313,25 +1313,30 @@ private:
     else {
       AssertIsOnMainThread();
 
       if (aWorkerPrivate->IsSuspended()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
-      if (aWorkerPrivate->IsServiceWorker()) {
-        nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-        MOZ_ASSERT(swm);
-        swm->HandleError(aCx, aWorkerPrivate->SharedWorkerName(),
-                         aWorkerPrivate->ScriptURL(),
-                         mMessage,
-                         mFilename, mLine, mLineNumber, mColumnNumber, mFlags);
-        return true;
-      } else if (aWorkerPrivate->IsSharedWorker()) {
+      if (aWorkerPrivate->IsServiceWorker() || aWorkerPrivate->IsSharedWorker()) {
+        if (aWorkerPrivate->IsServiceWorker()) {
+          nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+          MOZ_ASSERT(swm);
+          bool handled = swm->HandleError(aCx, aWorkerPrivate->SharedWorkerName(),
+                                          aWorkerPrivate->ScriptURL(),
+                                          mMessage,
+                                          mFilename, mLine, mLineNumber,
+                                          mColumnNumber, mFlags);
+          if (handled) {
+            return true;
+          }
+        }
+
         aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
                                                       mLine, mLineNumber,
                                                       mColumnNumber, mFlags);
         return true;
       }
 
       // The innerWindowId is only required if we are going to ReportError
       // below, which is gated on this condition. The inner window correctness
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -489,46 +489,55 @@ private:
   bool mValue;
 };
 
 class WorkerScopeUnregisterRunnable MOZ_FINAL : public nsRunnable
                                               , public nsIServiceWorkerUnregisterCallback
                                               , public WorkerFeature
 {
   WorkerPrivate* mWorkerPrivate;
+  nsString mScope;
+
+  // Worker thread only.
   nsRefPtr<Promise> mWorkerPromise;
-  nsString mScope;
   bool mCleanedUp;
 
+  ~WorkerScopeUnregisterRunnable()
+  {
+    MOZ_ASSERT(mCleanedUp);
+  }
+
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   WorkerScopeUnregisterRunnable(WorkerPrivate* aWorkerPrivate,
                                 Promise* aWorkerPromise,
                                 const nsAString& aScope)
     : mWorkerPrivate(aWorkerPrivate)
+    , mScope(aScope)
     , mWorkerPromise(aWorkerPromise)
-    , mScope(aScope)
     , mCleanedUp(false)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aWorkerPromise);
 
     if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
       MOZ_ASSERT(false, "cannot add the worker feature!");
+      mWorkerPromise = nullptr;
       mCleanedUp = true;
       return;
     }
   }
 
   Promise*
   WorkerPromise() const
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(!mCleanedUp);
     return mWorkerPromise;
   }
 
   NS_IMETHODIMP
   UnregisterSucceeded(bool aState) MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
@@ -556,25 +565,20 @@ public:
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     if (mCleanedUp) {
       return;
     }
 
     mWorkerPrivate->RemoveFeature(aCx, this);
+    mWorkerPromise = nullptr;
     mCleanedUp = true;
   }
 
-private:
-  ~WorkerScopeUnregisterRunnable()
-  {
-    MOZ_ASSERT(mCleanedUp);
-  }
-
   NS_IMETHODIMP
   Run() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
 
     nsresult rv;
     nsCOMPtr<nsIServiceWorkerManager> swm =
       do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
@@ -594,36 +598,34 @@ private:
 
     return NS_OK;
   }
 
   virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aStatus > workers::Running);
-
-    mCleanedUp = true;
+    CleanUp(aCx);
     return true;
   }
 };
 
 NS_IMPL_ISUPPORTS_INHERITED(WorkerScopeUnregisterRunnable, nsRunnable,
                             nsIServiceWorkerUnregisterCallback)
 
 bool
 UnregisterResultRunnable::WorkerRun(JSContext* aCx,
                                     WorkerPrivate* aWorkerPrivate)
 {
   if (mState == Failed) {
     mRunnable->WorkerPromise()->MaybeReject(aCx, JS::UndefinedHandleValue);
-    mRunnable->CleanUp(aCx);
-    return true;
+  } else {
+    mRunnable->WorkerPromise()->MaybeResolve(mValue);
   }
 
-  mRunnable->WorkerPromise()->MaybeResolve(mValue);
   mRunnable->CleanUp(aCx);
   return true;
 }
 
 } // anonymous namespace
 
 already_AddRefed<Promise>
 ServiceWorkerGlobalScope::Unregister(ErrorResult& aRv)
@@ -633,17 +635,23 @@ ServiceWorkerGlobalScope::Unregister(Err
 
   nsRefPtr<Promise> promise = Promise::Create(this, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   nsRefPtr<WorkerScopeUnregisterRunnable> runnable =
     new WorkerScopeUnregisterRunnable(mWorkerPrivate, promise, mScope);
-  NS_DispatchToMainThread(runnable);
+
+  // Ensure the AddFeature succeeded before dispatching.
+  // Otherwise we let the promise remain pending since script is going to stop
+  // soon anyway.
+  if (runnable->WorkerPromise()) {
+    NS_DispatchToMainThread(runnable);
+  }
 
   return promise.forget();
 }
 
 namespace {
 
 class UpdateRunnable MOZ_FINAL : public nsRunnable
 {
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Public stuff.
 EXPORTS.mozilla.dom += [
     'ServiceWorkerCommon.h',
     'ServiceWorkerContainer.h',
+    'ServiceWorkerEvents.h',
     'ServiceWorkerRegistration.h',
     'WorkerPrivate.h',
     'WorkerRunnable.h',
     'WorkerScope.h',
 ]
 
 EXPORTS.mozilla.dom.workers += [
     'ServiceWorkerManager.h',
--- a/dom/workers/test/fetch/worker_test_request.js
+++ b/dom/workers/test/fetch/worker_test_request.js
@@ -39,28 +39,28 @@ function testClone() {
        "URL should be resolved with entry settings object's API base URL");
   ok(req.referrer === "about:client", "Default referrer is `client` which serializes to about:client.");
   ok(req.mode === "same-origin", "Request mode is same-origin");
   ok(req.credentials === "same-origin", "Default credentials is same-origin");
 }
 
 function testUsedRequest() {
   // Passing a used request should fail.
-  var req = new Request("", { body: "This is foo" });
+  var req = new Request("", { method: 'post', body: "This is foo" });
   var p1 = req.text().then(function(v) {
     try {
       var req2 = new Request(req);
       ok(false, "Used Request cannot be passed to new Request");
     } catch(e) {
       ok(true, "Used Request cannot be passed to new Request");
     }
   });
 
   // Passing a request should set the request as used.
-  var reqA = new Request("", { body: "This is foo" });
+  var reqA = new Request("", { method: 'post', body: "This is foo" });
   var reqB = new Request(reqA);
   is(reqA.bodyUsed, true, "Passing a Request to another Request should set the former as used");
   return p1;
 }
 
 function testSimpleUrlParse() {
   // Just checks that the URL parser is actually being used.
   var req = new Request("/file.html");
@@ -128,95 +128,112 @@ function testMethod() {
   for (var i = 0; i < forbiddenNoCors.length; ++i) {
     try {
       var r = new Request("", { method: forbiddenNoCors[i], mode: "no-cors" });
       ok(false, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode");
     } catch(e) {
       ok(true, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode");
     }
   }
+
+  // HEAD/GET requests cannot have a body.
+  try {
+    var r = new Request("", { method: "get", body: "hello" });
+    ok(false, "HEAD/GET request cannot have a body");
+  } catch(e) {
+    is(e.name, "TypeError", "HEAD/GET request cannot have a body");
+  }
+
+  try {
+    var r = new Request("", { method: "head", body: "hello" });
+    ok(false, "HEAD/GET request cannot have a body");
+  } catch(e) {
+    is(e.name, "TypeError", "HEAD/GET request cannot have a body");
+  }
+
+  // Non HEAD/GET should not throw.
+  var r = new Request("", { method: "patch", body: "hello" });
 }
 
 function testUrlFragment() {
   var req = new Request("./request#withfragment");
   ok(req.url, (new URL("./request", self.location.href)).href, "request.url should be serialized with exclude fragment flag set");
 }
 
 function testBodyUsed() {
-  var req = new Request("./bodyused", { body: "Sample body" });
+  var req = new Request("./bodyused", { method: 'post', body: "Sample body" });
   is(req.bodyUsed, false, "bodyUsed is initially false.");
   return req.text().then((v) => {
     is(v, "Sample body", "Body should match");
     is(req.bodyUsed, true, "After reading body, bodyUsed should be true.");
   }).then((v) => {
     return req.blob().then((v) => {
       ok(false, "Attempting to read body again should fail.");
     }, (e) => {
       ok(true, "Attempting to read body again should fail.");
     })
   });
 }
 
 function testBodyCreation() {
   var text = "κόσμε";
-  var req1 = new Request("", { body: text });
+  var req1 = new Request("", { method: 'post', body: text });
   var p1 = req1.text().then(function(v) {
     ok(typeof v === "string", "Should resolve to string");
     is(text, v, "Extracted string should match");
   });
 
-  var req2 = new Request("", { body: new Uint8Array([72, 101, 108, 108, 111]) });
+  var req2 = new Request("", { method: 'post', body: new Uint8Array([72, 101, 108, 108, 111]) });
   var p2 = req2.text().then(function(v) {
     is("Hello", v, "Extracted string should match");
   });
 
-  var req2b = new Request("", { body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
+  var req2b = new Request("", { method: 'post', body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
   var p2b = req2b.text().then(function(v) {
     is("Hello", v, "Extracted string should match");
   });
 
-  var reqblob = new Request("", { body: new Blob([text]) });
+  var reqblob = new Request("", { method: 'post', body: new Blob([text]) });
   var pblob = reqblob.text().then(function(v) {
     is(v, text, "Extracted string should match");
   });
 
   var params = new URLSearchParams();
   params.append("item", "Geckos");
   params.append("feature", "stickyfeet");
   params.append("quantity", "700");
-  var req3 = new Request("", { body: params });
+  var req3 = new Request("", { method: 'post', body: params });
   var p3 = req3.text().then(function(v) {
     var extracted = new URLSearchParams(v);
     is(extracted.get("item"), "Geckos", "Param should match");
     is(extracted.get("feature"), "stickyfeet", "Param should match");
     is(extracted.get("quantity"), "700", "Param should match");
   });
 
   return Promise.all([p1, p2, p2b, pblob, p3]);
 }
 
 function testBodyExtraction() {
   var text = "κόσμε";
-  var newReq = function() { return new Request("", { body: text }); }
+  var newReq = function() { return new Request("", { method: 'post', body: text }); }
   return newReq().text().then(function(v) {
     ok(typeof v === "string", "Should resolve to string");
     is(text, v, "Extracted string should match");
   }).then(function() {
     return newReq().blob().then(function(v) {
       ok(v instanceof Blob, "Should resolve to Blob");
       var fs = new FileReaderSync();
       is(fs.readAsText(v), text, "Decoded Blob should match original");
     });
   }).then(function() {
-    // FIXME(nsm): Enable once Bug 1107777 and Bug 1072144 have been fixed.
-    //return newReq().json().then(function(v) {
-    //  ok(false, "Invalid json should reject");
-    //}, function(e) {
-    //  ok(true, "Invalid json should reject");
-    //})
+    return newReq().json().then(function(v) {
+      ok(false, "Invalid json should reject");
+    }, function(e) {
+      ok(true, "Invalid json should reject");
+    })
   }).then(function() {
     return newReq().arrayBuffer().then(function(v) {
       ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
       var dec = new TextDecoder();
       is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original");
     });
   })
 }
--- a/dom/workers/test/fetch/worker_test_response.js
+++ b/dom/workers/test/fetch/worker_test_response.js
@@ -113,22 +113,21 @@ function testBodyExtraction() {
     is(text, v, "Extracted string should match");
   }).then(function() {
     return newRes().blob().then(function(v) {
       ok(v instanceof Blob, "Should resolve to Blob");
       var fs = new FileReaderSync();
       is(fs.readAsText(v), text, "Decoded Blob should match original");
     });
   }).then(function() {
-    // FIXME(nsm): Enable once Bug 1107777 and Bug 1072144 have been fixed.
-    //return newRes().json().then(function(v) {
-    //  ok(false, "Invalid json should reject");
-    //}, function(e) {
-    //  ok(true, "Invalid json should reject");
-    //})
+    return newRes().json().then(function(v) {
+      ok(false, "Invalid json should reject");
+    }, function(e) {
+      ok(true, "Invalid json should reject");
+    })
   }).then(function() {
     return newRes().arrayBuffer().then(function(v) {
       ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
       var dec = new TextDecoder();
       is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original");
     });
   })
 }
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/activate_event_error_worker.js
@@ -0,0 +1,4 @@
+// Worker that errors on receiving an activate event.
+onactivate = function(e) {
+  undefined.doSomething;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/install_event_error_worker.js
@@ -0,0 +1,4 @@
+// Worker that errors on receiving an install event.
+oninstall = function(e) {
+  undefined.doSomething;
+}
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -1,29 +1,35 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || android_version == "10" # bug 1056702
 support-files =
   worker.js
   worker2.js
   worker3.js
   parse_error_worker.js
+  activate_event_error_worker.js
   install_event_worker.js
+  install_event_error_worker.js
   simpleregister/index.html
   simpleregister/ready.html
   controller/index.html
   unregister/index.html
+  unregister/unregister.html
   workerUpdate/update.html
   sw_clients/simple.html
   get_serviced_worker.js
+  worker_unregister.js
   worker_update.js
 
 [test_unregister.html]
 skip-if = true # bug 1094375
 [test_installation_simple.html]
 skip-if = true # bug 1094375
 [test_get_serviced.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
 skip-if = true # bug 1124743
 [test_controller.html]
 [test_workerUpdate.html]
 skip-if = true # Enable after Bug 982726 postMessage is landed.
+[test_workerUnregister.html]
+skip-if = true # Enable after Bug 982726 postMessage is landed.
--- a/dom/workers/test/serviceworkers/simpleregister/index.html
+++ b/dom/workers/test/serviceworkers/simpleregister/index.html
@@ -14,17 +14,17 @@
           navigator.serviceWorker.getRegistrations().then(function(a) {
             window.parent.postMessage({ type: "check", status: Array.isArray(a),
                                         msg: "getRegistrations returns an array" }, "*");
             window.parent.postMessage({ type: "check", status: a.length > 0,
                                         msg: "getRegistrations returns an array with 1 item" }, "*");
             for (var i = 0; i < a.length; ++i) {
               window.parent.postMessage({ type: "check", status: a[i] instanceof ServiceWorkerRegistration,
                                           msg: "getRegistrations returns an array of ServiceWorkerRegistration objects" }, "*");
-              if (a[i].scope.match(/simpleregister\/\*/)) {
+              if (a[i].scope.match(/simpleregister\//)) {
                 a[i].onupdatefound = function(e) {
                   eventReceived();
                 }
               }
             }
           });
 
           navigator.serviceWorker.getRegistration('http://mochi.test:8888/tests/dom/workers/test/serviceworkers/simpleregister/')
--- a/dom/workers/test/serviceworkers/simpleregister/ready.html
+++ b/dom/workers/test/serviceworkers/simpleregister/ready.html
@@ -1,15 +1,17 @@
 <html>
   <head></head>
   <body>
     <script type="text/javascript">
 
        window.addEventListener('message', function(evt) {
          navigator.serviceWorker.ready.then(function() {
-           evt.ports[0].postMessage("WOW!");
+           navigator.serviceWorker.oncontrollerchange = function(e) {
+             evt.ports[0].postMessage("WOW!");
+           }
          });
        }, false);
 
     </script>
   </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -11,40 +11,89 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   function simpleRegister() {
-    var p = navigator.serviceWorker.register("worker.js", { scope: "./" });
+    var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" });
     return p;
   }
 
   function nextRegister(reg) {
-    var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./" });
-
-    return new Promise(function(resolve, reject) {
-      reg.onupdatefound = function(e) {
-        ok(true, "Received onupdatefound");
-        resolve();
-      };
+    ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration");
+    var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" });
+    return p.then(function(swr) {
+      ok(reg.scope === swr.scope, "Scope for registrations should match.");
+      return new Promise(function(resolve, reject) {
+        swr.addEventListener('updatefound', function(e) {
+          ok(true, "Received onupdatefound");
+          resolve();
+        });
+      });
+    }, function(e) {
+      ok(false, "Unexpected Error in nextRegister! " + e);
     });
   }
 
+  function installError() {
+    // Silence worker errors so they don't cause the test to fail.
+    window.onerror = function() { }
+    return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" })
+      .then(function(swr) {
+        ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
+        return new Promise(function(resolve, reject) {
+          swr.installing.onstatechange = function(e) {
+            ok(e.target.state == "redundant", "Installation of worker with error should fail.");
+            resolve();
+          }
+        });
+      }).then(function() {
+        return navigator.serviceWorker.getRegistration("./install_event").then(function(swr) {
+          var newest = swr.waiting || swr.active;
+          ok(newest, "Waiting or active worker should still exist");
+          ok(newest.scriptURL.match(/install_event_worker.js$/), "Previous worker should remain the newest worker");
+        });
+      });
+  }
+
+  function activateError() {
+    // Silence worker errors so they don't cause the test to fail.
+    window.onerror = function() { }
+    return navigator.serviceWorker.register("activate_event_error_worker.js", { scope: "./activate_error" })
+      .then(function(swr) {
+        return new Promise(function(resolve, reject) {
+          ok(swr.installing.state == "installing", "activateError(): Installing worker's state should be 'installing'");
+          swr.installing.onstatechange = function(e) {
+            ok(swr.active, "transition to active successfully");
+            is(swr.active.state, "activating", "should be activating");
+            swr.active.onstatechange = function(e) {
+              is(e.target.state, "redundant", "Activation of worker with error in activate event handler should fail.");
+              resolve(swr);
+            }
+          }
+        });
+      }).then(function(swr) {
+        return swr.unregister();
+      });
+  }
+
   function unregister() {
-    return navigator.serviceWorker.getRegistration("./").then(function(reg) {
+    return navigator.serviceWorker.getRegistration("./install_event").then(function(reg) {
       return reg.unregister();
     });
   }
 
   function runTest() {
     simpleRegister()
       .then(nextRegister)
+      .then(installError)
+      .then(activateError)
       .then(unregister)
       .then(function() {
         SimpleTest.finish();
       }).catch(function(e) {
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -90,25 +90,25 @@
         ok(wr instanceof ServiceWorkerRegistration, "Second registration should succeed");
       }, function(e) {
         ok(false, "Second registration should succeed");
       })
     ]);
   }
 
   function networkError404() {
-    return navigator.serviceWorker.register("404.js").then(function(w) {
-        todo(false, "Should fail with NetworkError");
+    return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) {
+        ok(false, "Should fail with NetworkError");
       }, function(e) {
-        todo(e.name === "NetworkError", "Should fail with NetworkError");
+        ok(e.name === "NetworkError", "Should fail with NetworkError");
       });
   }
 
   function parseError() {
-    var p = navigator.serviceWorker.register("parse_error_worker.js");
+    var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
     return p.then(function(wr) {
       ok(false, "Registration should fail with parse error");
     }, function(e) {
     info("NSM " + e.name);
       ok(e instanceof Error, "Registration should fail with parse error");
     });
   }
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_workerUnregister.html
@@ -0,0 +1,57 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 982728 - Test ServiceWorkerGlobalScope.unregister</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="container"></div>
+<script class="testbody" type="text/javascript">
+
+  function simpleRegister() {
+    return navigator.serviceWorker.register("worker_unregister.js", { scope: "unregister/" });
+  }
+
+  function waitForMessages(sw) {
+    var p = new Promise(function(resolve, reject) {
+      window.onmessage = function(e) {
+        if (e.data === "DONE") {
+          ok(true, "The worker has unregistered itself");
+        } else if (e.data === "ERROR") {
+          ok(false, "The worker has unregistered itself");
+        } else if (e.data === "FINISH") {
+          resolve();
+        }
+      }
+    });
+
+    var frame = document.createElement("iframe");
+    frame.setAttribute("src", "unregister/unregister.html");
+    document.body.appendChild(frame);
+
+    return p;
+  }
+
+  function runTest() {
+    simpleRegister().then(waitForMessages).catch(function(e) {
+      ok(false, "Something went wrong.");
+    }).then(function() {
+      SimpleTest.finish();
+    });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/unregister/unregister.html
@@ -0,0 +1,22 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test worker::unregister</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript">
+
+  onmessage = function(e) { parent.postMessage(e.data, "*"); }
+  navigator.serviceWorker.controller.postMessage("GO");
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_unregister.js
@@ -0,0 +1,16 @@
+onmessage = function(e) {
+  clients.getServiced().then(function(c) {
+    if (c.length == 0) {
+      // We cannot proceed.
+      return;
+    }
+
+    unregister().then(function() {
+      c[0].postMessage('DONE');
+    }, function() {
+      c[0].postMessage('ERROR');
+    }).then(function() {
+      c[0].postMessage('FINISH');
+    });
+  });
+}
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1366,16 +1366,20 @@ APZCTreeManager::GetAPZCAtPoint(HitTesti
         for (HitTestingTreeNode* n = node->GetPrevSibling(); n; n = n->GetPrevSibling()) {
           if (n->GetApzc()) {
             prevSiblingApzc = n->GetApzc();
             break;
           }
         }
         if (result == prevSiblingApzc) {
           APZCTM_LOG("Continuing search past probable scrollinfo info layer\n");
+          // We need to reset aOutHitResult in order to keep searching. This is
+          // ok because we know that we will at least hit prevSiblingApzc
+          // again, which is the same as result.
+          *aOutHitResult = HitNothing;
           continue;
         }
       }
 
       return result;
     }
   }
 
--- a/gfx/layers/apz/src/APZUtils.h
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -2,16 +2,18 @@
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_APZUtils_h
 #define mozilla_layers_APZUtils_h
 
+#include <stdint.h>                     // for uint32_t
+
 namespace mozilla {
 namespace layers {
 
 enum HitTestResult {
   HitNothing,
   HitLayer,
   HitDispatchToContentRegion,
 };
new file mode 100644
--- /dev/null
+++ b/gfx/tests/gtest/TestVsync.cpp
@@ -0,0 +1,148 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+#include "MainThreadUtils.h"
+#include "nsIThread.h"
+#include "nsRefPtr.h"
+#include "SoftwareVsyncSource.h"
+#include "VsyncSource.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/VsyncDispatcher.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using ::testing::_;
+
+// Timeout for vsync events to occur in milliseconds
+const int kVsyncTimeoutMS = 50;
+
+class TestVsyncObserver : public VsyncObserver {
+public:
+  TestVsyncObserver()
+    : mDidGetVsyncNotification(false)
+    , mVsyncMonitor("VsyncMonitor")
+  {
+  }
+
+  virtual bool NotifyVsync(TimeStamp aVsyncTimeStamp) MOZ_OVERRIDE {
+    MonitorAutoLock lock(mVsyncMonitor);
+    mDidGetVsyncNotification = true;
+    mVsyncMonitor.Notify();
+    return true;
+  }
+
+  void WaitForVsyncNotification()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (DidGetVsyncNotification()) {
+      return;
+    }
+
+    { // scope lock
+      MonitorAutoLock lock(mVsyncMonitor);
+      PRIntervalTime timeout = PR_MillisecondsToInterval(kVsyncTimeoutMS);
+      lock.Wait(timeout);
+    }
+  }
+
+  bool DidGetVsyncNotification()
+  {
+    MonitorAutoLock lock(mVsyncMonitor);
+    return mDidGetVsyncNotification;
+  }
+
+private:
+  bool mDidGetVsyncNotification;
+
+private:
+  Monitor mVsyncMonitor;
+};
+
+class VsyncTester : public ::testing::Test {
+protected:
+  explicit VsyncTester()
+  {
+    gfxPlatform::GetPlatform();
+    gfxPrefs::GetSingleton();
+    if (gfxPrefs::HardwareVsyncEnabled() ) {
+      mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
+    }
+  }
+
+  virtual ~VsyncTester()
+  {
+    mVsyncSource = nullptr;
+  }
+
+  nsRefPtr<VsyncSource> mVsyncSource;
+};
+
+static void
+FlushMainThreadLoop()
+{
+  // Some tasks are pushed onto the main thread when adding vsync observers
+  // This function will ensure all tasks are executed on the main thread
+  // before returning.
+  nsCOMPtr<nsIThread> mainThread;
+  nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = NS_OK;
+  bool processed = true;
+  while (processed && NS_SUCCEEDED(rv)) {
+    rv = mainThread->ProcessNextEvent(false, &processed);
+  }
+}
+
+// Tests that we can enable/disable vsync notifications
+TEST_F(VsyncTester, EnableVsync)
+{
+  if (!gfxPrefs::HardwareVsyncEnabled()) {
+    return;
+  }
+
+  VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+  globalDisplay.DisableVsync();
+  ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+  globalDisplay.EnableVsync();
+  ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+  globalDisplay.DisableVsync();
+  ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that if we have vsync enabled, the display should get vsync notifications
+TEST_F(VsyncTester, CompositorGetVsyncNotifications)
+{
+  if (!gfxPrefs::HardwareVsyncEnabled() || !gfxPrefs::VsyncAlignedCompositor()) {
+    return;
+  }
+
+  CompositorVsyncDispatcher::SetThreadAssertionsEnabled(false);
+
+  VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+  globalDisplay.DisableVsync();
+  ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+  nsRefPtr<CompositorVsyncDispatcher> vsyncDispatcher = new CompositorVsyncDispatcher();
+  nsRefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+
+  vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
+  FlushMainThreadLoop();
+  ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+  testVsyncObserver->WaitForVsyncNotification();
+  ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+  vsyncDispatcher = nullptr;
+  testVsyncObserver = nullptr;
+}
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -17,16 +17,17 @@ UNIFIED_SOURCES += [
     'TestRegion.cpp',
     'TestSkipChars.cpp',
     # Hangs on linux in ApplyGdkScreenFontOptions
     #'gfxFontSelectionTest.cpp',
     'TestTextures.cpp',
     # Test works but it doesn't assert anything
     #'gfxTextRunPerfTest.cpp',
     'TestTiledLayerBuffer.cpp',
+    'TestVsync.cpp',
 ]
 
 # Because of gkmedia on windows we won't find these
 # symbols in xul.dll.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
     UNIFIED_SOURCES += [ '%s/gfx/2d/unittest/%s' % (TOPSRCDIR, p) for p in [
         'TestBase.cpp',
         'TestBugs.cpp',
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -423,17 +423,29 @@ js::DirectEvalStringFromIon(JSContext *c
 
         esg.setNewScript(compiled);
     }
 
     // Primitive 'this' values should have been filtered out by Ion. If boxed,
     // the calling frame cannot be updated to store the new object.
     MOZ_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull());
 
-    return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL),
+    // When eval'ing strict code in a non-strict context, compute the 'this'
+    // value to use from what the caller passed in. This isn't necessary if
+    // the callee is not strict, as it will compute the non-strict 'this'
+    // value as necessary while it executes.
+    RootedValue nthisValue(cx, thisValue);
+    if (!callerScript->strict() && esg.script()->strict() && !thisValue.isObject()) {
+        JSObject *obj = BoxNonStrictThis(cx, thisValue);
+        if (!obj)
+            return false;
+        nthisValue = ObjectValue(*obj);
+    }
+
+    return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, ExecuteType(DIRECT_EVAL),
                          NullFramePtr() /* evalInFrame */, vp.address());
 }
 
 bool
 js::DirectEvalValueFromIon(JSContext *cx,
                            HandleObject scopeobj, HandleScript callerScript,
                            HandleValue thisValue, HandleValue evalArg,
                            jsbytecode *pc, MutableHandleValue vp)
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -185,45 +185,37 @@ js::ObjectToSource(JSContext *cx, Handle
 
     AutoIdVector idv(cx);
     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv))
         return nullptr;
 
     bool comma = false;
     for (size_t i = 0; i < idv.length(); ++i) {
         RootedId id(cx, idv[i]);
-        RootedObject obj2(cx);
-        RootedShape shape(cx);
-        if (!LookupProperty(cx, obj, id, &obj2, &shape))
+        Rooted<PropertyDescriptor> desc(cx);
+        if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
             return nullptr;
 
-        /*  Decide early whether we prefer get/set or old getter/setter syntax. */
         int valcnt = 0;
-        if (shape) {
-            bool doGet = true;
-            if (obj2->isNative() && !IsImplicitDenseOrTypedArrayElement(shape)) {
-                unsigned attrs = shape->attributes();
-                if (attrs & JSPROP_GETTER) {
-                    doGet = false;
-                    val[valcnt].set(shape->getterValue());
+        if (desc.object()) {
+            if (desc.hasGetterOrSetterObject()) {
+                if (desc.hasGetterObject() && desc.getterObject()) {
+                    val[valcnt].setObject(*desc.getterObject());
                     gsop[valcnt].set(cx->names().get);
                     valcnt++;
                 }
-                if (attrs & JSPROP_SETTER) {
-                    doGet = false;
-                    val[valcnt].set(shape->setterValue());
+                if (desc.hasSetterObject() && desc.setterObject()) {
+                    val[valcnt].setObject(*desc.setterObject());
                     gsop[valcnt].set(cx->names().set);
                     valcnt++;
                 }
-            }
-            if (doGet) {
+            } else {
                 valcnt = 1;
+                val[0].set(desc.value());
                 gsop[0].set(nullptr);
-                if (!GetProperty(cx, obj, obj, id, val[0]))
-                    return nullptr;
             }
         }
 
         /* Convert id to a string. */
         RootedString idstr(cx);
         if (JSID_IS_SYMBOL(id)) {
             RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
             idstr = ValueToSource(cx, v);
@@ -245,23 +237,16 @@ js::ObjectToSource(JSContext *cx, Handle
             {
                 idstr = js_QuoteString(cx, idstr, char16_t('\''));
                 if (!idstr)
                     return nullptr;
             }
         }
 
         for (int j = 0; j < valcnt; j++) {
-            /*
-             * Censor an accessor descriptor getter or setter part if it's
-             * undefined.
-             */
-            if (gsop[j] && val[j].isUndefined())
-                continue;
-
             /* Convert val[j] to its canonical source form. */
             JSString *valsource = ValueToSource(cx, val[j]);
             if (!valsource)
                 return nullptr;
 
             RootedLinearString valstr(cx, valsource->ensureLinear(cx));
             if (!valstr)
                 return nullptr;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -190,18 +190,304 @@ class GCSchedulingTunables
     bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
     unsigned minEmptyChunkCount() const { return minEmptyChunkCount_; }
     unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
 
     void setParameter(JSGCParamKey key, uint32_t value);
 };
 
 /*
- * Internal values that effect GC scheduling that are not directly exposed
- * in the GC API.
+ * GC Scheduling Overview
+ * ======================
+ *
+ * Scheduling GC's in SpiderMonkey/Firefox is tremendously complicated because
+ * of the large number of subtle, cross-cutting, and widely dispersed factors
+ * that must be taken into account. A summary of some of the more important
+ * factors follows.
+ *
+ * Cost factors:
+ *
+ *   * GC too soon and we'll revisit an object graph almost identical to the
+ *     one we just visited; since we are unlikely to find new garbage, the
+ *     traversal will be largely overhead. We rely heavily on external factors
+ *     to signal us that we are likely to find lots of garbage: e.g. "a tab
+ *     just got closed".
+ *
+ *   * GC too late and we'll run out of memory to allocate (e.g. Out-Of-Memory,
+ *     hereafter simply abbreviated to OOM). If this happens inside
+ *     SpiderMonkey we may be able to recover, but most embedder allocations
+ *     will simply crash on OOM, even if the GC has plenty of free memory it
+ *     could surrender.
+ *
+ *   * Memory fragmentation: if we fill the process with GC allocations, a
+ *     request for a large block of contiguous memory may fail because no
+ *     contiguous block is free, despite having enough memory available to
+ *     service the request.
+ *
+ *   * Management overhead: if our GC heap becomes large, we create extra
+ *     overhead when managing the GC's structures, even if the allocations are
+ *     mostly unused.
+ *
+ * Heap Management Factors:
+ *
+ *   * GC memory: The GC has its own allocator that it uses to make fixed size
+ *     allocations for GC managed things. In cases where the GC thing requires
+ *     larger or variable sized memory to implement itself, it is responsible
+ *     for using the system heap.
+ *
+ *   * C Heap Memory: Rather than allowing for large or variable allocations,
+ *     the SpiderMonkey GC allows GC things to hold pointers to C heap memory.
+ *     It is the responsibility of the thing to free this memory with a custom
+ *     finalizer (with the sole exception of NativeObject, which knows about
+ *     slots and elements for performance reasons). C heap memory has different
+ *     performance and overhead tradeoffs than GC internal memory, which need
+ *     to be considered with scheduling a GC.
+ *
+ * Application Factors:
+ *
+ *   * Most applications allocate heavily at startup, then enter a processing
+ *     stage where memory utilization remains roughly fixed with a slower
+ *     allocation rate. This is not always the case, however, so while we may
+ *     optimize for this pattern, we must be able to handle arbitrary
+ *     allocation patterns.
+ *
+ * Other factors:
+ *
+ *   * Other memory: This is memory allocated outside the purview of the GC.
+ *     Data mapped by the system for code libraries, data allocated by those
+ *     libraries, data in the JSRuntime that is used to manage the engine,
+ *     memory used by the embedding that is not attached to a GC thing, memory
+ *     used by unrelated processes running on the hardware that use space we
+ *     could otherwise use for allocation, etc. While we don't have to manage
+ *     it, we do have to take it into account when scheduling since it affects
+ *     when we will OOM.
+ *
+ *   * Physical Reality: All real machines have limits on the number of bits
+ *     that they are physically able to store. While modern operating systems
+ *     can generally make additional space available with swapping, at some
+ *     point there are simply no more bits to allocate. There is also the
+ *     factor of address space limitations, particularly on 32bit machines.
+ *
+ *   * Platform Factors: Each OS makes use of wildly different memory
+ *     management techniques. These differences result in different performance
+ *     tradeoffs, different fragmentation patterns, and different hard limits
+ *     on the amount of physical and/or virtual memory that we can use before
+ *     OOMing.
+ *
+ *
+ * Reasons for scheduling GC
+ * -------------------------
+ *
+ *  While code generally takes the above factors into account in only an ad-hoc
+ *  fashion, the API forces the user to pick a "reason" for the GC. We have a
+ *  bunch of JS::gcreason reasons in GCAPI.h. These fall into a few categories
+ *  that generally coincide with one or more of the above factors.
+ *
+ *  Embedding reasons:
+ *
+ *   1) Do a GC now because the embedding knows something useful about the
+ *      zone's memory retention state. These are gcreasons like LOAD_END,
+ *      PAGE_HIDE, SET_NEW_DOCUMENT, DOM_UTILS. Mostly, Gecko uses these to
+ *      indicate that a significant fraction of the scheduled zone's memory is
+ *      probably reclaimable.
+ *
+ *   2) Do some known amount of GC work now because the embedding knows now is
+ *      a good time to do a long, unblockable operation of a known duration.
+ *      These are INTER_SLICE_GC and REFRESH_FRAME.
+ *
+ *  Correctness reasons:
+ *
+ *   3) Do a GC now because correctness depends on some GC property. For
+ *      example, CC_WAITING is where the embedding requires the mark bits
+ *      to be set correct. Also, EVICT_NURSERY where we need to work on the tenured
+ *      heap.
+ *
+ *   4) Do a GC because we are shutting down: e.g. SHUTDOWN_CC or DESTROY_*.
+ *
+ *   5) Do a GC because a compartment was accessed between GC slices when we
+ *      would have otherwise discarded it. We have to do a second GC to clean
+ *      it up: e.g. COMPARTMENT_REVIVED.
+ *
+ *  Emergency Reasons:
+ *
+ *   6) Do an all-zones, non-incremental GC now because the embedding knows it
+ *      cannot wait: e.g. MEM_PRESSURE.
+ *
+ *   7) OOM when fetching a new Chunk results in a LAST_DITCH GC.
+ *
+ *  Heap Size Limitation Reasons:
+ *
+ *   8) Do an incremental, zonal GC with reason MAYBEGC when we discover that
+ *      the gc's allocated size is approaching the current trigger. This is
+ *      called MAYBEGC because we make this check in the MaybeGC function.
+ *      MaybeGC gets called at the top of the main event loop. Normally, it is
+ *      expected that this callback will keep the heap size limited. It is
+ *      relatively inexpensive, because it is invoked with no JS running and
+ *      thus few stack roots to scan. For this reason, the GC's "trigger" bytes
+ *      is less than the GC's "max" bytes as used by the trigger below.
+ *
+ *   9) Do an incremental, zonal GC with reason MAYBEGC when we go to allocate
+ *      a new GC thing and find that the GC heap size has grown beyond the
+ *      configured maximum (JSGC_MAX_BYTES). We trigger this GC by returning
+ *      nullptr and then calling maybeGC at the top level of the allocator.
+ *      This is then guaranteed to fail the "size greater than trigger" check
+ *      above, since trigger is always less than max. After performing the GC,
+ *      the allocator unconditionally returns nullptr to force an OOM exception
+ *      is raised by the script.
+ *
+ *      Note that this differs from a LAST_DITCH GC where we actually run out
+ *      of memory (i.e., a call to a system allocator fails) when trying to
+ *      allocate. Unlike above, LAST_DITCH GC only happens when we are really
+ *      out of memory, not just when we cross an arbitrary trigger; despite
+ *      this, it may still return an allocation at the end and allow the script
+ *      to continue, if the LAST_DITCH GC was able to free up enough memory.
+ *
+ *  10) Do a GC under reason ALLOC_TRIGGER when we are over the GC heap trigger
+ *      limit, but in the allocator rather than in a random call to maybeGC.
+ *      This occurs if we allocate too much before returning to the event loop
+ *      and calling maybeGC; this is extremely common in benchmarks and
+ *      long-running Worker computations. Note that this uses a wildly
+ *      different mechanism from the above in that it sets the interrupt flag
+ *      and does the GC at the next loop head, before the next alloc, or
+ *      maybeGC. The reason for this is that this check is made after the
+ *      allocation and we cannot GC with an uninitialized thing in the heap.
+ *
+ *  11) Do an incremental, zonal GC with reason TOO_MUCH_MALLOC when we have
+ *      malloced more than JSGC_MAX_MALLOC_BYTES in a zone since the last GC.
+ *
+ *
+ * Size Limitation Triggers Explanation
+ * ------------------------------------
+ *
+ *  The GC internally is entirely unaware of the context of the execution of
+ *  the mutator. It sees only:
+ *
+ *   A) Allocated size: this is the amount of memory currently requested by the
+ *      mutator. This quantity is monotonically increasing: i.e. the allocation
+ *      rate is always >= 0. It is also easy for the system to track.
+ *
+ *   B) Retained size: this is the amount of memory that the mutator can
+ *      currently reach. Said another way, it is the size of the heap
+ *      immediately after a GC (modulo background sweeping). This size is very
+ *      costly to know exactly and also extremely hard to estimate with any
+ *      fidelity.
+ *
+ *   For reference, a common allocated vs. retained graph might look like:
+ *
+ *       |                                  **         **
+ *       |                       **       ** *       **
+ *       |                     ** *     **   *     **
+ *       |           *       **   *   **     *   **
+ *       |          **     **     * **       * **
+ *      s|         * *   **       ** +  +    **
+ *      i|        *  *  *      +  +       +  +     +
+ *      z|       *   * * +  +                   +     +  +
+ *      e|      *    **+
+ *       |     *     +
+ *       |    *    +
+ *       |   *   +
+ *       |  *  +
+ *       | * +
+ *       |*+
+ *       +--------------------------------------------------
+ *                               time
+ *                                           *** = allocated
+ *                                           +++ = retained
+ *
+ *           Note that this is a bit of a simplification
+ *           because in reality we track malloc and GC heap
+ *           sizes separately and have a different level of
+ *           granularity and accuracy on each heap.
+ *
+ *   This presents some obvious implications for Mark-and-Sweep collectors.
+ *   Namely:
+ *       -> t[marking] ~= size[retained]
+ *       -> t[sweeping] ~= size[allocated] - size[retained]
+ *
+ *   In a non-incremental collector, maintaining low latency and high
+ *   responsiveness requires that total GC times be as low as possible. Thus,
+ *   in order to stay responsive when we did not have a fully incremental
+ *   collector, our GC triggers were focused on minimizing collection time.
+ *   Furthermore, since size[retained] is not under control of the GC, all the
+ *   GC could do to control collection times was reduce sweep times by
+ *   minimizing size[allocated], per the equation above.
+ *
+ *   The result of the above is GC triggers that focus on size[allocated] to
+ *   the exclusion of other important factors and default heuristics that are
+ *   not optimal for a fully incremental collector. On the other hand, this is
+ *   not all bad: minimizing size[allocated] also minimizes the chance of OOM
+ *   and sweeping remains one of the hardest areas to further incrementalize.
+ *
+ *      MAYBEGC
+ *      -------
+ *      Occurs when we return to the event loop and find our heap is getting
+ *      largish, but before t[marking] OR t[sweeping] is too large for a
+ *      responsive non-incremental GC. This is intended to be the common case
+ *      in normal web applications: e.g. we just finished an event handler and
+ *      the few objects we allocated when computing the new whatzitz have
+ *      pushed us slightly over the limit. After this GC we rescale the new
+ *      MAYBEGC trigger to 150% of size[retained] so that our non-incremental
+ *      GC times will always be proportional to this size rather than being
+ *      dominated by sweeping.
+ *
+ *      As a concession to mutators that allocate heavily during their startup
+ *      phase, we have a highFrequencyGCMode that ups the growth rate to 300%
+ *      of the current size[retained] so that we'll do fewer longer GCs at the
+ *      end of the mutator startup rather than more, smaller GCs.
+ *
+ *          Assumptions:
+ *            -> Responsiveness is proportional to t[marking] + t[sweeping].
+ *            -> size[retained] is proportional only to GC allocations.
+ *
+ *      ALLOC_TRIGGER (non-incremental)
+ *      -------------------------------
+ *      If we do not return to the event loop before getting all the way to our
+ *      gc trigger bytes then MAYBEGC will never fire. To avoid OOMing, we
+ *      succeed the current allocation and set the script interrupt so that we
+ *      will (hopefully) do a GC before we overflow our max and have to raise
+ *      an OOM exception for the script.
+ *
+ *          Assumptions:
+ *            -> Common web scripts will return to the event loop before using
+ *               10% of the current gcTriggerBytes worth of GC memory.
+ *
+ *      ALLOC_TRIGGER (incremental)
+ *      ---------------------------
+ *      In practice the above trigger is rough: if a website is just on the
+ *      cusp, sometimes it will trigger a non-incremental GC moments before
+ *      returning to the event loop, where it could have done an incremental
+ *      GC. Thus, we recently added an incremental version of the above with a
+ *      substantially lower threshold, so that we have a soft limit here. If
+ *      IGC can collect faster than the allocator generates garbage, even if
+ *      the allocator does not return to the event loop frequently, we should
+ *      not have to fall back to a non-incremental GC.
+ *
+ *      TOO_MUCH_MALLOC
+ *      ---------------
+ *      Performs a GC before size[allocated] - size[retained] gets too large
+ *      for non-incremental sweeping to be fast in the case that we have
+ *      significantly more malloc allocation than GC allocation. This is meant
+ *      to complement MAYBEGC triggers. We track this by counting malloced
+ *      bytes; the counter gets reset at every GC since we do not always have a
+ *      size at the time we call free. Because of this, the malloc heuristic
+ *      is, unfortunatly, not usefully able to augment our other GC heap
+ *      triggers and is limited to this singular heuristic.
+ *
+ *          Assumptions:
+ *            -> EITHER size[allocated_by_malloc] ~= size[allocated_by_GC]
+ *                 OR   time[sweeping] ~= size[allocated_by_malloc]
+ *            -> size[retained] @ t0 ~= size[retained] @ t1
+ *               i.e. That the mutator is in steady-state operation.
+ *
+ *      LAST_DITCH_GC
+ *      -------------
+ *      Does a GC because we are out of memory.
+ *
+ *          Assumptions:
+ *            -> size[retained] < size[available_memory]
  */
 class GCSchedulingState
 {
     /*
      * Influences how we schedule and run GC's in several subtle ways. The most
      * important factor is in how it controls the "HeapGrowthFactor". The
      * growth factor is a measure of how large (as a percentage of the last GC)
      * the heap is allowed to grow before we try to schedule another GC.
@@ -873,17 +1159,17 @@ class GCRuntime
     bool fullCompartmentChecks;
 
     Callback<JSGCCallback> gcCallback;
     CallbackVector<JSFinalizeCallback> finalizeCallbacks;
     CallbackVector<JSWeakPointerCallback> updateWeakPointerCallbacks;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
-     * from   maxMallocBytes down to zero.
+     * from maxMallocBytes down to zero.
      */
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytes;
 
     /*
      * Whether a GC has been triggered as a result of mallocBytes falling
      * below zero.
      */
     mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/sleep-without-timeout.js
@@ -0,0 +1,6 @@
+// sleep() should work without timeout() prior to it.
+
+sleep(0.001);
+sleep(0.01);
+sleep(0.1);
+sleep(1);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-29.js
@@ -0,0 +1,6 @@
+// Debugger.prototype.addDebuggee should not accept invisible-to-debugger globals.
+load(libdir + 'asserts.js');
+
+var g = newGlobal({ invisibleToDebugger: true });
+
+assertThrowsInstanceOf(() => { new Debugger(g); }, TypeError);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-26.js
@@ -0,0 +1,16 @@
+// Bug 1026477: Defining functions with D.F.p.eval works, even if there's
+// already a non-aliased var binding for the identifier.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onDebuggerStatement = function (frame) {
+  frame.older.eval('function f() { }');
+};
+
+// When the compiler sees the 'debugger' statement, it marks all variables as
+// aliased, but we want to test the case where f is in a stack frame slot, so we
+// put the 'debugger' statement in a separate function, and use frame.older to
+// get back to the anonymous function's frame.
+g.eval('function q() { debugger; }');
+assertEq(typeof g.eval('(function () { var f = 42; q(); return f; })();'),
+         "function");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-27.js
@@ -0,0 +1,13 @@
+// Bug 1026477: Defining functions with D.F.p.eval works, even if there's
+// already a var binding for the identifier.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onDebuggerStatement = function (frame) {
+  frame.eval('function f() { }');
+};
+
+// When the compiler sees the 'debugger' statement, it marks all variables as
+// aliased, so f will live in a Call object.
+assertEq(typeof g.eval('(function () { var f = 42; debugger; return f;})();'),
+         "function");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-unwrap-03.js
@@ -0,0 +1,15 @@
+// Debugger.Object.prototype.unwrap should not let us see things in
+// invisible-to-Debugger compartments.
+
+load(libdir + 'asserts.js');
+
+var g = newGlobal({ invisibleToDebugger: true });
+
+var dbg = new Debugger;
+
+// Create a wrapper in our compartment for the global.
+// Note that makeGlobalObjectReference won't do: it tries to dereference as far
+// as it can go.
+var /* yo */ DOwg = dbg.makeGlobalObjectReference(this).makeDebuggeeValue(g);
+
+assertThrowsInstanceOf(() => DOwg.unwrap(), Error);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/makeGlobalObjectReference-03.js
@@ -0,0 +1,8 @@
+// Debugger.prototype.makeGlobalObjectReference should not accept invisible-to-debugger globals.
+load(libdir + 'asserts.js');
+
+var g = newGlobal({ invisibleToDebugger: true });
+
+assertThrowsInstanceOf(function () {
+  (new Debugger).makeGlobalObjectReference(g)
+}, TypeError);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1123011.js
@@ -0,0 +1,8 @@
+
+var global = this;
+function f() {
+    return eval("'use strict'; this;");
+}
+for (var j = 0; j < 5; ++j) {
+    assertEq(f(), global);
+}
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -1031,16 +1031,42 @@ function rtofloat32_object(i) {
     var o = { valueOf: function () { return t; } };
     var x = Math.fround(o);
     t = 1000.1111111111;
     if (uceFault_tofloat32_object(i) || uceFault_tofloat32_object(i))
         assertEq(x, Math.fround(99.1111111111));
     return i;
 }
 
+var uceFault_trunc_to_int32_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_number'));
+function rtrunc_to_int32_number(i) {
+    var x = (i + 0.12) | 0;
+    if (uceFault_trunc_to_int32_number(i) || uceFault_trunc_to_int32_number(i))
+        assertEq(x, (i + 0.12) | 0);
+    return i;
+}
+
+var uceFault_trunc_to_int32_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_object'));
+function rtrunc_to_int32_object(i) {
+    var t1 = i + 0.12;
+    var o1 = { valueOf: function() { return t1; } };
+    var x = o1 | 0;
+    t1 = 777.12;
+    if (uceFault_trunc_to_int32_object(i) || uceFault_trunc_to_int32_object(i))
+        assertEq(x, (i + 0.12) | 0);
+}
+
+var uceFault_trunc_to_int32_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_string'));
+function rtrunc_to_int32_string(i) {
+    var x = (i + "0") | 0;
+    if (uceFault_trunc_to_int32_string(i) || uceFault_trunc_to_int32_string(i))
+        assertEq(x, (i + "0") | 0);
+    return i;
+}
+
 var uceFault_hypot_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number'));
 function rhypot_number(i) {
     var x = Math.hypot(i, i + 1);
     if (uceFault_hypot_number(i) || uceFault_hypot_number(i))
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
     return i;
 }
 
@@ -1190,16 +1216,19 @@ for (i = 0; i < 100; i++) {
     rstring_replace(i);
     rstring_replace_y(i);
     rstring_replace_g(i);
     rtypeof(i);
     rtodouble_value(i);
     rtodouble_number(i);
     rtofloat32_number(i);
     rtofloat32_object(i);
+    rtrunc_to_int32_number(i);
+    rtrunc_to_int32_object(i);
+    rtrunc_to_int32_string(i);
     rhypot_number(i);
     rhypot_object(i);
     rsin_number(i);
     rsin_object(i);
     rlog_number(i);
     rlog_object(i);
 }
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1010,16 +1010,24 @@ ICWarmUpCounter_Fallback::Compiler::gene
     // If profiler instrumentation is on, ensure that lastProfilingFrame is
     // the frame currently being OSR-ed
     {
         Label checkOk;
         AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
         masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &checkOk);
         masm.loadPtr(AbsoluteAddress((void*)&cx->mainThread().jitActivation), scratchReg);
         masm.loadPtr(Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()), scratchReg);
+
+        // It may be the case that we entered the baseline frame with
+        // profiling turned off on, then in a call within a loop (i.e. a
+        // callee frame), turn on profiling, then return to this frame,
+        // and then OSR with profiling turned on.  In this case, allow for
+        // lastProfilingFrame to be null.
+        masm.branchPtr(Assembler::Equal, scratchReg, Imm32(0), &checkOk);
+
         masm.branchPtr(Assembler::Equal, scratchReg, BaselineStackReg, &checkOk);
         masm.assumeUnreachable("Baseline OSR lastProfilingFrame mismatch.");
         masm.bind(&checkOk);
     }
 #endif
 
     // Jump into Ion.
     masm.loadPtr(Address(osrDataReg, offsetof(IonOsrTempData, jitcode)), scratchReg);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2071,36 +2071,28 @@ CodeGenerator::visitStackArgT(LStackArgT
     Address dest(StackPointer, stack_offset);
 
     if (arg->isFloatReg())
         masm.storeDouble(ToFloatRegister(arg), dest);
     else if (arg->isRegister())
         masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
     else
         masm.storeValue(*(arg->toConstant()), dest);
-
-    uint32_t slot = StackOffsetToSlot(stack_offset);
-    MOZ_ASSERT(slot - 1u < graph.totalSlotCount());
-    masm.propagateOOM(pushedArgumentSlots_.append(slot));
 }
 
 void
 CodeGenerator::visitStackArgV(LStackArgV *lir)
 {
     ValueOperand val = ToValue(lir, 0);
     uint32_t argslot = lir->argslot();
     MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount());
 
     int32_t stack_offset = StackOffsetOfPassedArg(argslot);
 
     masm.storeValue(val, Address(StackPointer, stack_offset));
-
-    uint32_t slot = StackOffsetToSlot(stack_offset);
-    MOZ_ASSERT(slot - 1u < graph.totalSlotCount());
-    masm.propagateOOM(pushedArgumentSlots_.append(slot));
 }
 
 void
 CodeGenerator::visitMoveGroup(LMoveGroup *group)
 {
     if (!group->numMoves())
         return;
 
@@ -2659,18 +2651,16 @@ CodeGenerator::visitCallNative(LCallNati
     masm.loadValue(Address(StackPointer, NativeExitFrameLayout::offsetOfResult()), JSReturnOperand);
 
     // The next instruction is removing the footer of the exit frame, so there
     // is no need for leaveFakeExitFrame.
 
     // Move the StackPointer back to its original location, unwinding the native exit frame.
     masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
     MOZ_ASSERT(masm.framePushed() == initialStack);
-
-    dropArguments(call->numStackArgs() + 1);
 }
 
 static void
 LoadDOMPrivate(MacroAssembler &masm, Register obj, Register priv)
 {
     // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
     // will be in the first slot but may be fixed or non-fixed.
     MOZ_ASSERT(obj != priv);
@@ -2790,18 +2780,16 @@ CodeGenerator::visitCallDOMNative(LCallD
     }
 
     // The next instruction is removing the footer of the exit frame, so there
     // is no need for leaveFakeExitFrame.
 
     // Move the StackPointer back to its original location, unwinding the native exit frame.
     masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
     MOZ_ASSERT(masm.framePushed() == initialStack);
-
-    dropArguments(call->numStackArgs() + 1);
 }
 
 typedef bool (*GetIntrinsicValueFn)(JSContext *cx, HandlePropertyName, MutableHandleValue);
 static const VMFunction GetIntrinsicValueInfo =
     FunctionInfo<GetIntrinsicValueFn>(GetIntrinsicValue);
 
 void
 CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir)
@@ -2908,18 +2896,16 @@ CodeGenerator::visitCallGeneric(LCallGen
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
-
-    dropArguments(call->numStackArgs() + 1);
 }
 
 void
 CodeGenerator::visitCallKnown(LCallKnown *call)
 {
     Register calleereg = ToRegister(call->getFunction());
     Register objreg    = ToRegister(call->getTempObject());
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
@@ -2976,18 +2962,16 @@ CodeGenerator::visitCallKnown(LCallKnown
     // If the return value of the constructing function is Primitive,
     // replace the return value with the Object from CreateThis.
     if (call->mir()->isConstructing()) {
         Label notPrimitive;
         masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
         masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
         masm.bind(&notPrimitive);
     }
-
-    dropArguments(call->numStackArgs() + 1);
 }
 
 void
 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
 {
     Register objreg = ToRegister(apply->getTempObject());
     MOZ_ASSERT(objreg != extraStackSize);
 
@@ -3814,21 +3798,16 @@ CodeGenerator::generateBody()
             if (const char *extra = iter->extraName())
                 JitSpewCont(JitSpew_Codegen, ":%s", extra);
             JitSpewFin(JitSpew_Codegen);
 #endif
 
             if (counts)
                 blockCounts->visitInstruction(*iter);
 
-            if (iter->safepoint() && pushedArgumentSlots_.length()) {
-                if (!markArgumentSlots(iter->safepoint()))
-                    return false;
-            }
-
 #ifdef CHECK_OSIPOINT_REGISTERS
             if (iter->safepoint())
                 resetOsiPointRegs(iter->safepoint());
 #endif
 
             if (iter->mirRaw()) {
                 // Only add instructions that have a tracked inline script tree.
                 if (iter->mirRaw()->trackedTree()) {
@@ -3847,17 +3826,16 @@ CodeGenerator::generateBody()
         if (masm.oom())
             return false;
 
 #if defined(JS_ION_PERF)
         perfSpewer->endBasicBlock(masm);
 #endif
     }
 
-    MOZ_ASSERT(pushedArgumentSlots_.empty());
     return true;
 }
 
 // Out-of-line object allocation for LNewArray.
 class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator>
 {
     LNewArray *lir_;
 
@@ -7126,28 +7104,29 @@ CodeGenerator::link(JSContext *cx, types
 
     // IonMonkey could have inferred better type information during
     // compilation. Since adding the new information to the actual type
     // information can reset the usecount, increase it back to what it was
     // before.
     if (warmUpCount > script->getWarmUpCount())
         script->incWarmUpCounter(warmUpCount - script->getWarmUpCount());
 
+    uint32_t argumentSlots = (gen->info().nargs() + 1) * sizeof(Value);
     uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
 
     AutoDiscardIonCode discardIonCode(cx, &recompileInfo);
 
     IonScript *ionScript =
       IonScript::New(cx, recompileInfo,
-                     graph.totalSlotCount(), scriptFrameSize,
+                     graph.totalSlotCount(), argumentSlots, scriptFrameSize,
                      snapshots_.listSize(), snapshots_.RVATableSize(),
                      recovers_.size(), bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), patchableBackedges_.length(), optimizationLevel);
     if (!ionScript)
         return false;
     discardIonCode.ionScript = ionScript;
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -733,17 +733,17 @@ IonScript::IonScript()
     recompileInfo_(),
     osrPcMismatchCounter_(0),
     pendingBuilder_(nullptr)
 {
 }
 
 IonScript *
 IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
-               uint32_t frameSlots, uint32_t frameSize,
+               uint32_t frameSlots, uint32_t argumentSlots, uint32_t frameSize,
                size_t snapshotsListSize, size_t snapshotsRVATableSize,
                size_t recoversSize, size_t bailoutEntries,
                size_t constants, size_t safepointIndices,
                size_t osiIndices, size_t cacheEntries,
                size_t runtimeSize,  size_t safepointsSize,
                size_t backedgeEntries, OptimizationLevel optimizationLevel)
 {
     static const int DataAlignment = sizeof(void *);
@@ -822,16 +822,18 @@ IonScript::New(JSContext *cx, types::Rec
     script->constantEntries_ = constants;
     offsetCursor += paddedConstantsSize;
 
     script->backedgeList_ = offsetCursor;
     script->backedgeEntries_ = backedgeEntries;
     offsetCursor += paddedBackedgeSize;
 
     script->frameSlots_ = frameSlots;
+    script->argumentSlots_ = argumentSlots;
+
     script->frameSize_ = frameSize;
 
     script->recompileInfo_ = recompileInfo;
     script->optimizationLevel_ = optimizationLevel;
 
     return script;
 }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5938,16 +5938,19 @@ IonBuilder::jsop_eval(uint32_t argc)
 
     if (script()->global().valueIsEval(ObjectValue(*singleton))) {
         if (argc != 1)
             return abort("Direct eval with more than one argument");
 
         if (!info().funMaybeLazy())
             return abort("Direct eval in global code");
 
+        if (info().funMaybeLazy()->isArrow())
+            return abort("Direct eval from arrow function");
+
         // The 'this' value for the outer and eval scripts must be the
         // same. This is not guaranteed if a primitive string/number/etc.
         // is passed through to the eval invoke as the primitive may be
         // boxed into different objects if accessed via 'this'.
         MIRType type = thisTypes ? thisTypes->getKnownMIRType() : MIRType_Value;
         if (type != MIRType_Object && type != MIRType_Null && type != MIRType_Undefined)
             return abort("Direct eval from script with maybe-primitive 'this'");
 
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -220,16 +220,19 @@ struct IonScript
 
     // Offset to and length of the safepoint table in bytes.
     uint32_t safepointsStart_;
     uint32_t safepointsSize_;
 
     // Number of bytes this function reserves on the stack.
     uint32_t frameSlots_;
 
+    // Number of bytes used passed in as formal arguments or |this|.
+    uint32_t argumentSlots_;
+
     // Frame size is the value that can be added to the StackPointer along
     // with the frame prefix to get a valid JitFrameLayout.
     uint32_t frameSize_;
 
     // Table mapping bailout IDs to snapshot offsets.
     uint32_t bailoutTable_;
     uint32_t bailoutEntries_;
 
@@ -321,17 +324,17 @@ struct IonScript
   private:
     void trace(JSTracer *trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
     static IonScript *New(JSContext *cx, types::RecompileInfo recompileInfo,
-                          uint32_t frameLocals, uint32_t frameSize,
+                          uint32_t frameSlots, uint32_t argumentSlots, uint32_t frameSize,
                           size_t snapshotsListSize, size_t snapshotsRVATableSize,
                           size_t recoversSize, size_t bailoutEntries,
                           size_t constants, size_t safepointIndexEntries,
                           size_t osiIndexEntries, size_t cacheEntries,
                           size_t runtimeSize, size_t safepointsSize,
                           size_t backedgeEntries, OptimizationLevel optimizationLevel);
     static void Trace(JSTracer *trc, IonScript *script);
     static void Destroy(FreeOp *fop, IonScript *script);
@@ -457,16 +460,19 @@ struct IonScript
         return constants()[index];
     }
     size_t numConstants() const {
         return constantEntries_;
     }
     uint32_t frameSlots() const {
         return frameSlots_;
     }
+    uint32_t argumentSlots() const {
+        return argumentSlots_;
+    }
     uint32_t frameSize() const {
         return frameSize_;
     }
     SnapshotOffset bailoutToSnapshot(uint32_t bailoutId) {
         MOZ_ASSERT(bailoutId < bailoutEntries_);
         return bailoutTable()[bailoutId];
     }
     const SafepointIndex *getSafepointIndex(uint32_t disp) const;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -934,69 +934,68 @@ MarkCalleeToken(JSTracer *trc, CalleeTok
         MarkScriptRoot(trc, &script, "jit-script");
         return CalleeToToken(script);
       }
       default:
         MOZ_CRASH("unknown callee token type");
     }
 }
 
+uintptr_t *
+JitFrameLayout::slotRef(SafepointSlotEntry where)
+{
+    if (where.stack)
+        return (uintptr_t *)((uint8_t *)this - where.slot);
+    return (uintptr_t *)((uint8_t *)argv() + where.slot);
+}
+
 #ifdef JS_NUNBOX32
 static inline uintptr_t
 ReadAllocation(const JitFrameIterator &frame, const LAllocation *a)
 {
     if (a->isGeneralReg()) {
         Register reg = a->toGeneralReg()->reg();
         return frame.machineState().read(reg);
     }
-    if (a->isStackSlot()) {
-        uint32_t slot = a->toStackSlot()->slot();
-        return *frame.jsFrame()->slotRef(slot);
-    }
-    uint32_t index = a->toArgument()->index();
-    uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv());
-    return *reinterpret_cast<uintptr_t *>(argv + index);
+    return *frame.jsFrame()->slotRef(SafepointSlotEntry(a));
 }
 #endif
 
 static void
-MarkFrameAndActualArguments(JSTracer *trc, const JitFrameIterator &frame)
+MarkExtraActualArguments(JSTracer *trc, const JitFrameIterator &frame)
 {
-    // The trampoline produced by |generateEnterJit| is pushing |this| on the
-    // stack, as requested by |setEnterJitData|.  Thus, this function is also
-    // used for marking the |this| value of the top-level frame.
+    // Mark any extra actual arguments for an Ion frame. Marking of |this| and
+    // the formal arguments is taken care of by the frame's safepoint/snapshot.
 
     JitFrameLayout *layout = frame.jsFrame();
 
+    if (!CalleeTokenIsFunction(layout->calleeToken())) {
+        MOZ_ASSERT(frame.numActualArgs() == 0);
+        return;
+    }
+
     size_t nargs = frame.numActualArgs();
-    MOZ_ASSERT_IF(!CalleeTokenIsFunction(layout->calleeToken()), nargs == 0);
-
-    // Trace function arguments. Note + 1 for thisv.
+    size_t nformals = CalleeTokenToFunction(layout->calleeToken())->nargs();
+
+    // Trace actual arguments. Note + 1 for thisv.
     Value *argv = layout->argv();
-    for (size_t i = 0; i < nargs + 1; i++)
+    for (size_t i = nformals + 1; i < nargs + 1; i++)
         gc::MarkValueRoot(trc, &argv[i], "ion-argv");
 }
 
 #ifdef JS_NUNBOX32
 static inline void
 WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value)
 {
     if (a->isGeneralReg()) {
         Register reg = a->toGeneralReg()->reg();
         frame.machineState().write(reg, value);
-        return;
+    } else {
+        *frame.jsFrame()->slotRef(SafepointSlotEntry(a)) = value;
     }
-    if (a->isStackSlot()) {
-        uint32_t slot = a->toStackSlot()->slot();
-        *frame.jsFrame()->slotRef(slot) = value;
-        return;
-    }
-    uint32_t index = a->toArgument()->index();
-    uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv());
-    *reinterpret_cast<uintptr_t *>(argv + index) = value;
 }
 #endif
 
 static void
 MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     JitFrameLayout *layout = (JitFrameLayout *)frame.fp();
 
@@ -1007,32 +1006,33 @@ MarkIonJSFrame(JSTracer *trc, const JitF
         // This frame has been invalidated, meaning that its IonScript is no
         // longer reachable through the callee token (JSFunction/JSScript->ion
         // is now nullptr or recompiled). Manually trace it here.
         IonScript::Trace(trc, ionScript);
     } else {
         ionScript = frame.ionScriptFromCalleeToken();
     }
 
-    MarkFrameAndActualArguments(trc, frame);
+    MarkExtraActualArguments(trc, frame);
 
     const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp());
 
     SafepointReader safepoint(ionScript, si);
 
     // Scan through slots which contain pointers (or on punboxing systems,
     // actual values).
-    uint32_t slot;
-    while (safepoint.getGcSlot(&slot)) {
-        uintptr_t *ref = layout->slotRef(slot);
+    SafepointSlotEntry entry;
+
+    while (safepoint.getGcSlot(&entry)) {
+        uintptr_t *ref = layout->slotRef(entry);
         gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(ref), "ion-gc-slot");
     }
 
-    while (safepoint.getValueSlot(&slot)) {
-        Value *v = (Value *)layout->slotRef(slot);
+    while (safepoint.getValueSlot(&entry)) {
+        Value *v = (Value *)layout->slotRef(entry);
         gc::MarkValueRoot(trc, v, "ion-gc-slot");
     }
 
     uintptr_t *spill = frame.spillBase();
     GeneralRegisterSet gcRegs = safepoint.gcSpills();
     GeneralRegisterSet valueRegs = safepoint.valueSpills();
     for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) {
         --spill;
@@ -1065,17 +1065,17 @@ static void
 MarkBailoutFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     JitFrameLayout *layout = (JitFrameLayout *)frame.fp();
 
     layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
 
     // We have to mark the list of actual arguments, as only formal arguments
     // are represented in the Snapshot.
-    MarkFrameAndActualArguments(trc, frame);
+    MarkExtraActualArguments(trc, frame);
 
     // Under a bailout, do not have a Safepoint to only iterate over GC-things.
     // Thus we use a SnapshotIterator to trace all the locations which would be
     // used to reconstruct the Baseline frame.
     //
     // Note that at the time where this function is called, we have not yet
     // started to reconstruct baseline frames.
 
@@ -1123,26 +1123,26 @@ UpdateIonJSFrameForMinorGC(JSTracer *trc
     uintptr_t *spill = frame.spillBase();
     for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) {
         --spill;
         if (slotsRegs.has(*iter))
             nursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill));
     }
 
     // Skip to the right place in the safepoint
-    uint32_t slot;
-    while (safepoint.getGcSlot(&slot));
-    while (safepoint.getValueSlot(&slot));
+    SafepointSlotEntry entry;
+    while (safepoint.getGcSlot(&entry));
+    while (safepoint.getValueSlot(&entry));
 #ifdef JS_NUNBOX32
     LAllocation type, payload;
     while (safepoint.getNunboxSlot(&type, &payload));
 #endif
 
-    while (safepoint.getSlotsOrElementsSlot(&slot)) {
-        HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot));
+    while (safepoint.getSlotsOrElementsSlot(&entry)) {
+        HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(entry));
         nursery.forwardBufferPointer(slots);
     }
 }
 
 static void
 MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     // Mark the ICStub pointer stored in the stub frame. This is necessary
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -8,16 +8,17 @@
 #define jit_JitFrames_h
 
 #include <stdint.h>
 
 #include "jscntxt.h"
 #include "jsfun.h"
 
 #include "jit/JitFrameIterator.h"
+#include "jit/Safepoints.h"
 
 namespace js {
 namespace jit {
 
 typedef void * CalleeToken;
 
 enum CalleeTokenTag
 {
@@ -395,21 +396,20 @@ class JitFrameLayout : public CommonFram
     }
     Value *argv() {
         return (Value *)(this + 1);
     }
     uintptr_t numActualArgs() const {
         return numActualArgs_;
     }
 
-    // Computes a reference to a slot, where a slot is a distance from the base
-    // frame pointer (as would be used for LStackSlot).
-    uintptr_t *slotRef(uint32_t slot) {
-        return (uintptr_t *)((uint8_t *)this - slot);
-    }
+    // Computes a reference to a stack or argument slot, where a slot is a
+    // distance from the base frame pointer, as would be used for LStackSlot
+    // or LArgument.
+    uintptr_t *slotRef(SafepointSlotEntry where);
 
     static inline size_t Size() {
         return sizeof(JitFrameLayout);
     }
 };
 
 // this is the layout of the frame that is used when we enter Ion code from platform ABI code
 class EntryFrameLayout : public JitFrameLayout
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -159,16 +159,17 @@ class LAllocation : public TempObject
         return isGeneralReg() || isFloatReg();
     }
     bool isRegister(bool needFloat) const {
         return needFloat ? isFloatReg() : isGeneralReg();
     }
     bool isMemory() const {
         return isStackSlot() || isArgument();
     }
+    inline uint32_t memorySlot() const;
     inline LUse *toUse();
     inline const LUse *toUse() const;
     inline const LGeneralReg *toGeneralReg() const;
     inline const LFloatReg *toFloatReg() const;
     inline const LStackSlot *toStackSlot() const;
     inline const LArgument *toArgument() const;
     inline const LConstantIndex *toConstantIndex() const;
     inline AnyRegister toRegister() const;
@@ -355,25 +356,32 @@ class LStackSlot : public LAllocation
         return data();
     }
 };
 
 // Arguments are reverse indices into the stack. The indices are byte indices.
 class LArgument : public LAllocation
 {
   public:
-    explicit LArgument(int32_t index)
+    explicit LArgument(uint32_t index)
       : LAllocation(ARGUMENT_SLOT, index)
     { }
 
-    int32_t index() const {
+    uint32_t index() const {
         return data();
     }
 };
 
+inline uint32_t
+LAllocation::memorySlot() const
+{
+    MOZ_ASSERT(isMemory());
+    return isStackSlot() ? toStackSlot()->slot() : toArgument()->index();
+}
+
 // Represents storage for a definition.
 class LDefinition
 {
     // Bits containing policy, type, and virtual register.
     uint32_t bits_;
 
     // Before register allocation, this optionally contains a fixed policy.
     // Register allocation assigns this field to a physical policy if none is
@@ -1227,33 +1235,50 @@ class LSnapshot : public TempObject
         return bailoutKind_;
     }
     void setBailoutKind(BailoutKind kind) {
         bailoutKind_ = kind;
     }
     void rewriteRecoveredInput(LUse input);
 };
 
+struct SafepointSlotEntry {
+    // Flag indicating whether this is a slot in the stack or argument space.
+    uint32_t stack:1;
+
+    // Byte offset of the slot, as in LStackSlot or LArgument.
+    uint32_t slot:31;
+
+    SafepointSlotEntry() { }
+    SafepointSlotEntry(bool stack, uint32_t slot)
+      : stack(stack), slot(slot)
+    { }
+    explicit SafepointSlotEntry(const LAllocation *a)
+      : stack(a->isStackSlot()), slot(a->memorySlot())
+    { }
+};
+
 struct SafepointNunboxEntry {
     uint32_t typeVreg;
     LAllocation type;
     LAllocation payload;
 
     SafepointNunboxEntry() { }
     SafepointNunboxEntry(uint32_t typeVreg, LAllocation type, LAllocation payload)
       : typeVreg(typeVreg), type(type), payload(payload)
     { }
 };
 
 class LSafepoint : public TempObject
 {
+    typedef SafepointSlotEntry SlotEntry;
     typedef SafepointNunboxEntry NunboxEntry;
 
   public:
-    typedef Vector<uint32_t, 0, JitAllocPolicy> SlotList;
+    typedef Vector<SlotEntry, 0, JitAllocPolicy> SlotList;
     typedef Vector<NunboxEntry, 0, JitAllocPolicy> NunboxList;
 
   private:
     // The information in a safepoint describes the registers and gc related
     // values that are live at the start of the associated instruction.
 
     // The set of registers which are live at an OOL call made within the
     // instruction. This includes any registers for inputs which are not
@@ -1278,34 +1303,34 @@ class LSafepoint : public TempObject
 
     // Offset to a position in the safepoint stream, or
     // INVALID_SAFEPOINT_OFFSET.
     uint32_t safepointOffset_;
 
     // Assembler buffer displacement to OSI point's call location.
     uint32_t osiCallPointOffset_;
 
-    // List of stack slots which have gcthing pointers.
+    // List of slots which have gcthing pointers.
     SlotList gcSlots_;
 
-    // List of stack slots which have Values.
+    // List of slots which have Values.
     SlotList valueSlots_;
 
 #ifdef JS_NUNBOX32
-    // List of registers (in liveRegs) and stack slots which contain pieces of Values.
+    // List of registers (in liveRegs) and slots which contain pieces of Values.
     NunboxList nunboxParts_;
 #elif JS_PUNBOX64
     // The subset of liveRegs which have Values.
     GeneralRegisterSet valueRegs_;
 #endif
 
     // The subset of liveRegs which contains pointers to slots/elements.
     GeneralRegisterSet slotsOrElementsRegs_;
 
-    // List of stack slots which have slots/elements pointers.
+    // List of slots which have slots/elements pointers.
     SlotList slotsOrElementsSlots_;
 
   public:
     void assertInvariants() {
         // Every register in valueRegs and gcRegs should also be in liveRegs.
 #ifndef JS_NUNBOX32
         MOZ_ASSERT((valueRegs().bits() & ~liveRegs().gprs().bits()) == 0);
 #endif
@@ -1342,18 +1367,18 @@ class LSafepoint : public TempObject
 #endif
     void addGcRegister(Register reg) {
         gcRegs_.addUnchecked(reg);
         assertInvariants();
     }
     GeneralRegisterSet gcRegs() const {
         return gcRegs_;
     }
-    bool addGcSlot(uint32_t slot) {
-        bool result = gcSlots_.append(slot);
+    bool addGcSlot(bool stack, uint32_t slot) {
+        bool result = gcSlots_.append(SlotEntry(stack, slot));
         if (result)
             assertInvariants();
         return result;
     }
     SlotList &gcSlots() {
         return gcSlots_;
     }
 
@@ -1362,79 +1387,74 @@ class LSafepoint : public TempObject
     }
     GeneralRegisterSet slotsOrElementsRegs() const {
         return slotsOrElementsRegs_;
     }
     void addSlotsOrElementsRegister(Register reg) {
         slotsOrElementsRegs_.addUnchecked(reg);
         assertInvariants();
     }
-    bool addSlotsOrElementsSlot(uint32_t slot) {
-        bool result = slotsOrElementsSlots_.append(slot);
+    bool addSlotsOrElementsSlot(bool stack, uint32_t slot) {
+        bool result = slotsOrElementsSlots_.append(SlotEntry(stack, slot));
         if (result)
             assertInvariants();
         return result;
     }
     bool addSlotsOrElementsPointer(LAllocation alloc) {
-        if (alloc.isStackSlot())
-            return addSlotsOrElementsSlot(alloc.toStackSlot()->slot());
+        if (alloc.isMemory())
+            return addSlotsOrElementsSlot(alloc.isStackSlot(), alloc.memorySlot());
         MOZ_ASSERT(alloc.isRegister());
         addSlotsOrElementsRegister(alloc.toRegister().gpr());
         assertInvariants();
         return true;
     }
     bool hasSlotsOrElementsPointer(LAllocation alloc) const {
         if (alloc.isRegister())
             return slotsOrElementsRegs().has(alloc.toRegister().gpr());
-        if (alloc.isStackSlot()) {
-            for (size_t i = 0; i < slotsOrElementsSlots_.length(); i++) {
-                if (slotsOrElementsSlots_[i] == alloc.toStackSlot()->slot())
-                    return true;
-            }
-            return false;
+        for (size_t i = 0; i < slotsOrElementsSlots_.length(); i++) {
+            const SlotEntry &entry = slotsOrElementsSlots_[i];
+            if (entry.stack == alloc.isStackSlot() && entry.slot == alloc.memorySlot())
+                return true;
         }
         return false;
     }
 
     bool addGcPointer(LAllocation alloc) {
-        if (alloc.isStackSlot())
-            return addGcSlot(alloc.toStackSlot()->slot());
+        if (alloc.isMemory())
+            return addGcSlot(alloc.isStackSlot(), alloc.memorySlot());
         if (alloc.isRegister())
             addGcRegister(alloc.toRegister().gpr());
         assertInvariants();
         return true;
     }
 
     bool hasGcPointer(LAllocation alloc) const {
         if (alloc.isRegister())
             return gcRegs().has(alloc.toRegister().gpr());
-        if (alloc.isStackSlot()) {
-            for (size_t i = 0; i < gcSlots_.length(); i++) {
-                if (gcSlots_[i] == alloc.toStackSlot()->slot())
-                    return true;
-            }
-            return false;
+        MOZ_ASSERT(alloc.isMemory());
+        for (size_t i = 0; i < gcSlots_.length(); i++) {
+            if (gcSlots_[i].stack == alloc.isStackSlot() && gcSlots_[i].slot == alloc.memorySlot())
+                return true;
         }
-        MOZ_ASSERT(alloc.isArgument());
-        return true;
+        return false;
     }
 
-    bool addValueSlot(uint32_t slot) {
-        bool result = valueSlots_.append(slot);
+    bool addValueSlot(bool stack, uint32_t slot) {
+        bool result = valueSlots_.append(SlotEntry(stack, slot));
         if (result)
             assertInvariants();
         return result;
     }
     SlotList &valueSlots() {
         return valueSlots_;
     }
 
-    bool hasValueSlot(uint32_t slot) const {
+    bool hasValueSlot(bool stack, uint32_t slot) const {
         for (size_t i = 0; i < valueSlots_.length(); i++) {
-            if (valueSlots_[i] == slot)
+            if (valueSlots_[i].stack == stack && valueSlots_[i].slot == slot)
                 return true;
         }
         return false;
     }
 
 #ifdef JS_NUNBOX32
 
     bool addNunboxParts(uint32_t typeVreg, LAllocation type, LAllocation payload) {
@@ -1489,19 +1509,17 @@ class LSafepoint : public TempObject
             if (nunboxParts_[i].typeVreg == typeVreg && !nunboxParts_[i].type.isUse())
                 return nunboxParts_[i].type;
         }
         return LUse(typeVreg, LUse::ANY);
     }
 
 #ifdef DEBUG
     bool hasNunboxPayload(LAllocation payload) const {
-        if (payload.isArgument())
-            return true;
-        if (payload.isStackSlot() && hasValueSlot(payload.toStackSlot()->slot()))
+        if (payload.isMemory() && hasValueSlot(payload.isStackSlot(), payload.memorySlot()))
             return true;
         for (size_t i = 0; i < nunboxParts_.length(); i++) {
             if (nunboxParts_[i].payload == payload)
                 return true;
         }
         return false;
     }
 #endif
@@ -1522,35 +1540,25 @@ class LSafepoint : public TempObject
 
     bool addBoxedValue(LAllocation alloc) {
         if (alloc.isRegister()) {
             Register reg = alloc.toRegister().gpr();
             if (!valueRegs().has(reg))
                 addValueRegister(reg);
             return true;
         }
-        if (alloc.isStackSlot()) {
-            uint32_t slot = alloc.toStackSlot()->slot();
-            for (size_t i = 0; i < valueSlots().length(); i++) {
-                if (valueSlots()[i] == slot)
-                    return true;
-            }
-            return addValueSlot(slot);
-        }
-        MOZ_ASSERT(alloc.isArgument());
-        return true;
+        if (hasValueSlot(alloc.isStackSlot(), alloc.memorySlot()))
+            return true;
+        return addValueSlot(alloc.isStackSlot(), alloc.memorySlot());
     }
 
     bool hasBoxedValue(LAllocation alloc) const {
         if (alloc.isRegister())
             return valueRegs().has(alloc.toRegister().gpr());
-        if (alloc.isStackSlot())
-            return hasValueSlot(alloc.toStackSlot()->slot());
-        MOZ_ASSERT(alloc.isArgument());
-        return true;
+        return hasValueSlot(alloc.isStackSlot(), alloc.memorySlot());
     }
 
 #endif // JS_PUNBOX64
 
     bool encoded() const {
         return safepointOffset_ != INVALID_SAFEPOINT_OFFSET;
     }
     uint32_t offset() const {
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -479,16 +479,31 @@ LinearScanAllocator::isSpilledAt(LiveInt
     }
 
     return interval->getAllocation() == reg->canonicalSpill();
 }
 
 bool
 LinearScanAllocator::populateSafepoints()
 {
+    // Populate all safepoints with this/argument slots. These are never changed
+    // by the allocator and are not necessarily populated by the code below.
+    size_t nargs = graph.getBlock(0)->mir()->info().nargs();
+    for (size_t i = 0; i < graph.numSafepoints(); i++) {
+        LSafepoint *safepoint = graph.getSafepoint(i)->safepoint();
+
+        if (!safepoint->addValueSlot(/* stack = */ false, THIS_FRAME_ARGSLOT * sizeof(Value)))
+            return false;
+
+        for (size_t j = 0; j < nargs; j++) {
+            if (!safepoint->addValueSlot(/* stack = */ false, (j + 1) * sizeof(Value)))
+                return false;
+        }
+    }
+
     size_t firstSafepoint = 0;
 
     for (uint32_t i = 0; i < vregs.numVirtualRegisters(); i++) {
         LinearScanVirtualRegister *reg = &vregs[i];
 
         if (!reg->def() || (!IsTraceable(reg) && !IsSlotsOrElements(reg) && !IsNunbox(reg)))
             continue;
 
@@ -528,17 +543,17 @@ LinearScanAllocator::populateSafepoints(
                 if (!interval)
                     continue;
 
                 LAllocation *a = interval->getAllocation();
                 if (a->isGeneralReg() && !ins->isCall())
                     safepoint->addSlotsOrElementsRegister(a->toGeneralReg()->reg());
 
                 if (isSpilledAt(interval, inputOf(ins))) {
-                    if (!safepoint->addSlotsOrElementsSlot(reg->canonicalSpillSlot()))
+                    if (!safepoint->addSlotsOrElementsSlot(true, reg->canonicalSpillSlot()))
                         return false;
                 }
             } else if (!IsNunbox(reg)) {
                 MOZ_ASSERT(IsTraceable(reg));
 
                 LiveInterval *interval = reg->intervalFor(inputOf(ins));
                 if (!interval)
                     continue;
@@ -553,22 +568,22 @@ LinearScanAllocator::populateSafepoints(
                     {
                         safepoint->addGcRegister(a->toGeneralReg()->reg());
                     }
                 }
 
                 if (isSpilledAt(interval, inputOf(ins))) {
 #ifdef JS_PUNBOX64
                     if (reg->type() == LDefinition::BOX) {
-                        if (!safepoint->addValueSlot(reg->canonicalSpillSlot()))
+                        if (!safepoint->addValueSlot(true, reg->canonicalSpillSlot()))
                             return false;
                     } else
 #endif
                     {
-                        if (!safepoint->addGcSlot(reg->canonicalSpillSlot()))
+                        if (!safepoint->addGcSlot(true, reg->canonicalSpillSlot()))
                             return false;
                     }
                 }
 #ifdef JS_NUNBOX32
             } else {
                 LinearScanVirtualRegister *other = otherHalfOfNunbox(reg);
                 LinearScanVirtualRegister *type = (reg->type() == LDefinition::TYPE) ? reg : other;
                 LinearScanVirtualRegister *payload = (reg->type() == LDefinition::PAYLOAD) ? reg : other;
@@ -593,17 +608,17 @@ LinearScanAllocator::populateSafepoints(
 
                 if (isSpilledAt(typeInterval, inputOf(ins)) &&
                     isSpilledAt(payloadInterval, inputOf(ins)))
                 {
                     // These two components of the Value are spilled
                     // contiguously, so simply keep track of the base slot.
                     uint32_t payloadSlot = payload->canonicalSpillSlot();
                     uint32_t slot = BaseOfNunboxSlot(LDefinition::PAYLOAD, payloadSlot);
-                    if (!safepoint->addValueSlot(slot))
+                    if (!safepoint->addValueSlot(true, slot))
                         return false;
                 }
 
                 if (!ins->isCall() &&
                     (!isSpilledAt(typeInterval, inputOf(ins)) || payloadAlloc->isGeneralReg()))
                 {
                     // Either the payload is on the stack but the type is
                     // in a register, or the payload is in a register. In
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4753,16 +4753,21 @@ class MTruncateToInt32
     void computeRange(TempAllocator &alloc) MOZ_OVERRIDE;
     TruncateKind operandTruncateKind(size_t index) const MOZ_OVERRIDE;
 # ifdef DEBUG
     bool isConsistentFloat32Use(MUse *use) const MOZ_OVERRIDE {
         return true;
     }
 #endif
 
+    bool writeRecoverData(CompactBufferWriter &writer) const;
+    bool canRecoverOnBailout() const {
+        return input()->type() < MIRType_Symbol;
+    }
+
     ALLOW_CLONE(MTruncateToInt32)
 };
 
 // Converts any type to a string
 class MToString :
   public MUnaryInstruction,
   public ToStringPolicy::Data
 {
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1120,16 +1120,43 @@ RToFloat32::recover(JSContext *cx, Snaps
     if (!RoundFloat32(cx, v, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MTruncateToInt32::writeRecoverData(CompactBufferWriter &writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_TruncateToInt32));
+    return true;
+}
+
+RTruncateToInt32::RTruncateToInt32(CompactBufferReader &reader)
+{ }
+
+bool
+RTruncateToInt32::recover(JSContext *cx, SnapshotIterator &iter) const
+{
+    RootedValue value(cx, iter.read());
+    RootedValue result(cx);
+
+    double in;
+    if (!ToNumber(cx, value, &in))
+        return false;
+    int out = ToInt32(in);
+
+    result.setInt32(out);
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
 MNewObject::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
     writer.writeByte(templateObjectIsClassPrototype_);
     return true;
 }
 
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -52,16 +52,17 @@ namespace jit {
     _(StringSplit)                              \
     _(RegExpExec)                               \
     _(RegExpTest)                               \
     _(RegExpReplace)                            \
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
+    _(TruncateToInt32)                          \
     _(NewObject)                                \
     _(NewArray)                                 \
     _(NewDerivedTypedObject)                    \
     _(CreateThisWithTemplate)                   \
     _(Lambda)                                   \
     _(ObjectState)                              \
     _(ArrayState)
 
@@ -592,16 +593,28 @@ class RToFloat32 MOZ_FINAL : public RIns
 
     virtual uint32_t numOperands() const {
         return 1;
     }
 
     bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
+class RTruncateToInt32 MOZ_FINAL : public RInstruction
+{
+  public:
+    RINSTRUCTION_HEADER_(TruncateToInt32)
+
+    virtual uint32_t numOperands() const {
+        return 1;
+    }
+
+    bool recover(JSContext *cx, SnapshotIterator &iter) const;
+};
+
 class RNewObject MOZ_FINAL : public RInstruction
 {
   private:
     bool templateObjectIsClassPrototype_;
 
   public:
     RINSTRUCTION_HEADER_(NewObject)
 
--- a/js/src/jit/Safepoints.cpp
+++ b/js/src/jit/Safepoints.cpp
@@ -12,24 +12,25 @@
 #include "jit/JitSpewer.h"
 #include "jit/LIR.h"
 
 using namespace js;
 using namespace jit;
 
 using mozilla::FloorLog2;
 
-SafepointWriter::SafepointWriter(uint32_t slotCount)
-  : frameSlots_(slotCount / sizeof(intptr_t))
+SafepointWriter::SafepointWriter(uint32_t slotCount, uint32_t argumentCount)
+  : frameSlots_((slotCount / sizeof(intptr_t)) + 1), // Stack slot counts are inclusive.
+    argumentSlots_(argumentCount / sizeof(intptr_t))
 { }
 
 bool
 SafepointWriter::init(TempAllocator &alloc)
 {
-    return frameSlots_.init(alloc);
+    return frameSlots_.init(alloc) && argumentSlots_.init(alloc);
 }
 
 uint32_t
 SafepointWriter::startEntry()
 {
     JitSpew(JitSpew_Safepoints, "Encoding safepoint (position %d):", stream_.length());
     return uint32_t(stream_.length());
 }
@@ -124,78 +125,85 @@ SafepointWriter::writeGcRegs(LSafepoint 
         }
         for (FloatRegisterForwardIterator iter(spilledFloat); iter.more(); iter++)
             JitSpew(JitSpew_Safepoints, "    float reg: %s", (*iter).name());
     }
 #endif
 }
 
 static void
-MapSlotsToBitset(BitSet &set, CompactBufferWriter &stream, uint32_t nslots, uint32_t *slots)
+WriteBitset(const BitSet &set, CompactBufferWriter &stream)
 {
-    set.clear();
-
-    for (uint32_t i = 0; i < nslots; i++) {
-        // Slots are represented at a distance from |fp|. We divide by the
-        // pointer size, since we only care about pointer-sized/aligned slots
-        // here. Since the stack grows down, this means slots start at index 1,
-        // so we subtract 1 to pack the bitset.
-        MOZ_ASSERT(slots[i] % sizeof(intptr_t) == 0);
-        MOZ_ASSERT(slots[i] / sizeof(intptr_t) > 0);
-        set.insert(slots[i] / sizeof(intptr_t) - 1);
-    }
-
     size_t count = set.rawLength();
     const uint32_t *words = set.raw();
     for (size_t i = 0; i < count; i++)
         stream.writeUnsigned(words[i]);
 }
 
+static void
+MapSlotsToBitset(BitSet &stackSet, BitSet &argumentSet,
+                 CompactBufferWriter &stream, const LSafepoint::SlotList &slots)
+{
+    stackSet.clear();
+    argumentSet.clear();
+
+    for (uint32_t i = 0; i < slots.length(); i++) {
+        // Slots are represented at a distance from |fp|. We divide by the
+        // pointer size, since we only care about pointer-sized/aligned slots
+        // here.
+        MOZ_ASSERT(slots[i].slot % sizeof(intptr_t) == 0);
+        size_t index = slots[i].slot / sizeof(intptr_t);
+        (slots[i].stack ? stackSet : argumentSet).insert(index);
+    }
+
+    WriteBitset(stackSet, stream);
+    WriteBitset(argumentSet, stream);
+}
+
 void
 SafepointWriter::writeGcSlots(LSafepoint *safepoint)
 {
     LSafepoint::SlotList &slots = safepoint->gcSlots();
 
 #ifdef DEBUG
     for (uint32_t i = 0; i < slots.length(); i++)
         JitSpew(JitSpew_Safepoints, "    gc slot: %d", slots[i]);
 #endif
 
-    MapSlotsToBitset(frameSlots_,
-                     stream_,
-                     slots.length(),
-                     slots.begin());
+    MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
 }
 
 void
 SafepointWriter::writeSlotsOrElementsSlots(LSafepoint *safepoint)
 {
     LSafepoint::SlotList &slots = safepoint->slotsOrElementsSlots();
 
     stream_.writeUnsigned(slots.length());
 
     for (uint32_t i = 0; i < slots.length(); i++) {
+        if (!slots[i].stack)
+            MOZ_CRASH();
 #ifdef DEBUG
-        JitSpew(JitSpew_Safepoints, "    slots/elements slot: %d", slots[i]);
+        JitSpew(JitSpew_Safepoints, "    slots/elements slot: %d", slots[i].slot);
 #endif
-        stream_.writeUnsigned(slots[i]);
+        stream_.writeUnsigned(slots[i].slot);
     }
 }
 
 void
 SafepointWriter::writeValueSlots(LSafepoint *safepoint)
 {
     LSafepoint::SlotList &slots = safepoint->valueSlots();
 
 #ifdef DEBUG
     for (uint32_t i = 0; i < slots.length(); i++)
         JitSpew(JitSpew_Safepoints, "    gc value: %d", slots[i]);
 #endif
 
-    MapSlotsToBitset(frameSlots_, stream_, slots.length(), slots.begin());
+    MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
 }
 
 #if defined(DEBUG) && defined(JS_NUNBOX32)
 static void
 DumpNunboxPart(const LAllocation &a)
 {
     if (a.isStackSlot()) {
         fprintf(JitSpewFile, "stack %d", a.toStackSlot()->slot());
@@ -379,17 +387,18 @@ void
 SafepointWriter::endEntry()
 {
     JitSpew(JitSpew_Safepoints, "    -- entry ended at %d", uint32_t(stream_.length()));
 }
 
 SafepointReader::SafepointReader(IonScript *script, const SafepointIndex *si)
   : stream_(script->safepoints() + si->safepointOffset(),
             script->safepoints() + script->safepointsSize()),
-    frameSlots_(script->frameSlots() / sizeof(intptr_t))
+    frameSlots_((script->frameSlots() / sizeof(intptr_t)) + 1), // Stack slot counts are inclusive.
+    argumentSlots_(script->argumentSlots() / sizeof(intptr_t))
 {
     osiCallPointOffset_ = stream_.readUnsigned();
 
     // gcSpills is a subset of allGprSpills.
     allGprSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
     if (allGprSpills_.empty()) {
         gcSpills_ = allGprSpills_;
         valueSpills_ = allGprSpills_;
@@ -420,64 +429,73 @@ SafepointReader::InvalidationPatchPoint(
     return CodeLocationLabel(script->method(), CodeOffsetLabel(reader.osiCallPointOffset()));
 }
 
 void
 SafepointReader::advanceFromGcRegs()
 {
     currentSlotChunk_ = 0;
     nextSlotChunkNumber_ = 0;
+    currentSlotsAreStack_ = true;
 }
 
 bool
-SafepointReader::getSlotFromBitmap(uint32_t *slot)
+SafepointReader::getSlotFromBitmap(SafepointSlotEntry *entry)
 {
     while (currentSlotChunk_ == 0) {
         // Are there any more chunks to read?
-        if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_))
+        if (currentSlotsAreStack_) {
+            if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_)) {
+                nextSlotChunkNumber_ = 0;
+                currentSlotsAreStack_ = false;
+                continue;
+            }
+        } else if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(argumentSlots_)) {
             return false;
+        }
 
         // Yes, read the next chunk.
         currentSlotChunk_ = stream_.readUnsigned();
         nextSlotChunkNumber_++;
     }