Merge m-c to fx-team.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 28 May 2013 21:25:32 -0400
changeset 144683 e58336e813950e8d8573accc65906ef1207127d1
parent 144682 cfcce7c5eb74d5603d992ce2932693e6729fe519 (current diff)
parent 144676 495b385ae811893ec1ea43cef7b7e1a1faf8926f (diff)
child 144686 2372a5ade39f08683365a752aabaf0044084b1e5
child 144766 b6f1f43be5047afbaa8f3c8c8af3d09a89d6f4f8
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
e58336e81395 / 24.0a1 / 20130529031131 / files
nightly linux64
e58336e81395 / 24.0a1 / 20130529031131 / files
nightly mac
e58336e81395 / 24.0a1 / 20130529031131 / files
nightly win32
e58336e81395 / 24.0a1 / 20130529031131 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Merge m-c to fx-team.
testing/config/Makefile.in
testing/config/moz.build
testing/mozbase/mozfile/tests/is_url.py
--- a/accessible/src/base/nsEventShell.cpp
+++ b/accessible/src/base/nsEventShell.cpp
@@ -2,16 +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/. */
 
 #include "nsEventShell.h"
 
 #include "nsAccUtils.h"
 
+#include "mozilla/StaticPtr.h"
+
+using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsEventShell
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 nsEventShell::FireEvent(AccEvent* aEvent)
@@ -56,9 +59,9 @@ nsEventShell::GetEventAttributes(nsINode
                          sEventFromUserInput ? NS_LITERAL_STRING("true") :
                                                NS_LITERAL_STRING("false"));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsEventShell: private
 
 bool nsEventShell::sEventFromUserInput = false;
-nsCOMPtr<nsINode> nsEventShell::sEventTargetNode;
+StaticRefPtr<nsINode> nsEventShell::sEventTargetNode;
--- a/accessible/src/base/nsEventShell.h
+++ b/accessible/src/base/nsEventShell.h
@@ -3,16 +3,19 @@
  * 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 _nsEventShell_H_
 #define _nsEventShell_H_
 
 #include "AccEvent.h"
 
+namespace mozilla {
+template<typename T> class StaticRefPtr;
+}
 class nsIPersistentProperties;
 
 /**
  * Used for everything about events.
  */
 class nsEventShell
 {
 public:
@@ -38,13 +41,13 @@ public:
    *
    * @param  aNode        [in] the DOM node
    * @param  aAttributes  [in, out] the attributes
    */
   static void GetEventAttributes(nsINode *aNode,
                                  nsIPersistentProperties *aAttributes);
 
 private:
-  static nsCOMPtr<nsINode> sEventTargetNode;
+  static mozilla::StaticRefPtr<nsINode> sEventTargetNode;
   static bool sEventFromUserInput;
 };
 
 #endif
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -14,41 +14,48 @@
 #include "nsStyleStructInlines.h"
 
 #include "nsIDOMXULLabeledControlEl.h"
 
 #include "nsArrayUtils.h"
 
 using namespace mozilla::a11y;
 
+/**
+ * The accessible for which we are computing a text equivalent. It is useful
+ * for bailing out during recursive text computation, or for special cases
+ * like step f. of the ARIA implementation guide.
+ */
+static Accessible* sInitiatorAcc = nullptr;
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsTextEquivUtils. Public.
 
 nsresult
 nsTextEquivUtils::GetNameFromSubtree(Accessible* aAccessible,
                                      nsAString& aName)
 {
   aName.Truncate();
 
-  if (gInitiatorAcc)
+  if (sInitiatorAcc)
     return NS_OK;
 
-  gInitiatorAcc = aAccessible;
+  sInitiatorAcc = aAccessible;
   if (GetRoleRule(aAccessible->Role()) == eNameFromSubtreeRule) {
     //XXX: is it necessary to care the accessible is not a document?
     if (aAccessible->IsContent()) {
       nsAutoString name;
       AppendFromAccessibleChildren(aAccessible, &name);
       name.CompressWhitespace();
       if (!IsWhitespaceString(name))
         aName = name;
     }
   }
 
-  gInitiatorAcc = nullptr;
+  sInitiatorAcc = nullptr;
 
   return NS_OK;
 }
 
 void
 nsTextEquivUtils::GetTextEquivFromSubtree(Accessible* aAccessible,
                                           nsString& aTextEquiv)
 {
@@ -87,43 +94,43 @@ nsTextEquivUtils::GetTextEquivFromIDRefs
 }
 
 nsresult
 nsTextEquivUtils::AppendTextEquivFromContent(Accessible* aInitiatorAcc,
                                              nsIContent *aContent,
                                              nsAString *aString)
 {
   // Prevent recursion which can cause infinite loops.
-  if (gInitiatorAcc)
+  if (sInitiatorAcc)
     return NS_OK;
 
-  gInitiatorAcc = aInitiatorAcc;
+  sInitiatorAcc = aInitiatorAcc;
 
   // If the given content is not visible or isn't accessible then go down
   // through the DOM subtree otherwise go down through accessible subtree and
   // calculate the flat string.
   nsIFrame *frame = aContent->GetPrimaryFrame();
   bool isVisible = frame && frame->StyleVisibility()->IsVisible();
 
   nsresult rv = NS_ERROR_FAILURE;
   bool goThroughDOMSubtree = true;
 
   if (isVisible) {
     Accessible* accessible =
-      gInitiatorAcc->Document()->GetAccessible(aContent);
+      sInitiatorAcc->Document()->GetAccessible(aContent);
     if (accessible) {
       rv = AppendFromAccessible(accessible, aString);
       goThroughDOMSubtree = false;
     }
   }
 
   if (goThroughDOMSubtree)
     rv = AppendFromDOMNode(aContent, aString);
 
-  gInitiatorAcc = nullptr;
+  sInitiatorAcc = nullptr;
   return rv;
 }
 
 nsresult
 nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
                                                  nsAString *aString)
 {
   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
@@ -171,18 +178,16 @@ nsTextEquivUtils::AppendTextEquivFromTex
   }
   
   return NS_OK_NO_NAME_CLAUSE_HANDLED;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsTextEquivUtils. Private.
 
-nsRefPtr<Accessible> nsTextEquivUtils::gInitiatorAcc;
-
 nsresult
 nsTextEquivUtils::AppendFromAccessibleChildren(Accessible* aAccessible,
                                                nsAString *aString)
 {
   nsresult rv = NS_OK_NO_NAME_CLAUSE_HANDLED;
 
   uint32_t childCount = aAccessible->ChildCount();
   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
@@ -252,17 +257,17 @@ nsTextEquivUtils::AppendFromValue(Access
     return NS_OK_NO_NAME_CLAUSE_HANDLED;
 
   // Implementation of step f. of text equivalent computation. If the given
   // accessible is not root accessible (the accessible the text equivalent is
   // computed for in the end) then append accessible value. Otherwise append
   // value if and only if the given accessible is in the middle of its parent.
 
   nsAutoString text;
-  if (aAccessible != gInitiatorAcc) {
+  if (aAccessible != sInitiatorAcc) {
     aAccessible->Value(text);
 
     return AppendString(aString, text) ?
       NS_OK : NS_OK_NO_NAME_CLAUSE_HANDLED;
   }
 
   //XXX: is it necessary to care the accessible is not a document?
   if (aAccessible->IsDoc())
--- a/accessible/src/base/nsTextEquivUtils.h
+++ b/accessible/src/base/nsTextEquivUtils.h
@@ -148,18 +148,11 @@ private:
    * Returns true if the given character is whitespace symbol.
    */
   static bool IsWhitespace(PRUnichar aChar);
 
   /**
    * Returns the rule (constant of ETextEquivRule) for a given role.
    */
   static uint32_t GetRoleRule(mozilla::a11y::roles::Role aRole);
-
-  /**
-   * The accessible for which we are computing a text equivalent. It is useful
-   * for bailing out during recursive text computation, or for special cases
-   * like step f. of the ARIA implementation guide.
-   */
-  static nsRefPtr<Accessible> gInitiatorAcc;
 };
 
 #endif
--- a/accessible/src/jsat/TraversalRules.jsm
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -21,17 +21,18 @@ function BaseTraversalRule(aRoles, aMatc
 
 BaseTraversalRule.prototype = {
     getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
       aRules.value = this._matchRoles;
       return aRules.value.length;
     },
 
     preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
-    Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE,
+    Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
+    Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN,
 
     match: function BaseTraversalRule_match(aAccessible)
     {
       if (aAccessible.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
         return (Utils.getMessageManager(aAccessible.DOMNode)) ?
           Ci.nsIAccessibleTraversalRule.FILTER_MATCH :
           Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
       }
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -179,16 +179,31 @@ this.Utils = {
       return [0, 0];
 
     let state = {};
     let extState = {};
     aAccessible.getState(state, extState);
     return [state.value, extState.value];
   },
 
+  getAttributes: function getAttributes(aAccessible) {
+    let attributesEnum = aAccessible.attributes.enumerate();
+    let attributes = {};
+
+    // Populate |attributes| object with |aAccessible|'s attribute key-value
+    // pairs.
+    while (attributesEnum.hasMoreElements()) {
+      let attribute = attributesEnum.getNext().QueryInterface(
+        Ci.nsIPropertyElement);
+      attributes[attribute.key] = attribute.value;
+    }
+
+    return attributes;
+  },
+
   getVirtualCursor: function getVirtualCursor(aDocument) {
     let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
       this.AccRetrieval.getAccessibleFor(aDocument);
 
     return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
   },
 
   getPixelsPerCSSPixel: function getPixelsPerCSSPixel(aWindow) {
--- a/accessible/src/jsat/UtteranceGenerator.jsm
+++ b/accessible/src/jsat/UtteranceGenerator.jsm
@@ -7,16 +7,17 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const INCLUDE_DESC = 0x01;
 const INCLUDE_NAME = 0x02;
 const INCLUDE_CUSTOM = 0x04;
+const NAME_FROM_SUBTREE_RULE = 0x08;
 
 const UTTERANCE_DESC_FIRST = 0;
 
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 
 let gUtteranceOrder = new PrefCache('accessibility.accessfu.utterance');
 
 var gStringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
@@ -69,49 +70,62 @@ this.UtteranceGenerator = {
    *    starting from the accessible's ancestry or accessible's subtree.
    */
   genForContext: function genForContext(aContext) {
     let utterance = [];
     let addUtterance = function addUtterance(aAccessible) {
       utterance.push.apply(utterance,
         UtteranceGenerator.genForObject(aAccessible));
     };
-
+    let roleString = Utils.AccRetrieval.getStringRole(aContext.accessible.role);
+    let nameRule = this.roleRuleMap[roleString] || 0;
     let utteranceOrder = gUtteranceOrder.value || UTTERANCE_DESC_FIRST;
+    // Include subtree if the name is not explicit or the role's name rule is
+    // not the NAME_FROM_SUBTREE_RULE.
+    let includeSubtree = (Utils.getAttributes(aContext.accessible)[
+      'explicit-name'] !== 'true') || !(nameRule & NAME_FROM_SUBTREE_RULE);
 
     if (utteranceOrder === UTTERANCE_DESC_FIRST) {
       aContext.newAncestry.forEach(addUtterance);
       addUtterance(aContext.accessible);
-      aContext.subtreePreorder.forEach(addUtterance);
+      if (includeSubtree) {
+        aContext.subtreePreorder.forEach(addUtterance);
+      }
     } else {
-      aContext.subtreePostorder.forEach(addUtterance);
+      if (includeSubtree) {
+        aContext.subtreePostorder.forEach(addUtterance);
+      }
       addUtterance(aContext.accessible);
       aContext.newAncestry.reverse().forEach(addUtterance);
     }
 
+    // Clean up the white space.
+    let trimmed;
+    utterance = [trimmed for (word of utterance) if (trimmed = word.trim())];
+
     return utterance;
   },
 
 
   /**
    * Generates an utterance for an object.
    * @param {nsIAccessible} aAccessible accessible object to generate utterance
    *    for.
    * @return {Array} Two string array. The first string describes the object
    *    and its states. The second string is the object's name. Whether the
    *    object's description or it's role is included is determined by
-   *    {@link verbosityRoleMap}.
+   *    {@link roleRuleMap}.
    */
   genForObject: function genForObject(aAccessible) {
     let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role);
 
     let func = this.objectUtteranceFunctions[roleString] ||
       this.objectUtteranceFunctions.defaultFunc;
 
-    let flags = this.verbosityRoleMap[roleString] || UTTERANCE_DESC_FIRST;
+    let flags = this.roleRuleMap[roleString] || 0;
 
     if (aAccessible.childCount == 0)
       flags |= INCLUDE_NAME;
 
     let state = {};
     let extState = {};
     aAccessible.getState(state, extState);
     let states = {base: state.value, ext: extState.value};
@@ -177,65 +191,83 @@ this.UtteranceGenerator = {
    * @param {aIsEditing} boolean true if we are in editing mode
    * @return {Array} The mode utterance
    */
   genForEditingMode: function genForEditingMode(aIsEditing) {
     return [gStringBundle.GetStringFromName(
               aIsEditing ? 'editingMode' : 'navigationMode')];
   },
 
-  verbosityRoleMap: {
+  roleRuleMap: {
     'menubar': INCLUDE_DESC,
     'scrollbar': INCLUDE_DESC,
     'grip': INCLUDE_DESC,
     'alert': INCLUDE_DESC | INCLUDE_NAME,
     'menupopup': INCLUDE_DESC,
-    'menuitem': INCLUDE_DESC,
-    'tooltip': INCLUDE_DESC,
+    'menuitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'tooltip': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'columnheader': NAME_FROM_SUBTREE_RULE,
+    'rowheader': NAME_FROM_SUBTREE_RULE,
+    'column': NAME_FROM_SUBTREE_RULE,
+    'row': NAME_FROM_SUBTREE_RULE,
     'application': INCLUDE_NAME,
     'document': INCLUDE_NAME,
     'grouping': INCLUDE_DESC | INCLUDE_NAME,
     'toolbar': INCLUDE_DESC,
     'table': INCLUDE_DESC | INCLUDE_NAME,
-    'link': INCLUDE_DESC,
+    'link': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'helpballoon': NAME_FROM_SUBTREE_RULE,
     'list': INCLUDE_DESC | INCLUDE_NAME,
-    'listitem': INCLUDE_DESC,
+    'listitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'outline': INCLUDE_DESC,
-    'outlineitem': INCLUDE_DESC,
-    'pagetab': INCLUDE_DESC,
+    'outlineitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'pagetab': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'graphic': INCLUDE_DESC,
-    'pushbutton': INCLUDE_DESC,
-    'checkbutton': INCLUDE_DESC,
-    'radiobutton': INCLUDE_DESC,
+    'pushbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'checkbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'radiobutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'buttondropdown': NAME_FROM_SUBTREE_RULE,
     'combobox': INCLUDE_DESC,
     'droplist': INCLUDE_DESC,
     'progressbar': INCLUDE_DESC,
     'slider': INCLUDE_DESC,
     'spinbutton': INCLUDE_DESC,
     'diagram': INCLUDE_DESC,
     'animation': INCLUDE_DESC,
     'equation': INCLUDE_DESC,
-    'buttonmenu': INCLUDE_DESC,
+    'buttonmenu': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'buttondropdowngrid': NAME_FROM_SUBTREE_RULE,
     'pagetablist': INCLUDE_DESC,
     'canvas': INCLUDE_DESC,
-    'check menu item': INCLUDE_DESC,
-    'label': INCLUDE_DESC,
+    'check menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'label': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'password text': INCLUDE_DESC,
     'popup menu': INCLUDE_DESC,
-    'radio menu item': INCLUDE_DESC,
-    'toggle button': INCLUDE_DESC,
+    'radio menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'table column header': NAME_FROM_SUBTREE_RULE,
+    'table row header': NAME_FROM_SUBTREE_RULE,
+    'tear off menu item': NAME_FROM_SUBTREE_RULE,
+    'toggle button': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'parent menuitem': NAME_FROM_SUBTREE_RULE,
     'header': INCLUDE_DESC,
     'footer': INCLUDE_DESC,
     'entry': INCLUDE_DESC | INCLUDE_NAME,
     'caption': INCLUDE_DESC,
     'document frame': INCLUDE_DESC,
     'heading': INCLUDE_DESC,
     'calendar': INCLUDE_DESC | INCLUDE_NAME,
     'combobox list': INCLUDE_DESC,
-    'combobox option': INCLUDE_DESC,
+    'combobox option': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
+    'listbox option': NAME_FROM_SUBTREE_RULE,
+    'listbox rich option': NAME_FROM_SUBTREE_RULE,
+    'gridcell': NAME_FROM_SUBTREE_RULE,
+    'check rich option': NAME_FROM_SUBTREE_RULE,
+    'term': NAME_FROM_SUBTREE_RULE,
+    'definition': NAME_FROM_SUBTREE_RULE,
+    'key': NAME_FROM_SUBTREE_RULE,
     'image map': INCLUDE_DESC,
     'option': INCLUDE_DESC,
     'listbox': INCLUDE_DESC,
     'definitionlist': INCLUDE_DESC | INCLUDE_NAME},
 
   objectUtteranceFunctions: {
     defaultFunc: function defaultFunc(aAccessible, aRoleStr, aStates, aFlags) {
       let utterance = [];
@@ -309,22 +341,26 @@ this.UtteranceGenerator = {
         return this.objectUtteranceFunctions.defaultFunc.apply(this,
           [aAccessible, aRoleStr, aStates, aFlags]);
 
       return [];
     }
   },
 
   _addName: function _addName(utterance, aAccessible, aFlags) {
-    let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
-    let utteranceOrder = gUtteranceOrder.value || 0;
+    let name;
+    if (Utils.getAttributes(aAccessible)['explicit-name'] === 'true' ||
+      (aFlags & INCLUDE_NAME)) {
+      name = aAccessible.name;
+    }
 
     if (name) {
+      let utteranceOrder = gUtteranceOrder.value || UTTERANCE_DESC_FIRST;
       utterance[utteranceOrder === UTTERANCE_DESC_FIRST ?
-        "push" : "unshift"](name);
+        'push' : 'unshift'](name);
     }
   },
 
   _getLocalizedRole: function _getLocalizedRole(aRoleStr) {
     try {
       return gStringBundle.GetStringFromName(aRoleStr.replace(' ', ''));
     } catch (x) {
       return '';
--- a/accessible/tests/mochitest/jsat/Makefile.in
+++ b/accessible/tests/mochitest/jsat/Makefile.in
@@ -8,13 +8,15 @@ topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
 jsatcommon.js \
+utterance.js \
 test_alive.html \
+test_explicit_names.html \
 test_utterance_order.html \
 $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_explicit_names.html
@@ -0,0 +1,166 @@
+<html>
+<head>
+  <title>[AccessFu] Trust explicitly associated names when speaking certain elements</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="utterance.js"></script>
+  <script type="application/javascript">
+
+    function doTest() {
+      // Test the following accOrElmOrID.
+      var tests = [{
+        accOrElmOrID: "anchor1",
+        expected: ["link", "title"]
+      }, {
+        accOrElmOrID: "anchor2",
+        expected: ["link", "This is a link"]
+      }, {
+        accOrElmOrID: "button1",
+        expected: ["button", "Press me"]
+      }, {
+        accOrElmOrID: "button2",
+        expected: ["button", "Press me"]
+      }, {
+        accOrElmOrID: "textarea1",
+        expected: ["text area", "Test Text Area", "This is the text area text."]
+      }, {
+        accOrElmOrID: "textarea2",
+        expected: ["text area", "This is the text area text."]
+      }, {
+        accOrElmOrID: "heading1",
+        expected: ["heading level 1", "Test heading", "This is the heading."]
+      }, {
+        accOrElmOrID: "heading2",
+        expected: ["heading level 1", "This is the heading."]
+      }, {
+        accOrElmOrID: "list",
+        expected: ["list 2 items", "Test List", "First item", "Top of the list",
+          "1.", "list one", "Last item", "2.", "list two"]
+      }, {
+        accOrElmOrID: "dlist",
+        expected: ["definition list 0.5 items", "Test Definition List",
+          "dd one"]
+      }, {
+        accOrElmOrID: "li_one",
+        expected: ["list 2 items", "Test List", "First item", "Top of the list"]
+      }, {
+        accOrElmOrID: "li_two",
+        expected: ["list 2 items", "Test List", "Last item", "2.", "list two"]
+      }, {
+        accOrElmOrID: "cell",
+        expected: ["table", "Fruits and vegetables", "List of Fruits",
+          "list 4 items","First item", "link", "Apples", "link", "Bananas",
+          "link", "Peaches", "Last item", "link", "Plums"]
+      }, {
+        accOrElmOrID: "app.net",
+        expected: ["list 2 items", "First item", "link", "star", "Last item",
+          "link", "repost"]
+      }, {
+        // Test pivot to list from li_one.
+        accOrElmOrID: "list",
+        oldAccOrElmOrID: "li_one",
+        expected: ["list 2 items", "Test List", "First item", "Top of the list",
+          "1.", "list one", "Last item", "2.", "list two"]
+      }, {
+        // Test pivot to li_one from list.
+        accOrElmOrID: "li_one",
+        oldAccOrElmOrID: "list",
+        expected: ["list 2 items", "Test List", "First item", "Top of the list"]
+      }, {
+        // Test pivot to "apples" link from the table cell.
+        accOrElmOrID: "apples",
+        oldAccOrElmOrID: "cell",
+        expected: ["List of Fruits", "list 4 items", "First item", "link",
+          "Apples"]
+      }, {
+        // Test pivot to the table cell from the "apples" link.
+        accOrElmOrID: "cell",
+        oldAccOrElmOrID: "apples",
+        expected: ["List of Fruits", "list 4 items", "First item", "link",
+          "Apples", "link", "Bananas", "link", "Peaches", "Last item", "link",
+          "Plums"]
+      }];
+
+      // Test various explicit names vs the utterance generated from subtrees.
+      tests.forEach(function run(test) {
+        testUtterance(test.expected, test.accOrElmOrID, test.oldAccOrElmOrID);
+      });
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+  <div id="root">
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=845870"
+       title="[AccessFu] Trust explicitly associated names when speaking certain elements">
+       Mozilla Bug 845870
+       </a>
+    <p id="display"></p>
+    <div id="content" style="display: none"></div>
+    <pre id="test"></pre>
+    <button id="button1" aria-label="Press me">This is not a name</button>
+    <button id="button2">
+      Press me
+    </button>
+    <a id="anchor1" href="#test" title="title"></a>
+    <a id="anchor2" href="#test">This is a link</a>
+    <textarea id="textarea1" title="Test Text Area" cols="80" rows="5">This is the text area text.</textarea>
+    <textarea id="textarea2" cols="80" rows="5">
+      This is the text area text.
+    </textarea>
+    <h1 id="heading1" title="Test heading">This is the heading.</h1>
+    <h1 id="heading2">
+      This is the heading.
+    </h1>
+    <ol id="list" title="Test List">
+      <li id="li_one" aria-label="Top of the list">list one</li>
+      <li id="li_two">list two</li>
+    </ol>
+    <dl id="dlist" title="Test Definition List">
+      <dd id="dd_one">dd one</dd>
+    </dl>
+    <table>
+      <caption>Fruits and vegetables</caption>
+      <tr>
+        <td id="cell" aria-label="List of Fruits">
+          <ul style="list-style-type: none;">
+            <li><a id="apples" href="#">Apples</a></li>
+            <li><a id="bananas" href="#">Bananas</a></li>
+            <li><a href="#">Peaches</a></li>
+            <li>
+              <a href="#">
+
+                Plums
+              </a>
+            </li>
+          </ul>
+        </td>
+      </tr>
+    </table>
+    <!-- app.net -->
+    <ul id="app.net" class="unstyled ul-horizontal yui3-u fixed-right ta-right" style="list-style-type: none;">
+      <li class="yui3-u">
+        <a href="#star" data-starred="0" data-star-button="1" data-post-id="5098826">
+          <i aria-label="star" class="icon-star-empty"></i>
+        </a>
+      </li>
+      <li class="yui3-u repost">
+        <a href="#repost" title="repost" data-repost-button="1" data-reposted="0" data-post-id="5098826">
+          <i aria-label="repost" class="icon-repost"></i>
+        </a>
+      </li>
+    </ul>
+  </div>
+</body>
+</html>
\ No newline at end of file
--- a/accessible/tests/mochitest/jsat/test_utterance_order.html
+++ b/accessible/tests/mochitest/jsat/test_utterance_order.html
@@ -7,138 +7,100 @@ https://bugzilla.mozilla.org/show_bug.cg
     <title>[AccessFu] utterance order test</title>
     <meta charset="utf-8">
     <link rel="stylesheet" type="text/css"
           href="chrome://mochikit/content/tests/SimpleTest/test.css" />
     <script type="application/javascript"
             src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
     <script type="application/javascript"
             src="../common.js"></script>
+    <script type="application/javascript"
+            src="./utterance.js"></script>
     <script type="application/javascript">
 
-      const Cu = Components.utils;
-      const PREF_UTTERANCE_ORDER = "accessibility.accessfu.utterance";
-
-      Cu.import('resource://gre/modules/accessibility/Utils.jsm');
-      Cu.import("resource://gre/modules/accessibility/UtteranceGenerator.jsm",
-        this);
-
-      // Test UtteranceGenerator.genForContext utterance order for
-      // a particular accessible context with an optional old accessible.
-      function testContextUtterance(expected, aAccOrElmOrID, oldAAccOrElmOrID) {
-        oldAAccOrElmOrID = oldAAccOrElmOrID || "root";
-        var accessible = getAccessible(aAccOrElmOrID);
-        var oldAccessible = getAccessible(oldAAccOrElmOrID);
-        var context = new PivotContext(accessible, oldAccessible);
-        var utterance = UtteranceGenerator.genForContext(context);
-        isDeeply(utterance, expected,
-          "Utterance order is correct for " + aAccOrElmOrID);
-      }
-
-      // Test UtteranceGenerator.genForObject for a particular aAccOrElmOrID.
-      function testUtterance(utteranceOrder, aAccOrElmOrID) {
-        var accessible = getAccessible(aAccOrElmOrID);
-        var utterance = UtteranceGenerator.genForObject(accessible);
-        var expectedNameIndex = utteranceOrder === 0 ? utterance.length - 1 : 0;
-
-        if (aAccOrElmOrID === "li_one" || aAccOrElmOrID === "cell") {
-          // List item's and table cell's name is not included into an object
-          // utterance.
-          expectedNameIndex = -1;
-        }
-        ok(utterance.indexOf(accessible.name) === expectedNameIndex,
-          "Object utterance is correct for " + aAccOrElmOrID);
-      }
-
       function doTest() {
-        // Test the following aAccOrElmOrID (with optional old aAccOrElmOrID).
-        // Note: each aAccOrElmOrID entry maps to a unique object utterance
+        // Test the following accOrElmOrID (with optional old accOrElmOrID).
+        // Note: each accOrElmOrID entry maps to a unique object utterance
         // generator function within the UtteranceGenerator.
         var tests = [{
-          aAccOrElmOrID: "anchor",
+          accOrElmOrID: "anchor",
           expected: [["link", "title"], ["title", "link"]]
         }, {
-          aAccOrElmOrID: "textarea",
+          accOrElmOrID: "textarea",
           expected: [[
-            "text area", "Test Text Area", "This is the text area text."
+            "text area", "This is the text area text."
           ], [
-            "This is the text area text.", "Test Text Area", "text area"
+            "This is the text area text.", "text area"
           ]]
         }, {
-          aAccOrElmOrID: "heading",
+          accOrElmOrID: "heading",
           expected: [
             ["heading level 1", "Test heading"],
             ["Test heading", "heading level 1"]
           ]
         }, {
-          aAccOrElmOrID: "list",
+          accOrElmOrID: "list",
           expected: [
-            ["list 1 items", "Test List", "First item", "1. ", "list one"],
-            ["1. ", "list one", "First item", "Test List", "list 1 items"]
+            ["list 1 items", "First item", "1.", "list one"],
+            ["1.", "list one", "First item", "list 1 items"]
           ]
         }, {
-          aAccOrElmOrID: "dlist",
+          accOrElmOrID: "dlist",
           expected: [
-            ["definition list 0.5 items", "Test Definition List", "dd one "],
-            ["dd one ", "Test Definition List", "definition list 0.5 items"]
+            ["definition list 0.5 items", "dd one"],
+            ["dd one", "definition list 0.5 items"]
           ]
         }, {
-          aAccOrElmOrID: "li_one",
+          accOrElmOrID: "li_one",
           expected: [
-            ["list 1 items", "Test List", "First item", "1. ", "list one"],
-            ["1. ", "list one", "First item", "Test List", "list 1 items"]
+            ["list 1 items", "First item", "1.", "list one"],
+            ["1.", "list one", "First item", "list 1 items"]
           ]
         }, {
-          aAccOrElmOrID: "cell",
+          accOrElmOrID: "cell",
           expected: [[
-            "table", "Fruits and vegetables", "list 4 items", "First item", " ",
-            "link", "Apples", " ", "link", "Bananas", " ", "link", "Peaches",
-            "Last item", " ", "link", "Plums"
+            "table", "Fruits and vegetables", "list 4 items", "First item",
+            "link", "Apples", "link", "Bananas", "link", "Peaches",
+            "Last item", "link", "Plums"
           ], [
-            " ", "Apples", "link", "First item", " ", "Bananas", "link", " ",
-            "Peaches", "link", " ", "Plums", "link", "Last item",
-            "list 4 items", "Fruits and vegetables", "table"
+            "Apples", "link", "First item", "Bananas", "link", "Peaches",
+            "link", "Plums", "link", "Last item", "list 4 items",
+            "Fruits and vegetables", "table"
           ]]
         }, {
           // Test pivot to list from li_one.
-          aAccOrElmOrID: "list",
-          oldAAccOrElmOrID: "li_one",
+          accOrElmOrID: "list",
+          oldAccOrElmOrID: "li_one",
           expected: [
-            ["list 1 items", "Test List", "First item", "1. ", "list one"],
-            ["1. ", "list one", "First item", "Test List", "list 1 items"]
+            ["list 1 items", "First item", "1.", "list one"],
+            ["1.", "list one", "First item", "list 1 items"]
           ]
         }, {
           // Test pivot to "apples" link from the table cell.
-          aAccOrElmOrID: "apples",
-          oldAAccOrElmOrID: "cell",
+          accOrElmOrID: "apples",
+          oldAccOrElmOrID: "cell",
           expected: [
             ["list 4 items", "First item", "link", "Apples"],
             ["Apples", "link", "First item", "list 4 items"]
           ]
         }, {
           // Test pivot to 'bananas' link from 'apples' link.
-          aAccOrElmOrID: "bananas",
-          oldAAccOrElmOrID: "apples",
+          accOrElmOrID: "bananas",
+          oldAccOrElmOrID: "apples",
           expected: [["link", "Bananas"], ["Bananas", "link"]]
         }];
 
         // 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);
-              testContextUtterance(test.expected[utteranceOrder],
-                test.aAccOrElmOrID, test.oldAAccOrElmOrID);
-              // Just need to test object utterance for individual
-              // aAccOrElmOrID.
-              if (test.oldAAccOrElmOrID) {
-                return;
-              }
-              testUtterance(utteranceOrder, test.aAccOrElmOrID);
+              var expected = test.expected[utteranceOrder];
+              testUtterance(expected, test.accOrElmOrID, test.oldAccOrElmOrID);
             }
           );
         });
 
         // If there was an original utterance order preference, revert to it.
         SpecialPowers.clearUserPref(PREF_UTTERANCE_ORDER);
         SimpleTest.finish();
       }
@@ -153,32 +115,40 @@ https://bugzilla.mozilla.org/show_bug.cg
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=753984"
          title="[AccessFu] utterance order test">
          Mozilla Bug 753984</a>
       <p id="display"></p>
       <div id="content" style="display: none"></div>
       <pre id="test"></pre>
       <a id="anchor" href="#test" title="title"></a>
-      <textarea id="textarea" title="Test Text Area" cols="80" rows="5">This is the text area text.</textarea>
+      <textarea id="textarea" cols="80" rows="5">
+        This is the text area text.
+      </textarea>
       <h1 id="heading" title="Test heading"></h1>
-      <ol id="list" title="Test List">
+      <ol id="list">
         <li id="li_one">list one</li>
       </ol>
-      <dl id="dlist" title="Test Definition List">
-        <dd id="dd_one">dd one</li>
+      <dl id="dlist">
+        <dd id="dd_one">
+          dd one
+        </dd>
       </dl>
       <table>
         <caption>Fruits and vegetables</caption>
         <tr>
           <td id="cell">
             <ul style="list-style-type: none;">
               <li><a id="apples" href="#">Apples</a></li>
               <li><a id="bananas" href="#">Bananas</a></li>
               <li><a href="#">Peaches</a></li>
-              <li><a href="#">Plums</a></li>
+              <li>
+                <a href="#">
+                  Plums
+                </a>
+              </li>
             </ul>
           </td>
         </tr>
       </table>
     </div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/utterance.js
@@ -0,0 +1,70 @@
+const Cu = Components.utils;
+const PREF_UTTERANCE_ORDER = "accessibility.accessfu.utterance";
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import("resource://gre/modules/accessibility/UtteranceGenerator.jsm",
+  this);
+
+/**
+ * Test context utterance generation.
+ *
+ * @param expected {Array} expected utterance.
+ * @param aAccOrElmOrID    identifier to get an accessible to test.
+ * @param aOldAccOrElmOrID optional identifier to get an accessible relative to
+ *                         the |aAccOrElmOrID|.
+ *
+ * Note: if |aOldAccOrElmOrID| is not provided, the |aAccOrElmOrID| must be
+ * scoped to the "root" element in markup.
+ */
+function testContextUtterance(expected, aAccOrElmOrID, aOldAccOrElmOrID) {
+  aOldAccOrElmOrID = aOldAccOrElmOrID || "root";
+  var accessible = getAccessible(aAccOrElmOrID);
+  var oldAccessible = getAccessible(aOldAccOrElmOrID);
+  var context = new PivotContext(accessible, oldAccessible);
+  var utterance = UtteranceGenerator.genForContext(context);
+  isDeeply(utterance, expected,
+    "Context utterance is correct for " + aAccOrElmOrID);
+}
+
+/**
+ * Test object utterance generated array that includes names.
+ * Note: test ignores utterances without the name.
+ *
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ */
+function testObjectUtterance(aAccOrElmOrID) {
+  var accessible = getAccessible(aAccOrElmOrID);
+  var utterance = UtteranceGenerator.genForObject(accessible);
+  var utteranceOrder;
+  try {
+    utteranceOrder = SpecialPowers.getIntPref(PREF_UTTERANCE_ORDER);
+  } catch (ex) {
+    // PREF_UTTERANCE_ORDER not set.
+    utteranceOrder = 0;
+  }
+  var expectedNameIndex = utteranceOrder === 0 ? utterance.length - 1 : 0;
+  var nameIndex = utterance.indexOf(accessible.name);
+
+  if (nameIndex > -1) {
+    ok(utterance.indexOf(accessible.name) === expectedNameIndex,
+      "Object utterance is correct for " + aAccOrElmOrID);
+  }
+}
+
+/**
+ * Test object and context utterance for an accessible.
+ *
+ * @param expected {Array} expected utterance.
+ * @param aAccOrElmOrID    identifier to get an accessible to test.
+ * @param aOldAccOrElmOrID optional identifier to get an accessible relative to
+ *                         the |aAccOrElmOrID|.
+ */
+function testUtterance(expected, aAccOrElmOrID, aOldAccOrElmOrID) {
+  testContextUtterance(expected, aAccOrElmOrID, aOldAccOrElmOrID);
+  // Just need to test object utterance for individual
+  // accOrElmOrID.
+  if (aOldAccOrElmOrID) {
+    return;
+  }
+  testObjectUtterance(aAccOrElmOrID);
+}
\ No newline at end of file
--- a/b2g/chrome/content/dbg-webapps-actors.js
+++ b/b2g/chrome/content/dbg-webapps-actors.js
@@ -123,17 +123,17 @@ WebappsActor.prototype = {
             if (!aManifest) {
               self._sendError("Error Parsing manifest.webapp", aId);
               return;
             }
 
             let appType = self._getAppType(aManifest.type);
 
             // In production builds, don't allow installation of certified apps.
-#ifdef MOZ_OFFICIAL
+#ifdef MOZ_OFFICIAL_BRANDING
             if (appType == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
               self._sendError("Installing certified apps is not allowed.", aId);
               return;
             }
 #endif
             // The destination directory for this app.
             let installDir = FileUtils.getDir(DIRECTORY_NAME,
                                               ["webapps", aId], true);
@@ -205,17 +205,17 @@ WebappsActor.prototype = {
           DOMApplicationRegistry._loadJSONAsync(manFile, function(aManifest) {
             if (!aManifest) {
               self._sendError("Error Parsing manifest.webapp", aId);
             }
 
             let appType = self._getAppType(aManifest.type);
 
             // In production builds, don't allow installation of certified apps.
-#ifdef MOZ_OFFICIAL
+#ifdef MOZ_OFFICIAL_BRANDING
             if (appType == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
               self._sendError("Installing certified apps is not allowed.", aId);
               return;
             }
 #endif
             let origin = "app://" + aId;
 
             // Create a fake app object with the minimum set of properties we need.
@@ -281,17 +281,17 @@ WebappsActor.prototype = {
         .some(function(aName) {
           testFile = appDir.clone();
           testFile.append(aName);
           return !testFile.exists();
         });
 
       if (missing) {
         try {
-          aDir.remove(true);
+          appDir.remove(true);
         } catch(e) {}
         return { error: "badParameterType",
                  message: "hosted app file is missing" }
       }
 
       this.installHostedApp(appDir, appId);
     }
 
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -13,17 +13,22 @@ var FullScreen = {
     var enterFS = window.fullScreen;
 
     // We get the fullscreen event _before_ the window transitions into or out of FS mode.
     if (event && event.type == "fullscreen")
       enterFS = !enterFS;
 
     // Toggle the View:FullScreen command, which controls elements like the
     // fullscreen menuitem, menubars, and the appmenu.
-    document.getElementById("View:FullScreen").setAttribute("checked", enterFS);
+    let fullscreenCommand = document.getElementById("View:FullScreen");
+    if (enterFS) {
+      fullscreenCommand.setAttribute("checked", enterFS);
+    } else {
+      fullscreenCommand.removeAttribute("checked");
+    }
 
 #ifdef XP_MACOSX
     // Make sure the menu items are adjusted.
     document.getElementById("enterFullScreenItem").hidden = enterFS;
     document.getElementById("exitFullScreenItem").hidden = !enterFS;
 #endif
 
     // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless
--- a/browser/devtools/netmonitor/test/browser_net_json-long.js
+++ b/browser/devtools/netmonitor/test/browser_net_json-long.js
@@ -6,17 +6,17 @@
  */
 
 function test() {
   initNetMonitor(JSON_LONG_URL).then(([aTab, aDebuggee, aMonitor]) => {
     info("Starting test... ");
 
     // This is receiving over 80 KB of json and will populate over 6000 items
     // in a variables view instance. Debug builds are slow.
-    requestLongerTimeout(3);
+    requestLongerTimeout(4);
 
     let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
     let { RequestsMenu } = NetMonitorView;
 
     RequestsMenu.lazyUpdate = false;
 
     waitForNetworkEvents(aMonitor, 1).then(() => {
       verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
@@ -24,26 +24,26 @@ function test() {
           status: 200,
           statusText: "OK",
           type: "json",
           fullMimeType: "text/json; charset=utf-8",
           size: L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(85975/1024, 2)),
           time: true
         });
 
+      aMonitor.panelWin.once("NetMonitor:ResponseBodyAvailable", () => {
+        testResponseTab();
+        teardown(aMonitor).then(finish);
+      });
+
       EventUtils.sendMouseEvent({ type: "mousedown" },
         document.getElementById("details-pane-toggle"));
       EventUtils.sendMouseEvent({ type: "mousedown" },
         document.querySelectorAll("#details-pane tab")[3]);
 
-      aMonitor.panelWin.once("NetMonitor:ResponseBodyAvailable", () => {
-        testResponseTab();
-        teardown(aMonitor).then(finish);
-      });
-
       function testResponseTab() {
         let tab = document.querySelectorAll("#details-pane tab")[3];
         let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
 
         is(tab.getAttribute("selected"), "true",
           "The response tab in the network details pane should be selected.");
 
         is(tabpanel.querySelector("#response-content-json-box")
--- a/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
+++ b/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
@@ -32,16 +32,19 @@
 #define REQUEST_WAIT_TIMEOUT 30
 // Pulled from desktop browser's shell
 #define APP_REG_NAME L"Firefox"
 
 static const WCHAR* kFirefoxExe = L"firefox.exe";
 static const WCHAR* kMetroFirefoxExe = L"firefox.exe";
 static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL";
 
+static bool GetDesktopBrowserPath(CStringW& aPathBuffer);
+static bool GetDefaultBrowserPath(CStringW& aPathBuffer);
+
 template <class T>void SafeRelease(T **ppT)
 {
   if (*ppT) {
     (*ppT)->Release();
     *ppT = NULL;
   }
 }
 
@@ -273,48 +276,59 @@ public:
       *aLaunchType = AHE_IMMERSIVE;
       mIsDesktopRequest = false;
     }
     return S_OK;
   }
 
   bool IsDefaultBrowser()
   {
-    bool result = false;
     IApplicationAssociationRegistration* pAAR;
     HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
                                   NULL,
                                   CLSCTX_INPROC,
                                   IID_IApplicationAssociationRegistration,
                                   (void**)&pAAR);
-    if (SUCCEEDED(hr)) {
-      BOOL res;
-      hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
-                                      APP_REG_NAME,
-                                      &res);
-      Log(L"QueryAppIsDefaultAll: %d", res);
-      if (!res) 
-        return false;
-      // Make sure the Prog ID matches what we have
-      LPWSTR registeredApp;
-      hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE,
-                                      &registeredApp);
-      Log(L"QueryCurrentDefault: %X", hr);
-      if (SUCCEEDED(hr)) {
-        Log(L"registeredApp=%s", registeredApp);
-        result = !wcsicmp(registeredApp, kDefaultMetroBrowserIDPathKey);
-        CoTaskMemFree(registeredApp);
-      } else {
-        result = false;
-      }
+    if (FAILED(hr))
+      return false;
 
+    BOOL res = FALSE;
+    hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
+                                    APP_REG_NAME,
+                                    &res);
+    Log(L"QueryAppIsDefaultAll: %d", res);
+    if (!res) {
       pAAR->Release();
-      return result;
+      return false;
     }
-    return result;
+    // Make sure the Prog ID matches what we have
+    LPWSTR registeredApp;
+    hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE,
+                                    &registeredApp);
+    pAAR->Release();
+    Log(L"QueryCurrentDefault: %X", hr);
+    if (FAILED(hr))
+      return false;
+
+    Log(L"registeredApp=%s", registeredApp);
+    bool result = !wcsicmp(registeredApp, kDefaultMetroBrowserIDPathKey);
+    CoTaskMemFree(registeredApp);
+    if (!result)
+      return false;
+
+    // If the registry points another browser's path,
+    // activating the Metro browser will fail. So fallback to the desktop.
+    CStringW selfPath;
+    GetDesktopBrowserPath(selfPath);
+    selfPath.MakeLower();
+    CStringW browserPath;
+    GetDefaultBrowserPath(browserPath);
+    browserPath.MakeLower();
+
+    return selfPath == browserPath;
   }
 private:
   ~CExecuteCommandVerb()
   {
     SafeRelease(&mShellItemArray);
     SafeRelease(&mUnkSite);
   }
 
@@ -371,16 +385,42 @@ static bool GetDesktopBrowserPath(CStrin
   // ceh.exe sits in dist/bin root with the desktop browser. Since this
   // is a firefox only component, this hardcoded filename is ok.
   aPathBuffer.Append(L"\\");
   aPathBuffer.Append(kFirefoxExe);
   return true;
 }
 
 /*
+ * Retrieve the current default browser's path.
+ *
+ * @aPathBuffer Buffer to fill
+ */
+static bool GetDefaultBrowserPath(CStringW& aPathBuffer)
+{
+  WCHAR buffer[MAX_PATH];
+  DWORD length = MAX_PATH;
+
+  if (FAILED(AssocQueryStringW(ASSOCF_NOTRUNCATE | ASSOCF_INIT_IGNOREUNKNOWN,
+                               ASSOCSTR_EXECUTABLE,
+                               kDefaultMetroBrowserIDPathKey, NULL,
+                               buffer, &length))) {
+    Log(L"AssocQueryString failed.");
+    return false;
+  }
+
+  // sanity check
+  if (lstrcmpiW(PathFindFileNameW(buffer), kFirefoxExe))
+    return false;
+
+  aPathBuffer = buffer;
+  return true;
+}
+
+/*
  * Retrieve the app model id of the firefox metro browser.
  *
  * @aPathBuffer Buffer to fill
  * @aCharLength Length of buffer to fill in characters
  */
 static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer,
                                         long aCharLength)
 {
@@ -535,16 +575,18 @@ void CExecuteCommandVerb::LaunchDesktopB
   if (!GetDesktopBrowserPath(browserPath)) {
     return;
   }
 
   // If a taskbar shortcut, link or local file is clicked, the target will
   // be the browser exe or file.
   CStringW params;
   if (!IsTargetBrowser()) {
+    // Fallback to the module path if it failed to get the default browser.
+    GetDefaultBrowserPath(browserPath);
     params += "-url ";
     params += "\"";
     params += mTarget;
     params += "\"";
   }
 
   Log(L"Desktop Launch: verb:%s exe:%s params:%s", mVerb, browserPath, params); 
 
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -751,17 +751,17 @@ user_pref("camino.use_system_proxy_setti
     env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
     env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
 
     # Additional temporary logging while we try to debug some intermittent
     # WebRTC conditions. This is necessary to troubleshoot bugs 841496,
     # 841150, and 839677 (at least)
     # Also (temporary) bug 870002 (mediastreamgraph)
     if 'NSPR_LOG_MODULES' not in env:
-      env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3,mediastreamgraph:4'
+      env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3'
     env['R_LOG_LEVEL'] = '5'
     env['R_LOG_DESTINATION'] = 'stderr'
     env['R_LOG_VERBOSE'] = '1'
 
     # ASan specific environment stuff
     if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC):
       try:
         totalMemory = int(os.popen("free").readlines()[1].split()[1])
--- a/build/mobile/sutagent/android/ASMozStub.java
+++ b/build/mobile/sutagent/android/ASMozStub.java
@@ -71,25 +71,27 @@ public class ASMozStub extends android.a
         super.onCreate();
 
         mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
         try {
             mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature);
             mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
             }
         catch (NoSuchMethodException e) {
-            // Running on an older platform.
+            // Might be running on an older platform.
             mStartForeground = mStopForeground = null;
+            Log.w("SUTAgent", "unable to find start/stopForeground method(s) -- older platform?");
             }
 
         try {
             mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature);
             }
         catch (NoSuchMethodException e) {
             mSetForeground = null;
+            Log.e("SUTAgent", "unable to find setForeground method!");
             }
 
         doToast("Listener Service created...");
         }
 
     WifiManager.MulticastLock multicastLock;
     JmDNS jmdns;
 
@@ -251,34 +253,37 @@ public class ASMozStub extends android.a
         // If we have the new startForeground API, then use it.
         if (mStartForeground != null) {
             mStartForegroundArgs[0] = Integer.valueOf(id);
             mStartForegroundArgs[1] = notification;
             try {
                 mStartForeground.invoke(this, mStartForegroundArgs);
             } catch (InvocationTargetException e) {
                 // Should not happen.
-                Log.w("ScreenOnWidget", "Unable to invoke startForeground", e);
+                Log.e("SUTAgent", "Unable to invoke startForeground", e);
             } catch (IllegalAccessException e) {
                 // Should not happen.
-                Log.w("ScreenOnWidget", "Unable to invoke startForeground", e);
+                Log.e("SUTAgent", "Unable to invoke startForeground", e);
             }
             return;
         }
 
         // Fall back on the old API.
         if  (mSetForeground != null) {
             try {
                 mSetForegroundArgs[0] = Boolean.TRUE;
                 mSetForeground.invoke(this, mSetForegroundArgs);
             } catch (IllegalArgumentException e) {
+                Log.e("SUTAgent", "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (IllegalAccessException e) {
+                Log.e("SUTAgent", "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (InvocationTargetException e) {
+                Log.e("SUTAgent", "Unable to invoke setForeground", e);
                 e.printStackTrace();
             }
         }
         mNM.notify(id, notification);
     }
 
     /**
      * This is a wrapper around the new stopForeground method, using the older
@@ -287,33 +292,36 @@ public class ASMozStub extends android.a
     void stopForegroundCompat(int id) {
         // If we have the new stopForeground API, then use it.
         if (mStopForeground != null) {
             mStopForegroundArgs[0] = Boolean.TRUE;
             try {
                 mStopForeground.invoke(this, mStopForegroundArgs);
             } catch (InvocationTargetException e) {
                 // Should not happen.
-                Log.w("ScreenOnWidget", "Unable to invoke stopForeground", e);
+                Log.e("SUTAgent", "Unable to invoke stopForeground", e);
             } catch (IllegalAccessException e) {
                 // Should not happen.
-                Log.w("ScreenOnWidget", "Unable to invoke stopForeground", e);
+                Log.e("SUTAgent", "Unable to invoke stopForeground", e);
             }
             return;
         }
 
         // Fall back on the old API.  Note to cancel BEFORE changing the
         // foreground state, since we could be killed at that point.
         mNM.cancel(id);
         if  (mSetForeground != null) {
             try {
                 mSetForegroundArgs[0] = Boolean.FALSE;
                 mSetForeground.invoke(this, mSetForegroundArgs);
             } catch (IllegalArgumentException e) {
+                Log.e("SUTAgent", "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (IllegalAccessException e) {
+                Log.e("SUTAgent", "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (InvocationTargetException e) {
+                Log.e("SUTAgent", "Unable to invoke setForeground", e);
                 e.printStackTrace();
             }
         }
     }
 }
--- a/build/mobile/sutagent/android/DoCommand.java
+++ b/build/mobile/sutagent/android/DoCommand.java
@@ -102,33 +102,34 @@ public class DoCommand {
 
     String    currentDir = "/";
     String    sErrorPrefix = "##AGENT-WARNING## ";
     boolean bTraceOn = false;
 
     String ffxProvider = "org.mozilla.ffxcp";
     String fenProvider = "org.mozilla.fencp";
 
-    private final String prgVersion = "SUTAgentAndroid Version 1.17";
+    private final String prgVersion = "SUTAgentAndroid Version 1.18";
 
     public enum Command
         {
         RUN ("run"),
         EXEC ("exec"),
         EXECSU ("execsu"),
         EXECCWD ("execcwd"),
         EXECCWDSU ("execcwdsu"),
         ENVRUN ("envrun"),
         KILL ("kill"),
         PS ("ps"),
         DEVINFO ("info"),
         OS ("os"),
         ID ("id"),
         UPTIME ("uptime"),
         UPTIMEMILLIS ("uptimemillis"),
+        SUTUPTIMEMILLIS ("sutuptimemillis"),
         SETTIME ("settime"),
         SYSTIME ("systime"),
         SCREEN ("screen"),
         ROTATION ("rotation"),
         MEMORY ("memory"),
         POWER ("power"),
         PROCESS ("process"),
         SUTUSERINFO ("sutuserinfo"),
@@ -424,16 +425,18 @@ public class DoCommand {
                     strReturn += GetOSInfo();
                     strReturn += "\n";
                     strReturn += GetSystemTime();
                     strReturn += "\n";
                     strReturn += GetUptime();
                     strReturn += "\n";
                     strReturn += GetUptimeMillis();
                     strReturn += "\n";
+                    strReturn += GetSutUptimeMillis();
+                    strReturn += "\n";
                     strReturn += GetScreenInfo();
                     strReturn += "\n";
                     strReturn += GetRotationInfo();
                     strReturn += "\n";
                     strReturn += GetMemoryInfo();
                     strReturn += "\n";
                     strReturn += GetPowerInfo();
                     strReturn += "\n";
@@ -481,16 +484,20 @@ public class DoCommand {
                         case UPTIME:
                             strReturn = GetUptime();
                             break;
 
                         case UPTIMEMILLIS:
                             strReturn = GetUptimeMillis();
                             break;
 
+                        case SUTUPTIMEMILLIS:
+                            strReturn = GetSutUptimeMillis();
+                            break;
+
                         case MEMORY:
                             strReturn = GetMemoryInfo();
                             break;
 
                         case POWER:
                             strReturn += GetPowerInfo();
                             break;
 
@@ -3061,16 +3068,22 @@ private void CancelNotification()
         return (sRet);
         }
 
     public String GetUptimeMillis()
         {
         return Long.toString(SystemClock.uptimeMillis());
         }
  
+    public String GetSutUptimeMillis()
+        {
+        long now = System.currentTimeMillis();
+        return "SUTagent running for "+Long.toString(now - SUTAgentAndroid.nCreateTimeMillis)+" ms";
+        }
+ 
     public String NewKillProc(String sProcId, OutputStream out)
         {
         String sRet = "";
 
         try
             {
             pProc = Runtime.getRuntime().exec("kill "+sProcId);
             RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
@@ -3884,16 +3897,17 @@ private void CancelNotification()
             "kill [program name]             - kill program no path\n" +
             "killall                         - kill all processes started\n" +
             "ps                              - list of running processes\n" +
             "info                            - list of device info\n" +
             "        [os]                    - os version for device\n" +
             "        [id]                    - unique identifier for device\n" +
             "        [uptime]                - uptime for device\n" +
             "        [uptimemillis]          - uptime for device in milliseconds\n" +
+            "        [sutuptimemillis]       - uptime for SUT in milliseconds\n" +
             "        [systime]               - current system time\n" +
             "        [screen]                - width, height and bits per pixel for device\n" +
             "        [memory]                - physical, free, available, storage memory\n" +
             "                                  for device\n" +
             "        [processes]             - list of running processes see 'ps'\n" +
             "deadman timeout                 - set the duration for the deadman timer\n" +
             "alrt [on/off]                   - start or stop sysalert behavior\n" +
             "disk [arg]                      - prints disk space info\n" +
--- a/build/mobile/sutagent/android/SUTAgentAndroid.java
+++ b/build/mobile/sutagent/android/SUTAgentAndroid.java
@@ -61,16 +61,17 @@ public class SUTAgentAndroid extends Act
     Timer timer = null;
 
     public static String sUniqueID = null;
     public static String sLocalIPAddr = null;
     public static String sACStatus = null;
     public static String sPowerStatus = null;
     public static int    nChargeLevel = 0;
     public static int    nBatteryTemp = 0;
+    public static long   nCreateTimeMillis = System.currentTimeMillis();
 
     String lineSep = System.getProperty("line.separator");
     public PrintWriter dataOut = null;
 
     private static boolean bNetworkingStarted = false;
     private static String RegSvrIPAddr = "";
     private static String RegSvrIPPort = "";
     private static String HardwareID = "";
@@ -390,24 +391,22 @@ public class SUTAgentAndroid extends Act
             System.exit(0);
             }
         else
             {
             log(dc, "onDestroy - not finishing");
             }
         }
 
-    @Override
-    public void onLowMemory()
+    private void logMemory(String caller)
         {
-        System.gc();
         DoCommand dc = new DoCommand(getApplication());
         if (dc != null)
             {
-            log(dc, "onLowMemory");
+            log(dc, caller);
             log(dc, dc.GetMemoryInfo());
             String procInfo = dc.GetProcessInfo();
             if (procInfo != null)
                 {
                 String lines[] = procInfo.split("\n");
                 for (String line : lines) 
                     {
                     if (line.contains("mozilla"))
@@ -419,20 +418,34 @@ public class SUTAgentAndroid extends Act
                             log(dc, dc.StatProcess(words[1]));
                             }
                         }
                     }
                 }
             }
         else
             {
-            Log.e("SUTAgentAndroid", "onLowMemory: unable to log to file!");
+            Log.e("SUTAgentAndroid", "logMemory: unable to log to file!");
             }
         }
 
+    @Override
+    public void onLowMemory()
+        {
+        System.gc();
+        logMemory("onLowMemory");
+        }
+
+    @Override
+    public void onTrimMemory(int level)
+        {
+        System.gc();
+        logMemory("onTrimMemory"+level);
+        }
+
     private void monitorBatteryState()
         {
         battReceiver = new BroadcastReceiver()
             {
             public void onReceive(Context context, Intent intent)
                 {
                 StringBuilder sb = new StringBuilder();
 
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -44,42 +44,43 @@ import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Gravity;
 import android.widget.Toast;
 import android.os.Environment;
 
 public class WatcherService extends Service
 {
-    private final String prgVersion = "Watcher Version 1.15";
+    private final String prgVersion = "Watcher Version 1.16";
+    private final String LOGTAG = "Watcher";
 
     String sErrorPrefix = "##Installer Error## ";
     String currentDir = "/";
     String sPingTarget = "";
     long lDelay = 60000;
     long lPeriod = 300000;
     int nMaxStrikes = 0; // maximum number of tries before we consider network unreachable (0 means don't check)
     boolean bStartSUTAgent = true;
     boolean bStartedTimer = false;
 
-    Process    pProc;
+    Process pProc;
     Context myContext = null;
     Timer myTimer = null;
     private PowerManager.WakeLock pwl = null;
     public static final int NOTIFICATION_ID = 1964;
     boolean bInstalling = false;
 
     @SuppressWarnings("unchecked")
     private static final Class<?>[] mSetForegroundSignature = new Class[] {
-    boolean.class};
+        boolean.class};
     @SuppressWarnings("unchecked")
-    private static final Class[] mStartForegroundSignature = new Class[] {
+    private static final Class<?>[] mStartForegroundSignature = new Class[] {
         int.class, Notification.class};
     @SuppressWarnings("unchecked")
-    private static final Class[] mStopForegroundSignature = new Class[] {
+    private static final Class<?>[] mStopForegroundSignature = new Class[] {
         boolean.class};
 
     private NotificationManager mNM;
     private Method mSetForeground;
     private Method mStartForeground;
     private Method mStopForeground;
     private Object[] mSetForegroundArgs = new Object[1];
     private Object[] mStartForegroundArgs = new Object[2];
@@ -98,35 +99,37 @@ public class WatcherService extends Serv
         return stub;
     }
 
     @Override
     public void onCreate()
         {
         super.onCreate();
 
+        Log.i(LOGTAG, prgVersion);
+
         myContext = this;
 
         getKeyGuardAndWakeLock();
 
         File dir = getFilesDir();
         File iniFile = new File(dir, "watcher.ini");
         String sIniFile = iniFile.getAbsolutePath();
         String sHold = "";
 
-        Log.i("Watcher", String.format("Loading settings from %s", sIniFile));
+        Log.i(LOGTAG, String.format("Loading settings from %s", sIniFile));
         this.sPingTarget = GetIniData("watcher", "PingTarget", sIniFile, "www.mozilla.org");
         sHold = GetIniData("watcher", "delay", sIniFile, "60000");
         this.lDelay = Long.parseLong(sHold.trim());
         sHold = GetIniData("watcher", "period", sIniFile,"300000");
         this.lPeriod = Long.parseLong(sHold.trim());
         sHold = GetIniData("watcher", "strikes", sIniFile,"0");
         this.nMaxStrikes = Integer.parseInt(sHold.trim());
-        Log.i("Watcher", String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)",
-                                       this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes));
+        Log.i(LOGTAG, String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)",
+             this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes));
 
         sHold = GetIniData("watcher", "StartSUTAgent", sIniFile, "true");
         this.bStartSUTAgent = Boolean.parseBoolean(sHold.trim());
 
         sHold = GetIniData("watcher", "stayon", sIniFile,"0");
         int nStayOn = Integer.parseInt(sHold.trim());
         
         try {
@@ -146,17 +149,17 @@ public class WatcherService extends Serv
 
     public String GetIniData(String sSection, String sKey, String sFile, String sDefault)
         {
         String sRet = sDefault;
         String sComp = "";
         String sLine = "";
         boolean bFound = false;
         BufferedReader in = null;
-        String    sTmpFileName = fixFileName(sFile);
+        String sTmpFileName = fixFileName(sFile);
 
         try {
             in = new BufferedReader(new FileReader(sTmpFileName));
             sComp = "[" + sSection + "]";
             while ((sLine = in.readLine()) != null)
                 {
                 if (sLine.equalsIgnoreCase(sComp))
                     {
@@ -212,117 +215,151 @@ public class WatcherService extends Serv
             {
             if (sCmd.equalsIgnoreCase("updt"))
                 {
                 String sPkgName = intent.getStringExtra("pkgName");
                 String sPkgFile = intent.getStringExtra("pkgFile");
                 String sOutFile = intent.getStringExtra("outFile");
                 boolean bReboot = intent.getBooleanExtra("reboot", true);
                 int nReboot = bReboot ? 1 : 0;
-                   SendNotification("WatcherService updating " + sPkgName + " using file " + sPkgFile, "WatcherService updating " + sPkgName + " using file " + sPkgFile);
+                Log.i(LOGTAG, "WatcherService updating " + sPkgName + " using file " + sPkgFile);
 
-                   UpdateApplication worker = new UpdateApplication(sPkgName, sPkgFile, sOutFile, nReboot);
+                UpdateApplication worker = new UpdateApplication(sPkgName, sPkgFile, sOutFile, nReboot);
                 }
             else if (sCmd.equalsIgnoreCase("start"))
                 {
-                if (!this.bStartedTimer) {
+                if (!this.bStartedTimer) 
+                    {
                     doToast("WatcherService started");
                     myTimer = new Timer();
                     Date startSchedule = new Date(System.currentTimeMillis() + lDelay);
                     myTimer.schedule(new MyTime(), startSchedule, lPeriod);
                     this.bStartedTimer = true;
-                }
+                    }
                 }
             else
                 {
                 doToast("WatcherService unknown command");
                 }
             }
         else
             doToast("WatcherService created");
         }
 
     public void writeVersion() {
         PrintWriter pw = null;
         String appPath = getApplicationContext().getFilesDir().getAbsolutePath();
         String versionPath = appPath + "/version.txt";
-        Log.i("Watcher", "writing version string to: " + versionPath);
+        Log.i(LOGTAG, "writing version string to: " + versionPath);
         try {
             pw = new PrintWriter(new FileWriter(versionPath, true));
             pw.println(this.prgVersion);
         } catch (IOException ioe) {
-            Log.e("Watcher", "Exception writing version: " + this.prgVersion + " to file: " + versionPath);
+            Log.e(LOGTAG, "Exception writing version: " + this.prgVersion + " to file: " + versionPath);
         } finally {
             if (pw != null) {
                 pw.close();
             }
         }
     }
 
     @Override
     public void onStart(Intent intent, int startId) {
+        Log.i(LOGTAG, "onStart");
         writeVersion();
         handleCommand(intent);
         return;
     }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i(LOGTAG, "onStartCommand");
         writeVersion();
         handleCommand(intent);
         return START_STICKY;
-        }
+    }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         doToast("WatcherService destroyed");
         if (pwl != null)
             pwl.release();
         stopForegroundCompat(R.string.foreground_service_started);
     }
 
+    @Override
+    public void onLowMemory() {
+        Log.e(LOGTAG, "onLowMemory");
+        System.gc();
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        Log.e(LOGTAG, "onTrimMemory: "+level);
+        System.gc();
+    }
+
     protected void getKeyGuardAndWakeLock()
         {
         // Fire off a thread to do some work that we shouldn't do directly in the UI thread
         Thread t = new Thread() {
             public void run() {
+                Log.i(LOGTAG, "worker thread started");
                 // Keep phone from locking or remove lock on screen
                 KeyguardManager km = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
                 if (km != null)
                     {
                     KeyguardManager.KeyguardLock kl = km.newKeyguardLock("watcher");
                     if (kl != null)
+                        {
                         kl.disableKeyguard();
+                        Log.i(LOGTAG, "keyguard disabled");
+                        }
                     }
 
                 // No sleeping on the job
                 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
                 if (pm != null)
                     {
                     pwl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "watcher");
                     if (pwl != null)
+                        {
                         pwl.acquire();
+                        Log.i(LOGTAG, "wake lock acquired");
+                        }
                     }
 
+                Class<?> serviceClass = null;
+                try {
+                    serviceClass = Class.forName("com.mozilla.watcher.WatcherService");
+                    }
+                catch (Exception e)
+                    {
+                    Log.e(LOGTAG, "unable to find service class: "+e.toString());
+                    return;
+                    }
                 mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                 try {
-                    mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature);
-                    mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
+                    mStartForeground = serviceClass.getMethod("startForeground", mStartForegroundSignature);
+                    mStopForeground = serviceClass.getMethod("stopForeground", mStopForegroundSignature);
                     }
                 catch (NoSuchMethodException e)
                     {
-                    // Running on an older platform.
+                    // Might be running on an older platform.
                     mStartForeground = mStopForeground = null;
+                    Log.w(LOGTAG, "unable to find start/stopForeground method(s) -- older platform?");
                     }
                 try {
-                    mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature);
+                    mSetForeground = serviceClass.getMethod("setForeground", mSetForegroundSignature);
                     }
-                catch (NoSuchMethodException e) {
+                catch (NoSuchMethodException e) 
+                    {
                     mSetForeground = null;
+                    Log.e(LOGTAG, "unable to find setForeground method!");
                     }
                 Notification notification = new Notification();
                 startForegroundCompat(R.string.foreground_service_started, notification);
                 }
             };
         t.start();
         }
 
@@ -332,160 +369,169 @@ public class WatcherService extends Serv
      */
     void startForegroundCompat(int id, Notification notification) {
         // If we have the new startForeground API, then use it.
         if (mStartForeground != null) {
             mStartForegroundArgs[0] = Integer.valueOf(id);
             mStartForegroundArgs[1] = notification;
             try {
                 mStartForeground.invoke(this, mStartForegroundArgs);
+                Log.i(LOGTAG, "startForeground invoked");
             } catch (InvocationTargetException e) {
                 // Should not happen.
-                Log.w("ApiDemos", "Unable to invoke startForeground", e);
+                Log.e(LOGTAG, "Unable to invoke startForeground", e);
             } catch (IllegalAccessException e) {
                 // Should not happen.
-                Log.w("ApiDemos", "Unable to invoke startForeground", e);
+                Log.e(LOGTAG, "Unable to invoke startForeground", e);
             }
             return;
         }
 
         // Fall back on the old API.
         if  (mSetForeground != null) {
             try {
                 mSetForegroundArgs[0] = Boolean.TRUE;
                 mSetForeground.invoke(this, mSetForegroundArgs);
+                Log.i(LOGTAG, "setForeground(TRUE) invoked");
             } catch (IllegalArgumentException e) {
+                Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (IllegalAccessException e) {
+                Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (InvocationTargetException e) {
+                Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             }
         }
         mNM.notify(id, notification);
     }
 
     /**
      * This is a wrapper around the new stopForeground method, using the older
      * APIs if it is not available.
      */
     void stopForegroundCompat(int id) {
         // If we have the new stopForeground API, then use it.
         if (mStopForeground != null) {
             mStopForegroundArgs[0] = Boolean.TRUE;
             try {
                 mStopForeground.invoke(this, mStopForegroundArgs);
+                Log.i(LOGTAG, "stopForeground invoked");
             } catch (InvocationTargetException e) {
                 // Should not happen.
-                Log.w("ApiDemos", "Unable to invoke stopForeground", e);
+                Log.e(LOGTAG, "Unable to invoke stopForeground", e);
             } catch (IllegalAccessException e) {
                 // Should not happen.
-                Log.w("ApiDemos", "Unable to invoke stopForeground", e);
+                Log.e(LOGTAG, "Unable to invoke stopForeground", e);
             }
             return;
         }
 
         // Fall back on the old API.  Note to cancel BEFORE changing the
         // foreground state, since we could be killed at that point.
         mNM.cancel(id);
         if  (mSetForeground != null) {
             try {
                 mSetForegroundArgs[0] = Boolean.FALSE;
                 mSetForeground.invoke(this, mSetForegroundArgs);
+                Log.i(LOGTAG, "setForeground(FALSE) invoked");
             } catch (IllegalArgumentException e) {
+                Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (IllegalAccessException e) {
+                Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             } catch (InvocationTargetException e) {
+                Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             }
         }
     }
 
     public void doToast(String sMsg)
         {
-        Log.i("Watcher", sMsg);
+        Log.i(LOGTAG, sMsg);
         Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
         toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
         toast.show();
         }
 
-    public void CheckMem() {
-           System.gc();
+    public void CheckMem() 
+        {
+        System.gc();
         long lFreeMemory = Runtime.getRuntime().freeMemory();
         long lTotMemory = Runtime.getRuntime().totalMemory();
         long lMaxMemory = Runtime.getRuntime().maxMemory();
 
-        SendNotification("Memory Check", "Free: " + lFreeMemory + "Total: " + lTotMemory + "Max: " + lMaxMemory);
-    }
+        Log.i(LOGTAG, "Free: " + lFreeMemory + "Total: " + lTotMemory + "Max: " + lMaxMemory);
+        }
 
     public int UpdtApp(String sPkgName, String sPkgFileName, String sOutFile, int bReboot)
         {
         int nRet = 1;
         int lcv = 0;
         String sRet = "";
 
-//        Debug.waitForDebugger();
-
         FileOutputStream f = null;
 
-           try {
-               SendNotification("Killing " + sPkgName, "Step 1: Kill " + sPkgName + " if running");
+        try {
+            Log.i(LOGTAG, "Step 1: Kill " + sPkgName + " if running");
             while (!IsProcessDead(sPkgName) && (lcv < 5)) {
                 if (KillProcess(sPkgName, null).startsWith("Successfully"))
                     break;
                 else
                     lcv++;
-                   Thread.sleep(2000);
-                   }
+                Thread.sleep(2000);
+            }
 
-               CheckMem();
+            CheckMem();
 
             if ((sOutFile != null) && (sOutFile.length() > 0)) {
                 File outFile = new File(sOutFile);
                 if (outFile.exists() && outFile.canWrite()) {
                     f = new FileOutputStream(outFile, true);
                 } else {
-                       SendNotification("File not found or cannot write to " + sOutFile, "File not found or cannot write to " + sOutFile);
+                    Log.e(LOGTAG, "File not found or cannot write to " + sOutFile);
                 }
             }
         } catch (InterruptedException e) {
-               e.printStackTrace();
+            e.printStackTrace();
         } catch (FileNotFoundException e) {
-               SendNotification("File not found " + sOutFile, "Couldn't open " + sOutFile + " " + e.getLocalizedMessage());
+            Log.e(LOGTAG, "Couldn't open " + sOutFile + " " + e.getLocalizedMessage());
             e.printStackTrace();
-           } catch (SecurityException e) {
-               SendNotification("Security excepetion for " + sOutFile, "Exception message " + e.getLocalizedMessage());
+        } catch (SecurityException e) {
+            Log.e(LOGTAG, "Exception message " + e.getLocalizedMessage());
             e.printStackTrace();
-           }
+        }
 
         if ((sPkgName != null) && (sPkgName.length() > 0))
             {
-               SendNotification("Uninstalling " + sPkgName, "Step 2: Uninstall " + sPkgName);
+            Log.i(LOGTAG, "Step 2: Uninstall " + sPkgName);
             sRet = UnInstallApp(sPkgName, null);
-               CheckMem();
+            CheckMem();
             if ((sRet.length() > 0) && (f != null))
                 {
                 try {
                     f.write(sRet.getBytes());
                     f.flush();
                     }
                 catch (IOException e)
                     {
                     e.printStackTrace();
                     }
                 }
             }
 
         if ((sPkgFileName != null) && (sPkgFileName.length() > 0))
             {
-               SendNotification("Installing " + sPkgFileName, "Step 3: Install " + sPkgFileName);
+            Log.i(LOGTAG, "Step 3: Install " + sPkgFileName);
             sRet = InstallApp(sPkgFileName, null);
-               SendNotification("Installed " + sPkgFileName, "" + sRet);
-               CheckMem();
+            Log.i(LOGTAG, "" + sRet);
+            CheckMem();
             if ((sRet.length() > 0) && (f != null))
                 {
                 try {
                     f.write(sRet.getBytes());
                     f.flush();
                     f.close();
                     }
                 catch (IOException e)
@@ -528,17 +574,17 @@ public class WatcherService extends Serv
     public String RunReboot(OutputStream out)
         {
         String sRet = "";
         String [] theArgs = new String [3];
 
         theArgs[0] = "su";
         theArgs[1] = "-c";
         theArgs[2] = "reboot";
-        Log.i("Watcher", "Running reboot!");
+        Log.i(LOGTAG, "Running reboot!");
 
         try
             {
             pProc = Runtime.getRuntime().exec(theArgs);
             RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
             outThrd.start();
             outThrd.join(10000);
             }
@@ -563,17 +609,17 @@ public class WatcherService extends Serv
         theArgs[1] = "-c";
         theArgs[2] = "kill";
 
         String sRet = sErrorPrefix + "Unable to kill " + sProcName + "\n";
         ActivityManager aMgr = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE);
         List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses();
         int lcv = 0;
         String strProcName = "";
-        int    nPID = 0;
+        int nPID = 0;
         int nProcs = 0;
 
         if (lProcesses != null)
             nProcs = lProcesses.size();
 
         for (lcv = 0; lcv < nProcs; lcv++)
             {
             if (lProcesses.get(lcv).processName.contains(sProcName))
@@ -693,32 +739,16 @@ public class WatcherService extends Serv
 
         sRet = sTmpFileName.replace('\\', '/');
         sTmpFileName = sRet;
         sRet = sTmpFileName.replace("//", "/");
 
         return(sRet);
         }
 
-    public String GetTmpDir()
-        {
-        String     sRet = "";
-        Context ctx = getApplicationContext();
-        File dir = ctx.getFilesDir();
-        ctx = null;
-        try {
-            sRet = dir.getCanonicalPath();
-            }
-        catch (IOException e)
-            {
-            e.printStackTrace();
-            }
-        return(sRet);
-        }
-
     public String UnInstallApp(String sApp, OutputStream out)
         {
         String sRet = "";
         try
             {
             pProc = Runtime.getRuntime().exec(this.getSuArgs("pm uninstall " + sApp + ";exit"));
 
             RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
@@ -777,30 +807,30 @@ public class WatcherService extends Serv
             e.printStackTrace();
             }
 
         return (sRet);
         }
 
     private String SendPing(String sIPAddr)
         {
-        Process    pProc;
+        Process pProc;
         String sRet = "";
         String [] theArgs = new String [4];
         boolean bStillRunning = true;
-        int    nBytesOut = 0;
+        int nBytesOut = 0;
         int nBytesErr = 0;
         int nBytesRead = 0;
         byte[] buffer = new byte[1024];
 
         theArgs[0] = "ping";
         theArgs[1] = "-c";
         theArgs[2] = "3";
         theArgs[3] = sIPAddr;
-        Log.i("Watcher", "Pinging " + sIPAddr);
+        Log.i(LOGTAG, "Pinging " + sIPAddr);
 
         try
             {
             pProc = Runtime.getRuntime().exec(theArgs);
             InputStream sutOut = pProc.getInputStream();
             InputStream sutErr = pProc.getErrorStream();
 
             while (bStillRunning)
@@ -867,17 +897,17 @@ public class WatcherService extends Serv
             pProc = null;
             }
         catch (IOException e)
             {
             sRet = e.getMessage();
             e.printStackTrace();
             }
 
-        Log.i("Watcher", String.format("Ping result was: '%s'", sRet.trim()));
+        Log.i(LOGTAG, String.format("Ping result was: '%s'", sRet.trim()));
         return (sRet);
         }
 
     private boolean IsProcRunning(Process pProc)
         {
         boolean bRet = false;
         @SuppressWarnings("unused")
         int nExitCode = 0;
@@ -898,31 +928,31 @@ public class WatcherService extends Serv
         return(bRet);
         }
 
     private class UpdateApplication implements Runnable {
         Thread    runner;
         String    msPkgName = "";
         String    msPkgFileName = "";
         String    msOutFile = "";
-        int        mbReboot = 0;
+        int       mbReboot = 0;
 
         public UpdateApplication(String sPkgName, String sPkgFileName, String sOutFile, int bReboot) {
             runner = new Thread(this);
             msPkgName = sPkgName;
             msPkgFileName = sPkgFileName;
             msOutFile = sOutFile;
             mbReboot = bReboot;
             runner.start();
         }
 
         public void run() {
-               bInstalling = true;
+            bInstalling = true;
             UpdtApp(msPkgName, msPkgFileName, msOutFile, mbReboot);
-               bInstalling = false;
+            bInstalling = false;
         }
     }
 
     private class MyTime extends TimerTask
         {
         int    nStrikes = 0;
         final int PERIODS_TO_WAIT_FOR_SDCARD = 3;
         int    nPeriodsWaited = 0;
@@ -939,52 +969,50 @@ public class WatcherService extends Serv
 
             // See if the network is up, if not reboot after a configurable
             // number of tries
             if (nMaxStrikes > 0)
                 {
                     String sRet = SendPing(sPingTarget);
                     if (!sRet.contains("3 received"))
                         {
-                            Log.i("Watcher", String.format("Failed ping attempt (remaining: %s)!",
-                                                           nMaxStrikes - nStrikes));
+                            Log.i(LOGTAG, String.format("Failed ping attempt (remaining: %s)!",
+                                 nMaxStrikes - nStrikes));
                             if (++nStrikes >= nMaxStrikes)
                                 {
-                                    Log.e("Watcher", String.format("Number of failed ping attempts to %s (%s) exceeded maximum (%s), running reboot!", sPingTarget, nStrikes, nMaxStrikes));
+                                    Log.e(LOGTAG, String.format("Number of failed ping attempts to %s (%s) exceeded maximum (%s), running reboot!", sPingTarget, nStrikes, nMaxStrikes));
                                     RunReboot(null);
                                 }
                         }
                     else
                         {
                             nStrikes = 0;
                         }
                 }
 
             String sProgramName = "com.mozilla.SUTAgentAndroid";
 
-//            Debug.waitForDebugger();
-
             // Ensure the sdcard is mounted before we even attempt to start the agent
             // We will wait for the sdcard to mount for PERIODS_TO_WAIT_FOR_SDCARD
             // after which time we go ahead and attempt to start the agent.
             if (nPeriodsWaited++ < PERIODS_TO_WAIT_FOR_SDCARD) {
                 String state = Environment.getExternalStorageState();
                 if (Environment.MEDIA_MOUNTED.compareTo(state) != 0) {
-                    Log.i("SUTAgentWatcher", "SDcard not mounted, waiting another turn");
+                    Log.i(LOGTAG, "SDcard not mounted, waiting another turn");
                     return;
                 } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
-                    Log.e("SUTAgentWatcher", "SDcard mounted read only not starting agent now, try again in 60s");
+                    Log.e(LOGTAG, "SDcard mounted read only not starting agent now, try again in 60s");
                     return;
                 }
             }
 
             boolean isProc = GetProcessInfo(sProgramName);
             if (bStartSUTAgent && !isProc)
                 {
-                Log.i("SUTAgentWatcher", "Starting SUTAgent from watcher code");
+                Log.i(LOGTAG, "Starting SUTAgent from watcher code");
                 Intent agentIntent = new Intent();
                 agentIntent.setPackage(sProgramName);
                 agentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 agentIntent.setAction(Intent.ACTION_MAIN);
                 try {
                     PackageManager pm = myContext.getPackageManager();
                     PackageInfo pi = pm.getPackageInfo(sProgramName, PackageManager.GET_ACTIVITIES | PackageManager.GET_INTENT_FILTERS);
                     ActivityInfo [] ai = pi.activities;
@@ -1008,19 +1036,9 @@ public class WatcherService extends Serv
                     }
                 catch(ActivityNotFoundException anf)
                     {
                     anf.printStackTrace();
                     }
                 }
             }
         }
-
-    private void SendNotification(String tickerText, String expandedText) {
-        Log.i("Watcher", expandedText);
-    }
-
-    private void CancelNotification() {
-        NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
-        notificationManager.cancel(NOTIFICATION_ID);
-    }
-
 }
--- a/build/unix/mozconfig.linux32
+++ b/build/unix/mozconfig.linux32
@@ -1,9 +1,10 @@
 . "$topsrcdir/build/unix/mozconfig.linux"
 
 if test `uname -m` = "x86_64"; then
-  CC="$CC -m32"
-  CXX="$CXX -m32"
+  # -march=pentiumpro is what our 32-bit native toolchain defaults to
+  CC="$CC -m32 -march=pentiumpro"
+  CXX="$CXX -m32 -march=pentiumpro"
   ac_add_options --target=i686-pc-linux
   ac_add_options --x-libraries=/usr/lib
   export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
 fi
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2495,50 +2495,52 @@ nsDocument::InitCSP(nsIChannel* aChannel
     if (!specCompliantEnabled) {
       PR_LOG(gCspPRLog, PR_LOG_DEBUG,
              ("Got spec compliant CSP headers but pref was not set"));
       cspHeaderValue.Truncate();
       cspROHeaderValue.Truncate();
     }
   }
 
-  // ----- Figure out if we need to apply an app default CSP
+  // Figure out if we need to apply an app default CSP or a CSP from an app manifest
   bool applyAppDefaultCSP = false;
+  bool applyAppManifestCSP = false;
+
   nsIPrincipal* principal = NodePrincipal();
+
+  bool unknownAppId;
   uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
-  bool unknownAppId;
+  nsAutoString appManifestCSP;
   if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
       !unknownAppId &&
       NS_SUCCEEDED(principal->GetAppStatus(&appStatus))) {
     applyAppDefaultCSP = ( appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
                            appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
 
-    // Bug 773981. Allow a per-app policy from the manifest.
-    // Just read the CSP from the manifest into cspHeaderValue.
-    // That way we don't have to change the rest of the function logic
-    if (applyAppDefaultCSP || appStatus == nsIPrincipal::APP_STATUS_INSTALLED) {
-      nsCOMPtr<nsIAppsService> appsService =
-        do_GetService(APPS_SERVICE_CONTRACTID);
-
-      if (appsService)  {
-        uint32_t appId;
-
-        if ( NS_SUCCEEDED(principal->GetAppId(&appId)) ) {
-          appsService->GetCSPByLocalId(appId, cspHeaderValue);
+    if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
+      nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+      if (appsService) {
+        uint32_t appId = 0;
+        if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
+          appsService->GetCSPByLocalId(appId, appManifestCSP);
+          if (!appManifestCSP.IsEmpty()) {
+            applyAppManifestCSP = true;
+          }
         }
       }
     }
   }
 #ifdef PR_LOGGING
   else
     PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to get app status from principal"));
 #endif
 
   // If there's no CSP to apply, go ahead and return early
   if (!applyAppDefaultCSP &&
+      !applyAppManifestCSP &&
       cspHeaderValue.IsEmpty() &&
       cspROHeaderValue.IsEmpty() &&
       cspOldHeaderValue.IsEmpty() &&
       cspOldROHeaderValue.IsEmpty()) {
 #ifdef PR_LOGGING
     nsCOMPtr<nsIURI> chanURI;
     aChannel->GetURI(getter_AddRefs(chanURI));
     nsAutoCString aspec;
@@ -2567,32 +2569,43 @@ nsDocument::InitCSP(nsIChannel* aChannel
 
   // used as a "self" identifier for the CSP.
   nsCOMPtr<nsIURI> chanURI;
   aChannel->GetURI(getter_AddRefs(chanURI));
 
   // Store the request context for violation reports
   csp->ScanRequestData(httpChannel);
 
-  // ----- process the app default policy, if necessary
+  // The CSP is refined in the following order:
+  // 1. Default app CSP, if applicable
+  // 2. App manifest CSP, if provided
+  // 3. HTTP header CSP, if provided
+  // Note that since each application of refinePolicy is a set intersection,
+  // the order in which multiple CSP's are refined does not matter.
+
   if (applyAppDefaultCSP) {
     nsAdoptingString appCSP;
     if (appStatus ==  nsIPrincipal::APP_STATUS_PRIVILEGED) {
       appCSP = Preferences::GetString("security.apps.privileged.CSP.default");
       NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default");
     } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
       appCSP = Preferences::GetString("security.apps.certified.CSP.default");
       NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
     }
 
     if (appCSP)
       // Use the 1.0 CSP parser for apps if the pref to do so is set.
       csp->RefinePolicy(appCSP, chanURI, specCompliantEnabled);
   }
 
+  if (applyAppManifestCSP) {
+    // Use the 1.0 CSP parser for apps if the pref to do so is set.
+    csp->RefinePolicy(appManifestCSP, chanURI, specCompliantEnabled);
+  }
+
   // While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
   // take priority.  If any spec-compliant headers are present, the x- headers
   // are ignored, and the spec compliant parser is used.
   bool cspSpecCompliant = (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty());
 
   // If the old header is present, warn that it will be deprecated.
   if (!cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty()) {
     mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
@@ -6849,17 +6862,17 @@ nsDocument::GetViewportInfo(uint32_t aDi
         (userScalable.EqualsLiteral("false"))) {
       mAllowZoom = false;
     }
 
     mScaleStrEmpty = scaleStr.IsEmpty();
     mWidthStrEmpty = widthStr.IsEmpty();
     mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
     mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
-  
+
     mViewportType = Specified;
   }
   case Specified:
   default:
     uint32_t width = mViewportWidth, height = mViewportHeight;
 
     if (!mValidWidth) {
       if (mValidHeight && aDisplayWidth > 0 && aDisplayHeight > 0) {
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -624,16 +624,17 @@ MOCHITEST_FILES_C= \
 		file_bothCSPheaders.html^headers^ \
 		badMessageEvent2.eventsource \
 		badMessageEvent2.eventsource^headers^ \
 		test_object.html \
 		test_bug869006.html \
 		test_bug868999.html \
 		test_bug869000.html \
 		test_bug869002.html \
+		test_bug876282.html \
 		$(NULL)
 
 # OOP tests don't work on Windows (bug 763081) or native-fennec
 # (see Bug 774939)
 ifneq ($(OS_ARCH),WINNT)
 ifndef MOZ_ANDROID_OMTC
 MOCHITEST_FILES_B += \
 		test_messagemanager_assertpermission.html \
copy from content/base/test/test_bug647518.html
copy to content/base/test/test_bug876282.html
--- a/content/base/test/test_bug647518.html
+++ b/content/base/test/test_bug876282.html
@@ -17,29 +17,29 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 647518 **/
 SimpleTest.waitForExplicitFinish();
 var counter = 3;
 
 var called = false;
-var handle1 = window.mozRequestAnimationFrame(function() {
+var handle1 = window.requestAnimationFrame(function() {
   called = true;
 });
 ok(handle1 > 0, "Should get back a nonzero handle");
 
 function checker() {
   --counter;
   if (counter == 0) {
     is(called, false, "Canceled callback should not have been called");
     SimpleTest.finish();
   } else {
-    window.mozRequestAnimationFrame(checker);
+    window.requestAnimationFrame(checker);
   }
 }
-window.mozRequestAnimationFrame(checker);
-window.mozCancelAnimationFrame(handle1);
+window.requestAnimationFrame(checker);
+window.cancelAnimationFrame(handle1);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -38,16 +38,18 @@
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
 namespace mozilla {
 class MediaResource;
 class MediaDecoder;
 }
 
+class nsITimer;
+
 namespace mozilla {
 namespace dom {
 
 class MediaError;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIObserver,
                          public MediaDecoderOwner,
@@ -528,31 +530,36 @@ protected:
   virtual void GetItemValueText(nsAString& text);
   virtual void SetItemValueText(const nsAString& text);
 
   class WakeLockBoolWrapper {
   public:
     WakeLockBoolWrapper(bool val = false)
       : mValue(val), mCanPlay(true), mOuter(nullptr) {}
 
+    ~WakeLockBoolWrapper();
+
     void SetOuter(HTMLMediaElement* outer) { mOuter = outer; }
     void SetCanPlay(bool aCanPlay);
 
     operator bool() const { return mValue; }
 
     WakeLockBoolWrapper& operator=(bool val);
 
     bool operator !() const { return !mValue; }
 
+    static void TimerCallback(nsITimer* aTimer, void* aClosure);
+
   private:
     void UpdateWakeLock();
 
     bool mValue;
     bool mCanPlay;
     HTMLMediaElement* mOuter;
+    nsCOMPtr<nsITimer> mTimer;
   };
 
   /**
    * These two methods are called by the WakeLockBoolWrapper when the wakelock
    * has to be created or released.
    */
   virtual void WakeLockCreate();
   virtual void WakeLockRelease();
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1982,16 +1982,18 @@ HTMLMediaElement::~HTMLMediaElement()
     "Destroyed media element should no longer be in element table");
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
   }
   if (mAudioStream) {
     mAudioStream->Shutdown();
   }
+
+  WakeLockRelease();
 }
 
 void
 HTMLMediaElement::GetItemValueText(nsAString& aValue)
 {
   // Can't call GetSrc because we don't have a JSContext
   GetURIAttr(nsGkAtoms::src, nullptr, aValue);
 }
@@ -2101,26 +2103,34 @@ HTMLMediaElement::Play(ErrorResult& aRv)
 NS_IMETHODIMP HTMLMediaElement::Play()
 {
   ErrorResult rv;
   Play(rv);
   return rv.ErrorCode();
 }
 
 HTMLMediaElement::WakeLockBoolWrapper&
-HTMLMediaElement::WakeLockBoolWrapper::operator=(bool val) {
+HTMLMediaElement::WakeLockBoolWrapper::operator=(bool val)
+{
   if (mValue == val) {
     return *this;
   }
 
   mValue = val;
   UpdateWakeLock();
   return *this;
 }
 
+HTMLMediaElement::WakeLockBoolWrapper::~WakeLockBoolWrapper()
+{
+  if (mTimer) {
+    mTimer->Cancel();
+  }
+}
+
 void
 HTMLMediaElement::WakeLockBoolWrapper::SetCanPlay(bool aCanPlay)
 {
   mCanPlay = aCanPlay;
   UpdateWakeLock();
 }
 
 void
@@ -2128,23 +2138,41 @@ HTMLMediaElement::WakeLockBoolWrapper::U
 {
   if (!mOuter) {
     return;
   }
 
   bool playing = (!mValue && mCanPlay);
 
   if (playing) {
+    if (mTimer) {
+      mTimer->Cancel();
+      mTimer = nullptr;
+    }
     mOuter->WakeLockCreate();
-  } else {
-    mOuter->WakeLockRelease();
+  } else if (!mTimer) {
+    // Don't release the wake lock immediately; instead, release it after a
+    // grace period.
+    int timeout = Preferences::GetInt("media.wakelock_timeout", 2000);
+    mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    mTimer->InitWithFuncCallback(TimerCallback, this, timeout,
+                                 nsITimer::TYPE_ONE_SHOT);
   }
 }
 
 void
+HTMLMediaElement::WakeLockBoolWrapper::TimerCallback(nsITimer* aTimer,
+                                                     void* aClosure)
+{
+  WakeLockBoolWrapper* wakeLock = static_cast<WakeLockBoolWrapper*>(aClosure);
+  wakeLock->mOuter->WakeLockRelease();
+  wakeLock->mTimer = nullptr;
+}
+
+void
 HTMLMediaElement::WakeLockCreate()
 {
   if (!mWakeLock) {
     nsCOMPtr<nsIPowerManagerService> pmService =
       do_GetService(POWERMANAGERSERVICE_CONTRACTID);
     NS_ENSURE_TRUE_VOID(pmService);
 
     pmService->NewWakeLock(NS_LITERAL_STRING("cpu"),
--- a/content/html/content/test/forms/test_input_sanitization.html
+++ b/content/html/content/test/forms/test_input_sanitization.html
@@ -31,16 +31,46 @@ https://bugzilla.mozilla.org/show_bug.cg
  * "other things" that affect .value makes it harder to know what we're testing
  * and what we've missed, because what's included in the value sanitization
  * algorithm and what's not is different from input type to input type. It
  * seems to me it would be better to have a test (maybe one per type) focused
  * on testing .value for permutations of all other inputs that can affect it.
  * The value sanitization algorithm is just an internal spec concept after all.
  */
 
+// We buffer up the results of sets of sub-tests, and avoid outputting log
+// entries for them all if they all pass.  Otherwise, we have an enormous amount
+// of test output.
+
+var delayedTests = [];
+var anyFailedDelayedTests = false;
+
+function delayed_is(actual, expected, description)
+{
+  var result = actual == expected;
+  delayedTests.push({ actual: actual, expected: expected, description: description });
+  if (!result) {
+    anyFailedDelayedTests = true;
+  }
+}
+
+function flushDelayedTests(description)
+{
+  if (anyFailedDelayedTests) {
+    info("Outputting individual results for \"" + description + "\" due to failures in subtests");
+    for (var test of delayedTests) {
+      is(test.actual, test.expected, test.description);
+    }
+  } else {
+    ok(true, description + " (" + delayedTests.length + " subtests)");
+  }
+  delayedTests = [];
+  anyFailedDelayedTests = false;
+}
+
 // We are excluding "file" because it's too different from the other types.
 // And it has no sanitizing algorithm.
 var inputTypes =
 [
   "text", "password", "search", "tel", "hidden", "checkbox", "radio",
   "submit", "image", "reset", "button", "email", "url", "number", "date",
   "time", "range", "color"
 ];
@@ -152,17 +182,17 @@ function sanitizeValue(aType, aValue)
       return "";
     case "color":
       return /^#[0-9A-Fa-f]{6}$/.exec(aValue) ? aValue.toLowerCase() : "#000000";
     default:
       return aValue;
   }
 }
 
-function checkSanitizing(element)
+function checkSanitizing(element, inputTypeDescription)
 {
   var testData =
   [
     // For text, password, search, tel, email:
     "\n\rfoo\n\r",
     "foo\n\rbar",
     "  foo  ",
     "  foo\n\r  bar  ",
@@ -297,74 +327,76 @@ function checkSanitizing(element)
     "fFAaBb",
     "FFAAZZ",
     "ABCDEF",
     "#7654321",
   ];
 
   for (value of testData) {
     element.setAttribute('value', value);
-    is(element.value, sanitizeValue(type, value),
+    delayed_is(element.value, sanitizeValue(type, value),
        "The value has not been correctly sanitized for type=" + type);
-    is(element.getAttribute('value'), value,
+    delayed_is(element.getAttribute('value'), value,
        "The content value should not have been sanitized");
 
     if (type in valueModeValue) {
       element.setAttribute('value', 'tulip');
       element.value = value;
-      is(element.value, sanitizeValue(type, value),
+      delayed_is(element.value, sanitizeValue(type, value),
          "The value has not been correctly sanitized for type=" + type);
-      is(element.getAttribute('value'), 'tulip',
+      delayed_is(element.getAttribute('value'), 'tulip',
          "The content value should not have been sanitized");
     }
 
     element.setAttribute('value', '');
     form.reset();
     element.type = 'checkbox'; // We know this type has no sanitizing algorithm.
     element.setAttribute('value', value);
-    is(element.value, value, "The value should not have been sanitized");
+    delayed_is(element.value, value, "The value should not have been sanitized");
     element.type = type;
-    is(element.value, sanitizeValue(type, value),
+    delayed_is(element.value, sanitizeValue(type, value),
        "The value has not been correctly sanitized for type=" + type);
-    is(element.getAttribute('value'), value,
+    delayed_is(element.getAttribute('value'), value,
        "The content value should not have been sanitized");
 
     element.setAttribute('value', '');
     form.reset();
     element.setAttribute('value', value);
     form.reset();
-    is(element.value, sanitizeValue(type, value),
+    delayed_is(element.value, sanitizeValue(type, value),
        "The value has not been correctly sanitized for type=" + type);
-    is(element.getAttribute('value'), value,
+    delayed_is(element.getAttribute('value'), value,
        "The content value should not have been sanitized");
 
     // Cleaning-up.
     element.setAttribute('value', '');
     form.reset();
   }
+
+  flushDelayedTests(inputTypeDescription);
 }
 
 for (type of inputTypes) {
   var form = document.forms[0];
   var element = document.createElement("input");
   element.style.display = "none";
   element.type = type;
   form.appendChild(element);
 
-  checkSanitizing(element); // no frame, no editor
+  checkSanitizing(element, "type=" + type + ", no frame, no editor");
 
   element.style.display = "";
-  checkSanitizing(element); // frame, no editor
+  checkSanitizing(element, "type=" + type + ", frame, no editor");
 
   element.focus();
   element.blur();
-  checkSanitizing(element); // frame, editor
+  checkSanitizing(element, "type=" + type + ", frame, editor");
 
   element.style.display = "none";
-  checkSanitizing(element); // no frame, editor
+  checkSanitizing(element, "type=" + type + ", no frame, editor");
 
   form.removeChild(element);
 }
 
 for (type of todoTypes) {
   // The only meaning of this is to have a failure when new types are introduced
   // so we will know we have to write the tests here.
   var form = document.forms[0];
--- a/content/html/content/test/test_audio_wakelock.html
+++ b/content/html/content/test/test_audio_wakelock.html
@@ -15,45 +15,48 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 868943 **/
 
-SpecialPowers.addPermission("power", true, document);
-
 function testAudioPlayPause() {
   var lockState = true;
   var count = 0;
 
   var content = document.getElementById('content');
 
   var audio = document.createElement('audio');
   audio.src = "wakelock.ogg";
   content.appendChild(audio);
 
+  var startDate;
   audio.addEventListener('progress', function() {
     lockState = false;
     audio.pause();
+    startDate = new Date();
   });
 
   navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
     is(topic, "cpu", "Audio element locked the target == cpu");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Audio element locked the cpu - no paused");
     count++;
 
     // count == 1 is when the cpu wakelock is created
     // count == 2 is when the cpu wakelock is released
 
     if (count == 2) {
+      var diffDate = (new Date() - startDate);
+      ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
+
       content.removeChild(audio);
       navigator.mozPower.removeWakeLockListener(testAudioPlayListener);
       runTests();
     }
   });
 
   audio.play();
 }
@@ -63,32 +66,40 @@ function testAudioPlay() {
   var count = 0;
 
   var content = document.getElementById('content');
 
   var audio = document.createElement('audio');
   audio.src = "wakelock.ogg";
   content.appendChild(audio);
 
+  var startDate;
+  audio.addEventListener('progress', function() {
+    startDate = new Date();
+  });
+
   navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
     is(topic, "cpu", "Audio element locked the target == cpu");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Audio element locked the cpu - no paused");
     count++;
 
     // count == 1 is when the cpu wakelock is created: the wakelock must be
     // created when the media element starts playing.
     // count == 2 is when the cpu wakelock is released.
 
     if (count == 1) {
       // The next step is to unlock the resource.
       lockState = false;
     } else if (count == 2) {
+      var diffDate = (new Date() - startDate);
+      ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
+
       content.removeChild(audio);
       navigator.mozPower.removeWakeLockListener(testAudioPlayListener);
       runTests();
     }
   });
 
   audio.play();
 }
@@ -99,15 +110,17 @@ function runTests() {
     SimpleTest.finish();
     return;
   }
 
   var test =  tests.pop();
   test();
 };
 
+SpecialPowers.addPermission("power", true, document);
+SpecialPowers.pushPrefEnv({"set": [["media.wakelock_timeout", 500]]}, runTests);
+
 SimpleTest.waitForExplicitFinish();
-runTests();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_video_wakelock.html
+++ b/content/html/content/test/test_video_wakelock.html
@@ -15,41 +15,47 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 868943 **/
 
-SpecialPowers.addPermission("power", true, document);
-
 function testVideoPlayPause() {
   var lockState = true;
   var count = 0;
 
   var content = document.getElementById('content');
 
   var video = document.createElement('video');
   video.src = "wakelock.ogv";
   content.appendChild(video);
 
+  var startDate;
+  video.addEventListener('progress', function() {
+    startDate = new Date();
+
+    // The next step is to unlock the resource.
+    lockState = false;
+    video.pause();
+  });
+
   navigator.mozPower.addWakeLockListener(function testVideoPlayPauseListener(topic, state) {
     is(topic, "screen", "Video element locked the target == screen");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Video element locked the screen - paused");
     count++;
 
-    if (count == 1) {
-      // The next step is to unlock the resource.
-      lockState = false;
-      video.pause();
-    } else if (count == 2) {
+    if (count == 2) {
+      var diffDate = (new Date() - startDate);
+      ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
+
       content.removeChild(video);
       navigator.mozPower.removeWakeLockListener(testVideoPlayPauseListener);
       runTests();
     }
   });
 
   video.play();
 }
@@ -59,28 +65,36 @@ function testVideoPlay() {
   var count = 0;
 
   var content = document.getElementById('content');
 
   var video = document.createElement('video');
   video.src = "wakelock.ogv";
   content.appendChild(video);
 
+  var startDate;
+  video.addEventListener('progress', function() {
+    startDate = new Date();
+  });
+
   navigator.mozPower.addWakeLockListener(function testVideoPlayListener(topic, state) {
     is(topic, "screen", "Video element locked the target == screen");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Video element locked the screen - no paused");
     count++;
 
     if (count == 1) {
       // The next step is to unlock the resource.
       lockState = false;
     } else if (count == 2) {
+      var diffDate = (new Date() - startDate);
+      ok(diffDate > 200, "There was at least milliseconds between the stop and the wakelock release");
+
       content.removeChild(video);
       navigator.mozPower.removeWakeLockListener(testVideoPlayListener);
       runTests();
     }
   });
 
   video.play();
 }
@@ -91,15 +105,17 @@ function runTests() {
     SimpleTest.finish();
     return;
   }
 
   var test =  tests.pop();
   test();
 };
 
+SpecialPowers.addPermission("power", true, document);
+SpecialPowers.pushPrefEnv({"set": [["media.wakelock_timeout", 500]]}, runTests);
+
 SimpleTest.waitForExplicitFinish();
-runTests();
 
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/876249.html
@@ -0,0 +1,27 @@
+<script>
+var Context0= new AudioContext()
+var BufferSource0=Context0.createBufferSource();
+var BiquadFilter0=Context0.createBiquadFilter();
+BufferSource0.start(0,0.6167310480959713,0.7142638498917222);
+BiquadFilter0.connect(Context0.destination);
+BufferSource0.buffer=function(){
+	var length=86333;
+	var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
+	var bufferData= Buffer.getChannelData(0);
+	for (var i = 0; i < length; ++i) { bufferData[i] = Math.sin(i*(-1))};
+	return Buffer;
+}();
+
+BufferSource0.connect(BiquadFilter0);
+
+BufferSource0.buffer=function(){
+	var length=21989;
+	var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
+	var bufferData= Buffer.getChannelData(0);
+	for (var i = 0; i < length; ++i) { bufferData[i] = Math.sin(i*(0))};
+	return Buffer;
+}();
+
+BufferSource0.stop(0.04184641852043569);
+
+</script>
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -17,9 +17,10 @@ load 846612.html
 load 852838.html
 load 874869.html
 load 874915.html
 load 874934.html
 load 874952.html
 load 875144.html
 load 875596.html
 load 875911.html
+load 876249.html
 load 876252.html
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -20,16 +20,17 @@
 #include "PannerNode.h"
 #include "AudioListener.h"
 #include "DynamicsCompressorNode.h"
 #include "BiquadFilterNode.h"
 #include "ScriptProcessorNode.h"
 #include "ChannelMergerNode.h"
 #include "ChannelSplitterNode.h"
 #include "WaveShaperNode.h"
+#include "WaveTable.h"
 #include "nsNetUtil.h"
 
 // Note that this number is an arbitrary large value to protect against OOM
 // attacks.
 const unsigned MAX_SCRIPT_PROCESSOR_CHANNELS = 10000;
 const unsigned MAX_CHANNEL_SPLITTER_OUTPUTS = UINT16_MAX;
 const unsigned MAX_CHANNEL_MERGER_INPUTS = UINT16_MAX;
 
@@ -303,16 +304,34 @@ AudioContext::CreateDynamicsCompressor()
 already_AddRefed<BiquadFilterNode>
 AudioContext::CreateBiquadFilter()
 {
   nsRefPtr<BiquadFilterNode> filterNode =
     new BiquadFilterNode(this);
   return filterNode.forget();
 }
 
+already_AddRefed<WaveTable>
+AudioContext::CreateWaveTable(const Float32Array& aRealData,
+                              const Float32Array& aImagData,
+                              ErrorResult& aRv)
+{
+  if (aRealData.Length() != aImagData.Length() ||
+      aRealData.Length() == 0 ||
+      aRealData.Length() > 4096) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<WaveTable> waveTable =
+    new WaveTable(this, aRealData.Data(), aRealData.Length(),
+                  aImagData.Data(), aImagData.Length());
+  return waveTable.forget();
+}
+
 AudioListener*
 AudioContext::Listener()
 {
   if (!mListener) {
     mListener = new AudioListener(this);
   }
   return mListener;
 }
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -49,16 +49,17 @@ class ChannelSplitterNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class GlobalObject;
 class OfflineRenderSuccessCallback;
 class PannerNode;
 class ScriptProcessorNode;
 class WaveShaperNode;
+class WaveTable;
 
 class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
                                public EnableWebAudioCheck
 {
   AudioContext(nsPIDOMWindow* aParentWindow,
                bool aIsOffline,
                uint32_t aNumberOfChannels = 0,
                uint32_t aLength = 0,
@@ -173,16 +174,20 @@ public:
   CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv);
 
   already_AddRefed<DynamicsCompressorNode>
   CreateDynamicsCompressor();
 
   already_AddRefed<BiquadFilterNode>
   CreateBiquadFilter();
 
+  already_AddRefed<WaveTable>
+  CreateWaveTable(const Float32Array& aRealData, const Float32Array& aImagData,
+                  ErrorResult& aRv);
+
   void DecodeAudioData(const ArrayBuffer& aBuffer,
                        DecodeSuccessCallback& aSuccessCallback,
                        const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback);
 
   // OfflineAudioContext methods
   void StartRendering();
   IMPL_EVENT_HANDLER(complete)
 
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/WaveTable.cpp
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WaveTable.h"
+#include "AudioContext.h"
+#include "mozilla/dom/WaveTableBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(WaveTable, mContext)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WaveTable, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WaveTable, Release)
+
+WaveTable::WaveTable(AudioContext* aContext,
+                     const float* aRealData,
+                     uint32_t aRealDataLength,
+                     const float* aImagData,
+                     uint32_t aImagDataLength)
+  : mContext(aContext)
+{
+  MOZ_ASSERT(aContext);
+  SetIsDOMBinding();
+}
+
+JSObject*
+WaveTable::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return WaveTableBinding::Wrap(aCx, aScope, this);
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/WaveTable.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WaveTable_h_
+#define WaveTable_h_
+
+#include "nsWrapperCache.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+#include "EnableWebAudioCheck.h"
+#include "AudioContext.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+namespace dom {
+
+class WaveTable MOZ_FINAL : public nsWrapperCache,
+                            public EnableWebAudioCheck
+{
+public:
+  WaveTable(AudioContext* aContext,
+            const float* aRealData,
+            uint32_t aRealDataLength,
+            const float* aImagData,
+            uint32_t aImagDataLength);
+
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WaveTable)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WaveTable)
+
+  AudioContext* GetParentObject() const
+  {
+    return mContext;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+private:
+  nsRefPtr<AudioContext> mContext;
+};
+
+}
+}
+
+#endif
+
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -33,16 +33,17 @@ EXPORTS.mozilla.dom += [
     'DelayNode.h',
     'DynamicsCompressorNode.h',
     'EnableWebAudioCheck.h',
     'GainNode.h',
     'OfflineAudioCompletionEvent.h',
     'PannerNode.h',
     'ScriptProcessorNode.h',
     'WaveShaperNode.h',
+    'WaveTable.h',
 ]
 
 CPP_SOURCES += [
     'AnalyserNode.cpp',
     'AudioBuffer.cpp',
     'AudioBufferSourceNode.cpp',
     'AudioContext.cpp',
     'AudioDestinationNode.cpp',
@@ -58,11 +59,12 @@ CPP_SOURCES += [
     'EnableWebAudioCheck.cpp',
     'GainNode.cpp',
     'MediaBufferDecoder.cpp',
     'OfflineAudioCompletionEvent.cpp',
     'PannerNode.cpp',
     'ScriptProcessorNode.cpp',
     'ThreeDPoint.cpp',
     'WaveShaperNode.cpp',
+    'WaveTable.cpp',
     'WebAudioUtils.cpp',
 ]
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -64,16 +64,17 @@ MOCHITEST_FILES := \
   test_pannerNode.html \
   test_scriptProcessorNode.html \
   test_scriptProcessorNodeChannelCount.html \
   test_scriptProcessorNodeZeroInputOutput.html \
   test_singleSourceDest.html \
   test_waveShaper.html \
   test_waveShaperNoCurve.html \
   test_waveShaperZeroLengthCurve.html \
+  test_waveTable.html \
   ting.ogg \
   ting-expected.wav \
   ting-dualchannel44.1.ogg \
   ting-dualchannel44.1-expected.wav \
   ting-dualchannel48.ogg \
   ting-dualchannel48-expected.wav \
   ting-mono-expected.wav \
   ting-mono-dualchannel44.1-expected.wav \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_waveTable.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test the WaveTable interface</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+  var ac = new AudioContext();
+  var real = new Float32Array(4096);
+  var imag = new Float32Array(4096);
+  var table = ac.createWaveTable(real, imag);
+  expectException(function() {
+    ac.createWaveTable(new Float32Array(512), imag);
+  }, DOMException.NOT_SUPPORTED_ERR);
+  expectException(function() {
+    ac.createWaveTable(new Float32Array(0), new Float32Array(0));
+  }, DOMException.NOT_SUPPORTED_ERR);
+  expectException(function() {
+    ac.createWaveTable(new Float32Array(4097), new Float32Array(4097));
+  }, DOMException.NOT_SUPPORTED_ERR);
+  SpecialPowers.clearUserPref("media.webaudio.enabled");
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -15,16 +15,20 @@ function debug(aStr) {
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AlarmDB.jsm");
 
 this.EXPORTED_SYMBOLS = ["AlarmService"];
 
+XPCOMUtils.defineLazyGetter(this, "appsService", function() {
+  return Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
+});
+
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyGetter(this, "messenger", function() {
   return Cc["@mozilla.org/system-message-internal;1"].getService(Ci.nsISystemMessagesInternal);
 });
 
@@ -44,16 +48,17 @@ let myGlobal = this;
  * using AlarmService.add() and AlarmService.remove(). Only Gecko code running
  * in the parent process should do this.
  */
 
 this.AlarmService = {
   init: function init() {
     debug("init()");
     Services.obs.addObserver(this, "profile-change-teardown", false);
+    Services.obs.addObserver(this, "webapps-clear-data",false);
 
     this._currentTimezoneOffset = (new Date()).getTimezoneOffset();
 
     let alarmHalService =
       this._alarmHalService = Cc["@mozilla.org/alarmHalService;1"]
                               .getService(Ci.nsIAlarmHalService);
 
     alarmHalService.setAlarmFiredCb(this._onAlarmFired.bind(this));
@@ -491,22 +496,40 @@ this.AlarmService = {
       }.bind(this)
     );
   },
 
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "profile-change-teardown":
         this.uninit();
+        break;
+      case "webapps-clear-data":
+        let params =
+          aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
+        let manifestURL = appsService.getManifestURLByLocalId(params.appId);
+        this._db.getAll(
+          manifestURL,
+          function getAllSuccessCb(aAlarms) {
+            aAlarms.forEach(function removeAlarm(aAlarm) {
+              this.remove(aAlarm.id, manifestURL);
+            }, this);
+          }.bind(this),
+          function getAllErrorCb(aErrorMsg) {
+            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+          }
+        );
+        break;
     }
   },
 
   uninit: function uninit() {
     debug("uninit()");
     Services.obs.removeObserver(this, "profile-change-teardown");
+    Services.obs.removeObserver(this, "webapps-clear-data");
 
     this._messages.forEach(function(aMsgName) {
       ppmm.removeMessageListener(aMsgName, this);
     }.bind(this));
     ppmm = null;
 
     if (this._db) {
       this._db.close();
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4424,23 +4424,29 @@ nsGlobalWindow::RequestAnimationFrame(co
 
   nsIDocument::FrameRequestCallbackHolder holder(callback);
   return RequestAnimationFrame(holder, aHandle);
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::MozCancelRequestAnimationFrame(int32_t aHandle)
 {
-  return MozCancelAnimationFrame(aHandle);
+  return CancelAnimationFrame(aHandle);
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::MozCancelAnimationFrame(int32_t aHandle)
 {
-  FORWARD_TO_INNER(MozCancelAnimationFrame, (aHandle),
+  return CancelAnimationFrame(aHandle);
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::CancelAnimationFrame(int32_t aHandle)
+{
+  FORWARD_TO_INNER(CancelAnimationFrame, (aHandle),
                    NS_ERROR_NOT_INITIALIZED);
 
   if (!mDoc) {
     return NS_OK;
   }
 
   mDoc->CancelFrameRequestCallback(aHandle);
   return NS_OK;
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/BindingGen.py
@@ -47,17 +47,30 @@ def main():
         finally:
             file.close()
         return contents
     allWebIDLFiles = readFile(args[2]).split()
     changedDeps = readFile(args[3]).split()
 
     if all(f.endswith("Binding") or f == "ParserResults.pkl" for f in changedDeps):
         toRegenerate = filter(lambda f: f.endswith("Binding"), changedDeps)
-        toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl", toRegenerate)
+        if len(toRegenerate) == 0 and len(changedDeps) == 1:
+            # Work around build system bug 874923: if we get here that means
+            # that changedDeps contained only one entry and it was
+            # "ParserResults.pkl".  That should never happen: if the
+            # ParserResults.pkl changes then either one of the globalgen files
+            # changed (in which case we wouldn't be in this "only
+            # ParserResults.pkl and *Binding changed" code) or some .webidl
+            # files changed (and then the corresponding *Binding files should
+            # show up in changedDeps).  Since clearly the build system is
+            # confused, just regenerate everything to be safe.
+            toRegenerate = allWebIDLFiles
+        else:
+            toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl",
+                               toRegenerate)
     else:
         toRegenerate = allWebIDLFiles
 
     for webIDLFile in toRegenerate:
         assert webIDLFile.endswith(".webidl")
         outputPrefix = webIDLFile[:-len(".webidl")] + "Binding"
         generate_binding_files(config, outputPrefix, srcPrefix, webIDLFile);
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1086,16 +1086,20 @@ DOMInterfaces = {
 {
     'implicitJSContext': [ 'createObjectURL', 'revokeObjectURL' ],
     'workers': True,
 }],
 
 'VideoStreamTrack': {
 },
 
+'WaveTable' : {
+    'nativeOwnership': 'refcounted'
+},
+
 'WebGLActiveInfo': {
    'nativeType': 'mozilla::WebGLActiveInfo',
    'headerFile': 'WebGLContext.h',
    'wrapperCache': False
 },
 
 'WebGLBuffer': {
    'nativeType': 'mozilla::WebGLBuffer',
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -349,17 +349,16 @@ IsValidDtmf(const char aChar) {
 BluetoothHfpManager::BluetoothHfpManager()
 {
   Reset();
 }
 
 void
 BluetoothHfpManager::ResetCallArray()
 {
-  mCurrentCallIndex = 0;
   mCurrentCallArray.Clear();
   // Append a call object at the beginning of mCurrentCallArray since call
   // index from RIL starts at 1.
   Call call;
   mCurrentCallArray.AppendElement(call);
 }
 
 void
@@ -1176,17 +1175,21 @@ BluetoothHfpManager::SendCommand(const c
           break;
         case nsITelephonyProvider::CALL_STATE_DIALING:
           message.AppendInt(2);
           break;
         case nsITelephonyProvider::CALL_STATE_ALERTING:
           message.AppendInt(3);
           break;
         case nsITelephonyProvider::CALL_STATE_INCOMING:
-          message.AppendInt((i == mCurrentCallIndex) ? 4 : 5);
+          if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
+            message.AppendInt(4);
+          } else {
+            message.AppendInt(5);
+          }
           break;
         default:
           NS_WARNING("Not handling call status for CLCC");
           break;
       }
       message.AppendLiteral(",0,0,\"");
       message.Append(NS_ConvertUTF16toUTF8(call.mNumber));
       message.AppendLiteral("\",");
@@ -1209,16 +1212,45 @@ BluetoothHfpManager::UpdateCIND(uint8_t 
     sCINDItems[aType].value = aValue;
     // Indicator status update is enabled
     if (aSend && mCMER) {
       SendCommand("+CIEV: ", aType);
     }
   }
 }
 
+uint32_t
+BluetoothHfpManager::FindFirstCall(uint16_t aState)
+{
+  uint32_t callLength = mCurrentCallArray.Length();
+
+  for (uint32_t i = 1; i < callLength; ++i) {
+    if (mCurrentCallArray[i].mState == aState) {
+      return i;
+    }
+  }
+
+  return 0;
+}
+
+uint32_t
+BluetoothHfpManager::GetNumberOfCalls(uint16_t aState)
+{
+  uint32_t num = 0;
+  uint32_t callLength = mCurrentCallArray.Length();
+
+  for (uint32_t i = 1; i < callLength; ++i) {
+    if (mCurrentCallArray[i].mState == aState) {
+      ++num;
+    }
+  }
+
+  return num;
+}
+
 void
 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
                                             uint16_t aCallState,
                                             const nsAString& aNumber,
                                             const bool aIsOutgoing,
                                             bool aSend)
 {
   if (!IsConnected()) {
@@ -1238,27 +1270,24 @@ BluetoothHfpManager::HandleCallStateChan
   // Same logic as implementation in ril_worker.js
   if (aNumber.Length() && aNumber[0] == '+') {
     mCurrentCallArray[aCallIndex].mType = TOA_INTERNATIONAL;
   }
   mCurrentCallArray[aCallIndex].mNumber = aNumber;
 
   nsRefPtr<nsRunnable> sendRingTask;
   nsString address;
-  uint32_t callArrayLength = mCurrentCallArray.Length();
-  uint32_t index = 1;
 
   switch (aCallState) {
     case nsITelephonyProvider::CALL_STATE_HELD:
       sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
       SendCommand("+CIEV: ", CINDType::CALLHELD);
       break;
     case nsITelephonyProvider::CALL_STATE_INCOMING:
-
-      if (mCurrentCallIndex) {
+      if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
         if (mCCWA) {
           nsAutoCString ccwaMsg("+CCWA: \"");
           ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
           ccwaMsg.AppendLiteral("\",");
           ccwaMsg.AppendInt(mCurrentCallArray[aCallIndex].mType);
           SendLine(ccwaMsg.get());
         }
         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
@@ -1291,46 +1320,40 @@ BluetoothHfpManager::HandleCallStateChan
     case nsITelephonyProvider::CALL_STATE_ALERTING:
       UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend);
 
       // If there's an ongoing call when the headset is just connected, we have
       // to open a sco socket here.
       ConnectSco();
       break;
     case nsITelephonyProvider::CALL_STATE_CONNECTED:
-      mCurrentCallIndex = aCallIndex;
       switch (prevCallState) {
         case nsITelephonyProvider::CALL_STATE_INCOMING:
         case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
           // Incoming call, no break
           sStopSendingRingFlag = true;
           ConnectSco();
         case nsITelephonyProvider::CALL_STATE_ALERTING:
           // Outgoing call
           UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
           break;
         case nsITelephonyProvider::CALL_STATE_HELD:
-          // Check whether to update CINDType::CALLHELD or not
-          while (index < callArrayLength) {
-            if (index == mCurrentCallIndex) {
-              index++;
-              continue;
-            }
-
-            uint16_t state = mCurrentCallArray[index].mState;
-            // If there's another call on hold or other calls exist, no need to
-            // update CINDType::CALLHELD
-            if (state != nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
-              break;
-            }
-            index++;
-          }
-
-          if (index == callArrayLength) {
+          // Besides checking if there is still held calls, another thing we
+          // need to consider is the state change when receiving AT+CHLD=2.
+          // Assume that there is one active call(c1) and one call on hold(c2).
+          // We got AT+CHLD=2, which swaps active/held position. The first
+          // action would be c2 -> ACTIVE, then c1 -> HELD. When we get the
+          // CallStateChanged event of c2 becoming ACTIVE, we enter here.
+          // However we can't send callheld=0 at this time because we should
+          // see c2 -> ACTIVE + c1 -> HELD as one operation. That's the reason
+          // why I added the GetNumberOfCalls() condition check.
+          if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD) &&
+              GetNumberOfCalls(
+                nsITelephonyProvider::CALL_STATE_CONNECTED) == 1) {
             UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
           }
           break;
         default:
           NS_WARNING("Not handling state changed");
       }
       break;
     case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
@@ -1353,33 +1376,22 @@ BluetoothHfpManager::HandleCallStateChan
           break;
         case nsITelephonyProvider::CALL_STATE_HELD:
           UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
           break;
         default:
           NS_WARNING("Not handling state changed");
       }
 
-      if (aCallIndex == mCurrentCallIndex) {
-        // Find the first non-disconnected call (like connected, held),
-        // and update mCurrentCallIndex
-        while (index < callArrayLength) {
-          if (mCurrentCallArray[index].mState !=
-              nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
-            mCurrentCallIndex = index;
-            break;
-          }
-          index++;
-        }
-
+      // -1 is necessary because call 0 is an invalid (padding) call object.
+      if (mCurrentCallArray.Length() - 1 ==
+          GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) {
         // There is no call, close Sco and clear mCurrentCallArray
-        if (index == callArrayLength) {
-          DisconnectSco();
-          ResetCallArray();
-        }
+        DisconnectSco();
+        ResetCallArray();
       }
       break;
     default:
       NS_WARNING("Not handling state changed");
       break;
   }
 }
 
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -104,31 +104,32 @@ private:
   nsresult HandleShutdown();
   nsresult HandleVolumeChanged(const nsAString& aData);
   nsresult HandleVoiceConnectionChanged();
 
   bool Init();
   void Cleanup();
   void Reset();
   void ResetCallArray();
+  uint32_t FindFirstCall(uint16_t aState);
+  uint32_t GetNumberOfCalls(uint16_t aState);
 
   void NotifyDialer(const nsAString& aCommand);
   void NotifyStatusChanged(const nsAString& aType);
   void NotifyAudioManager(const nsAString& aAddress);
 
   bool SendCommand(const char* aCommand, uint8_t aValue = 0);
   bool SendLine(const char* aMessage);
   void UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend);
   void OnScoConnectSuccess();
   void OnScoConnectError();
   void OnScoDisconnect();
 
   int mCurrentVgs;
   int mCurrentVgm;
-  uint32_t mCurrentCallIndex;
   bool mCCWA;
   bool mCLIP;
   bool mCMEE;
   bool mCMER;
   bool mFirstCKPD;
   int mNetworkSelectionMode;
   bool mReceiveVgsFlag;
   bool mBLDNProcessed;
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -434,16 +434,17 @@ BluetoothOppManager::AfterFirstPut()
 {
   mUpdateProgressCounter = 1;
   mPutFinalFlag = false;
   mReceivedDataBufferOffset = 0;
   mSendTransferCompleteFlag = false;
   sSentFileLength = 0;
   sWaitingToSendPutFinal = false;
   mSuccessFlag = false;
+  mBodySegmentLength = 0;
 }
 
 void
 BluetoothOppManager::AfterOppConnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = true;
@@ -766,17 +767,17 @@ BluetoothOppManager::ServerDataHandler(U
                  &pktHeaders);
     ExtractPacketHeaders(pktHeaders);
     ValidateFileName();
 
     mReceivedDataBufferOffset = 0;
 
     // When we cancel the transfer, delete the file and notify complemention
     if (mAbortFlag) {
-      ReplyToPut(mPutFinalFlag, !mAbortFlag);
+      ReplyToPut(mPutFinalFlag, false);
       sSentFileLength += mBodySegmentLength;
       DeleteReceivedFile();
       FileTransferComplete();
       return;
     }
 
     // Wait until get confirmation from user, then create file and write to it
     if (mWaitingForConfirmationFlag) {
--- a/dom/indexedDB/TransactionThreadPool.cpp
+++ b/dom/indexedDB/TransactionThreadPool.cpp
@@ -667,17 +667,18 @@ FinishTransactionRunnable::Run()
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(TransactionThreadPoolListener,
                               nsIThreadPoolListener)
 
 NS_IMETHODIMP
 TransactionThreadPoolListener::OnThreadCreated()
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  profiler_register_thread("IndexedDB Transaction");
+  char aLocal;
+  profiler_register_thread("IndexedDB Transaction", &aLocal);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TransactionThreadPoolListener::OnThreadShuttingDown()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   profiler_unregister_thread();
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -20,17 +20,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(e0f33b20-72ef-415b-9ff2-8bf176f581f8)]
+[scriptable, uuid(be62660a-e3f6-409c-a4a9-378364a9526f)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -449,16 +449,17 @@ interface nsIDOMWindow : nsISupports
   long requestAnimationFrame(in jsval aCallback);
 
   /**
    * Cancel a refresh callback.
    */
   void mozCancelAnimationFrame(in long aHandle);
   // Backwards-compat shim for now to make Google maps work
   void mozCancelRequestAnimationFrame(in long aHandle);
+  void cancelAnimationFrame(in long aHandle);
 
   /**
    * The current animation start time in milliseconds since the epoch.
    */
   readonly attribute long long mozAnimationStartTime;
 
   /**
    * HTML5 event attributes that only apply to windows and <body>/<frameset>
@@ -503,10 +504,10 @@ interface nsIDOMWindowPerformance : nsIS
    */
   readonly attribute nsISupports performance;
 };
 
 /**
  * Empty interface for compatibility with older versions.
  * @deprecated Use nsIDOMWindow instead
  */
-[scriptable, uuid(36aeaa8e-3126-49de-9581-276dd7117826)]
+[scriptable, uuid(ad5768c7-8668-4cd4-bcac-3d0a400d50be)]
 interface nsIDOMWindowInternal : nsIDOMWindow {};
--- a/dom/mobilemessage/src/MmsMessage.cpp
+++ b/dom/mobilemessage/src/MmsMessage.cpp
@@ -9,16 +9,17 @@
 #include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch
 #include "nsJSUtils.h"
 #include "Constants.h"
 #include "nsContentUtils.h"
 #include "nsIDOMFile.h"
 #include "nsTArrayHelpers.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/mobilemessage/SmsTypes.h"
+#include "nsDOMFile.h"
 
 using namespace mozilla::idl;
 using namespace mozilla::dom::mobilemessage;
 
 DOMCI_DATA(MozMmsMessage, mozilla::dom::MmsMessage)
 
 namespace mozilla {
 namespace dom {
@@ -294,16 +295,29 @@ MmsMessage::GetData(ContentParent* aPare
   aData.expiryDate() = mExpiryDate;
 
   aData.attachments().SetCapacity(mAttachments.Length());
   for (uint32_t i = 0; i < mAttachments.Length(); i++) {
     MmsAttachmentData mma;
     const MmsAttachment &element = mAttachments[i];
     mma.id().Assign(element.id);
     mma.location().Assign(element.location);
+
+    // This is a workaround. Sometimes the blob we get from the database
+    // doesn't have a valid last modified date, making the ContentParent
+    // send a "Mystery Blob" to the ContentChild. Attempting to get the
+    // last modified date of blob can force that value to be initialized.
+    nsDOMFileBase* file = static_cast<nsDOMFileBase*>(element.content.get());
+    if (file->IsDateUnknown()) {
+      uint64_t date;
+      if (NS_FAILED(file->GetMozLastModifiedDate(&date))) {
+        NS_WARNING("Failed to get last modified date!");
+      }
+    }
+
     mma.contentParent() = aParent->GetOrCreateActorForBlob(element.content);
     if (!mma.contentParent()) {
       return false;
     }
     aData.attachments().AppendElement(mma);
   }
 
   return true;
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2256,26 +2256,24 @@ NPError NP_CALLBACK
     case kSupportedDrawingModel_ANPGetValue: {
       LOG("get supported drawing model");
       uint32_t* bits = reinterpret_cast<uint32_t*>(result);
       *bits = kBitmap_ANPDrawingModel && kSurface_ANPDrawingModel;
       return NPERR_NO_ERROR;
     }  
 
     case kJavaContext_ANPGetValue: {
-      LOG("get context");
-      JNIEnv* env = GetJNIForThread();
-      if (!env)
+      AndroidBridge *bridge = AndroidBridge::Bridge();
+      if (!bridge)
         return NPERR_GENERIC_ERROR;
 
-      jclass cls     = env->FindClass("org/mozilla/gecko/GeckoApp");
-      jfieldID field = env->GetStaticFieldID(cls, "mAppContext",
-                                             "Lorg/mozilla/gecko/GeckoApp;");
-      jobject ret = env->GetStaticObjectField(cls, field);
-      env->DeleteLocalRef(cls);
+      jobject ret = bridge->GetContext();
+      if (!ret)
+        return NPERR_GENERIC_ERROR;
+
       int32_t* i  = reinterpret_cast<int32_t*>(result);
       *i = reinterpret_cast<int32_t>(ret);
       return NPERR_NO_ERROR;
     }
 
     case kAudioTrackInterfaceV1_ANPGetValue: {
       LOG("get audio interface v1");
       ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result;
--- a/dom/plugins/base/nsPluginsDirWin.cpp
+++ b/dom/plugins/base/nsPluginsDirWin.cpp
@@ -22,16 +22,51 @@
 
 #include "windows.h"
 #include "winbase.h"
 
 #include "nsString.h"
 #include "nsIFile.h"
 #include "nsUnicharUtils.h"
 
+#include <shlwapi.h>
+#define SHOCKWAVE_BASE_FILENAME L"np32dsw"
+/**
+ * Determines whether or not SetDllDirectory should be called for this plugin.
+ *
+ * @param pluginFilePath The full path of the plugin file
+ * @return true if SetDllDirectory can be called for the plugin
+ */
+bool
+ShouldProtectPluginCurrentDirectory(LPCWSTR pluginFilePath)
+{
+  LPCWSTR passedInFilename = PathFindFileName(pluginFilePath);
+  if (!passedInFilename) {
+    return true;
+  }
+
+  // Somewhere in the middle of 11.6 version of Shockwave, naming of the DLL
+  // after its version number is introduced.
+  if (!wcsicmp(passedInFilename, SHOCKWAVE_BASE_FILENAME L".dll")) {
+    return false;
+  }
+
+  // Shockwave versions before 1202122 will break if you call SetDllDirectory
+  const uint64_t kFixedShockwaveVersion = 1202122;
+  uint64_t version;
+  int found = swscanf(passedInFilename, SHOCKWAVE_BASE_FILENAME L"_%llu.dll",
+                      &version);
+  if (found && version < kFixedShockwaveVersion) {
+    return false;
+  }
+
+  // We always want to call SetDllDirectory otherwise
+  return true;
+}
+
 using namespace mozilla;
 
 /* Local helper functions */
 
 static char* GetKeyValue(void* verbuf, const WCHAR* key,
                          UINT language, UINT codepage)
 {
   WCHAR keybuf[64]; // plenty for the template below, with the longest key
@@ -240,27 +275,23 @@ nsPluginFile::~nsPluginFile()
  */
 nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
 {
   if (!mPlugin)
     return NS_ERROR_NULL_POINTER;
 
   bool protectCurrentDirectory = true;
 
-  nsAutoString pluginFolderPath;
-  mPlugin->GetPath(pluginFolderPath);
+  nsAutoString pluginFilePath;
+  mPlugin->GetPath(pluginFilePath);
+  protectCurrentDirectory =
+    ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading());
 
-  int32_t idx = pluginFolderPath.RFindChar('\\');
-  if (kNotFound == idx)
-    return NS_ERROR_FILE_INVALID_PATH;
-
-  if (Substring(pluginFolderPath, idx).LowerCaseEqualsLiteral("\\np32dsw.dll")) {
-    protectCurrentDirectory = false;
-  }
-
+  nsAutoString pluginFolderPath = pluginFilePath;
+  int32_t idx = pluginFilePath.RFindChar('\\');
   pluginFolderPath.SetLength(idx);
 
   BOOL restoreOrigDir = FALSE;
   WCHAR aOrigDir[MAX_PATH + 1];
   DWORD dwCheck = GetCurrentDirectoryW(MAX_PATH, aOrigDir);
   NS_ASSERTION(dwCheck <= MAX_PATH + 1, "Error in Loading plugin");
 
   if (dwCheck <= MAX_PATH + 1) {
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -10,16 +10,17 @@
 #include "prlink.h"
 
 #include "base/command_line.h"
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
 
 #ifdef XP_WIN
 #include <objbase.h>
+bool ShouldProtectPluginCurrentDirectory(LPCWSTR pluginFilePath);
 #endif
 
 using mozilla::ipc::IOThreadChild;
 
 #ifdef OS_WIN
 #include "nsSetDllDirectory.h"
 #include <algorithm>
 
@@ -32,16 +33,17 @@ std::size_t caseInsensitiveFind(std::str
 }
 
 }
 #endif
 
 namespace mozilla {
 namespace plugins {
 
+
 bool
 PluginProcessChild::Init()
 {
 #if defined(XP_MACOSX)
     // Remove the trigger for "dyld interposing" that we added in
     // GeckoChildProcessHost::PerformAsyncLaunchInternal(), in the host
     // process just before we were launched.  Dyld interposing will still
     // happen in our process (the plugin child process).  But we don't want
@@ -97,31 +99,22 @@ PluginProcessChild::Init()
 
     pluginFilename = UnmungePluginDsoPath(values[1]);
 
 #elif defined(OS_WIN)
     std::vector<std::wstring> values =
         CommandLine::ForCurrentProcess()->GetLooseValues();
     NS_ABORT_IF_FALSE(values.size() >= 1, "not enough loose args");
 
-    pluginFilename = WideToUTF8(values[0]);
-
-    bool protectCurrentDirectory = true;
-    // Don't use SetDllDirectory for Shockwave Director
-    const std::string shockwaveDirectorPluginFilename("\\np32dsw.dll");
-    std::size_t index = caseInsensitiveFind(pluginFilename, shockwaveDirectorPluginFilename);
-    if (index != std::string::npos &&
-        index + shockwaveDirectorPluginFilename.length() == pluginFilename.length()) {
-        protectCurrentDirectory = false;
-    }
-    if (protectCurrentDirectory) {
+    if (ShouldProtectPluginCurrentDirectory(values[0].c_str())) {
         SanitizeEnvironmentVariables();
         SetDllDirectory(L"");
     }
 
+    pluginFilename = WideToUTF8(values[0]);
 #else
 #  error Sorry
 #endif
 
     if (NS_FAILED(nsRegion::InitStatic())) {
       NS_ERROR("Could not initialize nsRegion");
       return false;
     }
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -56,16 +56,19 @@ interface AudioContext : EventTarget {
     [Creator, Throws]
     ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
     [Creator, Throws]
     ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6);
 
     [Creator]
     DynamicsCompressorNode createDynamicsCompressor();
 
+    [Creator, Throws]
+    WaveTable createWaveTable(Float32Array real, Float32Array imag);
+
 };
 
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
  */
 [PrefControlled]
 partial interface AudioContext {
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WaveTable.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface WaveTable {
+
+};
+
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -333,16 +333,17 @@ webidl_files = \
   WebComponents.webidl \
   WebSocket.webidl \
   WheelEvent.webidl \
   UndoManager.webidl \
   URLUtils.webidl \
   USSDReceivedEvent.webidl \
   VideoStreamTrack.webidl \
   WaveShaperNode.webidl \
+  WaveTable.webidl \
   Window.webidl \
   XMLDocument.webidl \
   XMLHttpRequest.webidl \
   XMLHttpRequestEventTarget.webidl \
   XMLHttpRequestUpload.webidl \
   XMLSerializer.webidl \
   XMLStylesheetProcessingInstruction.webidl \
   XPathEvaluator.webidl \
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -509,17 +509,18 @@ public:
     if (!cx) {
       // XXX need to fire an error at parent.
       NS_ERROR("Failed to create runtime and context!");
       return NS_ERROR_FAILURE;
     }
 
     JSRuntime* rt = JS_GetRuntime(cx);
 
-    profiler_register_thread("WebWorker");
+    char aLocal;
+    profiler_register_thread("WebWorker", &aLocal);
 #ifdef MOZ_ENABLE_PROFILER_SPS
     if (PseudoStack* stack = mozilla_get_pseudo_stack())
       stack->sampleRuntime(rt);
 #endif
 
     {
       JSAutoRequest ar(cx);
       workerPrivate->DoRunLoop(cx);
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -3227,17 +3227,22 @@ static cairo_int_status_t
 		 cairo_clip_t		*clip)
 {
     cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
     cairo_int_status_t status;
 
     op = _cairo_d2d_simplify_operator(op, source);
 
     if (op == CAIRO_OPERATOR_SOURCE) {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+	if (!clip) {
+	    _cairo_d2d_clear(d2dsurf, NULL);
+	    op = CAIRO_OPERATOR_OVER;
+	} else {
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
     }
 
     if (op == CAIRO_OPERATOR_CLEAR) {
 	return _cairo_d2d_clear(d2dsurf, clip);
     }
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	const cairo_surface_pattern_t *surf_pattern = 
--- a/gfx/graphite2/README.mozilla
+++ b/gfx/graphite2/README.mozilla
@@ -1,6 +1,6 @@
 This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev
 
-Current version derived from upstream changeset 51e72e74b9a6
+Current version derived from upstream changeset 09707dd22634
 
 See gfx/graphite2/moz-gr-update.sh for update procedure.
 
--- a/gfx/graphite2/include/graphite2/Font.h
+++ b/gfx/graphite2/include/graphite2/Font.h
@@ -25,17 +25,17 @@
     either version 2 of the License or (at your option) any later version.
 */
 #pragma once
 
 #include "graphite2/Types.h"
 
 #define GR2_VERSION_MAJOR   1
 #define GR2_VERSION_MINOR   2
-#define GR2_VERSION_BUGFIX  0
+#define GR2_VERSION_BUGFIX  2
 
 #ifdef __cplusplus
 extern "C"
 {
 #endif
 
 typedef struct gr_face          gr_face;
 typedef struct gr_font          gr_font;
--- a/gfx/graphite2/src/Bidi.cpp
+++ b/gfx/graphite2/src/Bidi.cpp
@@ -32,20 +32,20 @@ using namespace graphite2;
 
 enum DirCode {  // Hungarian: dirc
         Unk        = -1,
         N          =  0,   // other neutrals (default) - ON
         L          =  1,   // left-to-right, strong - L
         R          =  2,   // right-to-left, strong - R
         AL         =  3,   // Arabic letter, right-to-left, strong, AR
         EN         =  4,   // European number, left-to-right, weak - EN
-        ES         =  5,   // European separator, left-to-right, weak - ES
+        EUS        =  5,   // European separator, left-to-right, weak - ES
         ET         =  6,   // European number terminator, left-to-right, weak - ET
         AN         =  7,   // Arabic number, left-to-right, weak - AN
-        CS         =  8,   // Common number separator, left-to-right, weak - CS
+        CUS        =  8,   // Common number separator, left-to-right, weak - CS
         WS         =  9,   // white space, neutral - WS
         BN         = 10,   // boundary neutral - BN
 
         LRO        = 11,   // LTR override
         RLO        = 12,   // RTL override
         LRE        = 13,   // LTR embedding
         RLE        = 14,   // RTL embedding
         PDF        = 15,   // pop directional format
--- a/gfx/graphite2/src/CMakeLists.txt
+++ b/gfx/graphite2/src/CMakeLists.txt
@@ -47,16 +47,19 @@ if (GRAPHITE2_NFILEFACE)
 endif (GRAPHITE2_NFILEFACE)
 
 set(TRACING json.cpp)
 if (GRAPHITE2_NTRACING)
     add_definitions(-DGRAPHITE2_NTRACING)
     set(TRACING)
 endif (GRAPHITE2_NTRACING)
 
+if (GRAPHITE2_TELEMETRY)
+    add_definitions(-DGRAPHITE2_TELEMETRY)
+endif (GRAPHITE2_TELEMETRY)
 
 set(GRAPHITE_HEADERS 
     ../include/graphite2/Font.h
     ../include/graphite2/Segment.h
     ../include/graphite2/Types.h
     ../include/graphite2/Log.h
     )
 
--- a/gfx/graphite2/src/CmapCache.cpp
+++ b/gfx/graphite2/src/CmapCache.cpp
@@ -32,28 +32,30 @@ of the License or (at your option) any l
 #include "inc/TtfUtil.h"
 
 
 using namespace graphite2;
 
 const void * bmp_subtable(const Face::Table & cmap)
 {
     const void * stbl;
+    if (!cmap.size()) return 0;
     if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()))
      || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()))
      || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()))
      || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()))
      || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size())))
         return stbl;
     return 0;
 }
 
 const void * smp_subtable(const Face::Table & cmap)
 {
     const void * stbl;
+    if (!cmap.size()) return 0;
     if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()))
      || TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size())))
         return stbl;
     return 0;
 }
 
 template <unsigned int (*NextCodePoint)(const void *, unsigned int, int *),
           uint16 (*LookupCodePoint)(const void *, unsigned int, int)>
@@ -104,16 +106,17 @@ CachedCmap::CachedCmap(const Face & face
     {
         if (!cache_subtable<TtfUtil::CmapSubtable4NextCodepoint, TtfUtil::CmapSubtable4Lookup>(m_blocks, bmp_cmap, 0xFFFF))
             return;
     }
 }
 
 CachedCmap::~CachedCmap() throw()
 {
+    if (!m_blocks) return;
     unsigned int numBlocks = (m_isBmpOnly)? 0x100 : 0x1100;
     for (unsigned int i = 0; i < numBlocks; i++)
     	free(m_blocks[i]);
     free(m_blocks);
 }
 
 uint16 CachedCmap::operator [] (const uint32 usv) const throw()
 {
--- a/gfx/graphite2/src/Code.cpp
+++ b/gfx/graphite2/src/Code.cpp
@@ -139,16 +139,19 @@ inline Machine::Code::decoder::decoder(c
     
 
 
 Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
            uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face)
  :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
     _constraint(is_constraint), _modify(false), _delete(false), _own(true)
 {
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _code_cat(face.tele.code);
+#endif
     assert(bytecode_begin != 0);
     if (bytecode_begin == bytecode_end)
     {
       ::new (this) Code();
       return;
     }
     assert(bytecode_end > bytecode_begin);
     const opcode_t *    op_to_fn = Machine::getOpcodeTable();
@@ -205,22 +208,26 @@ Machine::Code::Code(bool is_constraint, 
     
     // Now we know exactly how much code and data the program really needs
     // realloc the buffers to exactly the right size so we don't waste any 
     // memory.
     assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_instr_count));
     assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_data_size));
     _code = static_cast<instr *>(realloc(_code, (_instr_count+1)*sizeof(instr)));
     _data = static_cast<byte *>(realloc(_data, _data_size*sizeof(byte)));
-    
+
+    if (!_code)
+        failure(alloc_failed);
+
     // Make this RET_ZERO, we should never reach this but just in case ...
     _code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];
 
-    if (!_code)
-        failure(alloc_failed);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr));
+#endif
 }
 
 Machine::Code::~Code() throw ()
 {
     if (_own)
         release_buffers();
 }
 
--- a/gfx/graphite2/src/Face.cpp
+++ b/gfx/graphite2/src/Face.cpp
@@ -73,16 +73,19 @@ float Face::default_glyph_advance(const 
 {
     const Font & font = *reinterpret_cast<const Font *>(font_ptr);
 
     return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
 }
 
 bool Face::readGlyphs(uint32 faceOptions)
 {
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _glyph_cat(tele.glyph);
+#endif
     if (faceOptions & gr_face_cacheCmap)
     	m_cmap = new CachedCmap(*this);
     else
     	m_cmap = new DirectCmap(*this);
 
     m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
     if (!m_pGlyphFaceCache
         || m_pGlyphFaceCache->numGlyphs() == 0
@@ -93,16 +96,19 @@ bool Face::readGlyphs(uint32 faceOptions
     if (faceOptions & gr_face_preloadGlyphs)
         nameTable();        // preload the name table along with the glyphs.
 
     return true;
 }
 
 bool Face::readGraphite(const Table & silf)
 {
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _silf_cat(tele.silf);
+#endif
     const byte * p = silf;
     if (!p) return false;
 
     const uint32 version = be::read<uint32>(p);
     if (version < 0x00020000) return false;
     if (version >= 0x00030000)
     	be::skip<uint32>(p);		// compilerVersion
     m_numSilf = be::read<uint16>(p);
@@ -140,17 +146,18 @@ bool Face::runGraphite(Segment *seg, con
     {
     	*dbgout << json::object
     				<< "id"			<< objectid(seg)
     				<< "passes"		<< json::array;
     }
 #endif
 
     bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass());
-    res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses());
+    if (res)
+        res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses());
 
 #if !defined GRAPHITE2_NTRACING
 	if (dbgout)
 {
 		*dbgout 			<< json::item
 							<< json::close // Close up the passes array
 				<< "output" << json::array;
 		for(Slot * s = seg->first(); s; s = s->next())
--- a/gfx/graphite2/src/FileFace.cpp
+++ b/gfx/graphite2/src/FileFace.cpp
@@ -73,26 +73,26 @@ FileFace::~FileFace()
 }
 
 
 const void *FileFace::get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len)
 {
     if (appFaceHandle == 0)     return 0;
     const FileFace & file_face = *static_cast<const FileFace *>(appFaceHandle);
 
-    char *tbl;
+    void *tbl;
     size_t tbl_offset, tbl_len;
     if (!TtfUtil::GetTableInfo(name, file_face._header_tbl, file_face._table_dir, tbl_offset, tbl_len))
         return 0;
 
     if (tbl_offset + tbl_len > file_face._file_len
             || fseek(file_face._file, tbl_offset, SEEK_SET) != 0)
         return 0;
 
-    tbl = gralloc<char>(tbl_len);
+    tbl = malloc(tbl_len);
     if (fread(tbl, 1, tbl_len, file_face._file) != tbl_len)
     {
         free(tbl);
         return 0;
     }
 
     if (len) *len = tbl_len;
     return tbl;
--- a/gfx/graphite2/src/NameTable.cpp
+++ b/gfx/graphite2/src/NameTable.cpp
@@ -32,17 +32,17 @@ of the License or (at your option) any l
 
 using namespace graphite2;
 
 NameTable::NameTable(const void* data, size_t length, uint16 platformId, uint16 encodingID)
  : m_platformId(0), m_encodingId(0), m_languageCount(0),
    m_platformOffset(0), m_platformLastRecord(0), m_nameDataLength(0),
    m_table(0), m_nameData(NULL)
 {
-    void *pdata = malloc(length);
+    void *pdata = gralloc<byte>(length);
     if (!pdata) return;
     memcpy(pdata, data, length);
     m_table = reinterpret_cast<const TtfUtil::Sfnt::FontNames*>(pdata);
 
     if ((length > sizeof(TtfUtil::Sfnt::FontNames)) &&
         (length > sizeof(TtfUtil::Sfnt::FontNames) +
          sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16>(m_table->count) - 1)))
     {
--- a/gfx/graphite2/src/Pass.cpp
+++ b/gfx/graphite2/src/Pass.cpp
@@ -41,86 +41,87 @@ typedef Machine::Code  Code;
 
 
 Pass::Pass()
 : m_silf(0),
   m_cols(0),
   m_rules(0),
   m_ruleMap(0),
   m_startStates(0),
-  m_sTable(0),
+  m_transitions(0),
   m_states(0),
   m_flags(0),
   m_iMaxLoop(0),
   m_numGlyphs(0),
   m_numRules(0),
-  m_sRows(0),
-  m_sTransition(0),
-  m_sSuccess(0),
-  m_sColumns(0),
+  m_numStates(0),
+  m_numTransition(0),
+  m_numSuccess(0),
+  m_numColumns(0),
   m_minPreCtxt(0),
   m_maxPreCtxt(0)
 {
 }
 
 Pass::~Pass()
 {
     free(m_cols);
     free(m_startStates);
-    free(m_sTable);
+    free(m_transitions);
     free(m_states);
     free(m_ruleMap);
 
     delete [] m_rules;
 }
 
-bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, const Face & face)
+bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED const Face & face)
 {
     const byte *                p = pass_start,
                * const pass_end   = p + pass_length;
     size_t numRanges;
 
     if (pass_length < 40) return false; 
     // Read in basic values
     m_flags = be::read<byte>(p);
     m_iMaxLoop = be::read<byte>(p);
     be::skip<byte>(p,2); // skip maxContext & maxBackup
     m_numRules = be::read<uint16>(p);
     be::skip<uint16>(p);   // fsmOffset - not sure why we would want this
     const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
                * const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
                * const aCode  = pass_start + be::read<uint32>(p) - subtable_base;
     be::skip<uint32>(p);
-    m_sRows = be::read<uint16>(p);
-    m_sTransition = be::read<uint16>(p);
-    m_sSuccess = be::read<uint16>(p);
-    m_sColumns = be::read<uint16>(p);
+    m_numStates = be::read<uint16>(p);
+    m_numTransition = be::read<uint16>(p);
+    m_numSuccess = be::read<uint16>(p);
+    m_numColumns = be::read<uint16>(p);
     numRanges = be::read<uint16>(p);
     be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift.
     assert(p - pass_start == 40);
     // Perform some sanity checks.
-    if (   m_sTransition > m_sRows
-            || m_sSuccess > m_sRows
-            || m_sSuccess + m_sTransition < m_sRows
+    if (   m_numTransition > m_numStates
+            || m_numSuccess > m_numStates
+            || m_numSuccess + m_numTransition < m_numStates
             || numRanges == 0)
         return false;
 
+    m_successStart = m_numStates - m_numSuccess;
     if (p + numRanges * 6 - 4 > pass_end) return false;
     m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1;
     // Calculate the start of various arrays.
     const byte * const ranges = p;
     be::skip<uint16>(p, numRanges*3);
     const byte * const o_rule_map = p;
-    be::skip<uint16>(p, m_sSuccess + 1);
+    be::skip<uint16>(p, m_numSuccess + 1);
 
     // More sanity checks
-    if (reinterpret_cast<const byte *>(o_rule_map + m_sSuccess*sizeof(uint16)) > pass_end
+    if (reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end
             || p > pass_end)
         return false;
-    const size_t numEntries = be::peek<uint16>(o_rule_map + m_sSuccess*sizeof(uint16));
+    const size_t numEntries = be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
     const byte * const   rule_map = p;
     be::skip<uint16>(p, numEntries);
 
     if (p + 2*sizeof(uint8) > pass_end) return false;
     m_minPreCtxt = be::read<uint8>(p);
     m_maxPreCtxt = be::read<uint8>(p);
     if (m_minPreCtxt > m_maxPreCtxt) return false;
     const byte * const start_states = p;
@@ -133,17 +134,17 @@ bool Pass::readPass(const byte * const p
 
     if (p + sizeof(uint16) > pass_end) return false;
     const size_t pass_constraint_len = be::read<uint16>(p);
     const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
     be::skip<uint16>(p, m_numRules + 1);
     const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
     be::skip<uint16>(p, m_numRules + 1);
     const byte * const states = p;
-    be::skip<int16>(p, m_sTransition*m_sColumns);
+    be::skip<int16>(p, m_numTransition*m_numColumns);
     be::skip<byte>(p);          // skip reserved byte
     if (p != pcCode || p >= pass_end) return false;
     be::skip<byte>(p, pass_constraint_len);
     if (p != rcCode || p >= pass_end
         || size_t(rcCode - pcCode) != pass_constraint_len) return false;
     be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
     if (p != aCode || p >= pass_end) return false;
     be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
@@ -156,17 +157,20 @@ bool Pass::readPass(const byte * const p
     {
         m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, 
                                   precontext[0], be::peek<uint16>(sort_keys), *m_silf, face);
         if (!m_cPConstraint) return false;
     }
     if (!readRanges(ranges, numRanges)) return false;
     if (!readRules(rule_map, numEntries,  precontext, sort_keys,
                    o_constraint, rcCode, o_actions, aCode, face)) return false;
-    return readStates(start_states, states, o_rule_map);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _states_cat(face.tele.states);
+#endif
+    return readStates(start_states, states, o_rule_map, face);
 }
 
 
 bool Pass::readRules(const byte * rule_map, const size_t num_entries,
                      const byte *precontext, const uint16 * sort_key,
                      const uint16 * o_constraint, const byte *rc_data,
                      const uint16 * o_action,     const byte * ac_data,
                      const Face & face)
@@ -220,46 +224,53 @@ bool Pass::readRules(const byte * rule_m
     }
 
     return true;
 }
 
 static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 :
                                                                 (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); }
 
-bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map)
+bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED const Face & face)
 {
-    m_startStates = gralloc<State *>(m_maxPreCtxt - m_minPreCtxt + 1);
-    m_states      = gralloc<State>(m_sRows);
-    m_sTable      = gralloc<State *>(m_sTransition * m_sColumns);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _states_cat(face.tele.starts);
+#endif
+    m_startStates = gralloc<uint16>(m_maxPreCtxt - m_minPreCtxt + 1);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::set_category(face.tele.states);
+#endif
+    m_states      = gralloc<State>(m_numStates);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::set_category(face.tele.transitions);
+#endif
+    m_transitions      = gralloc<uint16>(m_numTransition * m_numColumns);
 
-    if (!m_startStates || !m_states || !m_sTable) return false;
+    if (!m_startStates || !m_states || !m_transitions) return false;
     // load start states
-    for (State * * s = m_startStates,
-            * * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s)
+    for (uint16 * s = m_startStates,
+                * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s)
     {
-        *s = m_states + be::read<uint16>(starts);
-        if (*s < m_states || *s >= m_states + m_sRows) return false; // true;
+        *s = be::read<uint16>(starts);
+        if (*s >= m_numStates) return false; // true;
     }
 
     // load state transition table.
-    for (State * * t = m_sTable,
-               * * const t_end = t + m_sTransition*m_sColumns; t != t_end; ++t)
+    for (uint16 * t = m_transitions,
+                * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t)
     {
-        *t = m_states + be::read<uint16>(states);
-        if (*t < m_states || *t >= m_states + m_sRows) return false;
+        *t = be::read<uint16>(states);
+        if (*t >= m_numStates) return false;
     }
 
     State * s = m_states,
-          * const transitions_end = m_states + m_sTransition,
-          * const success_begin = m_states + m_sRows - m_sSuccess;
-    const RuleEntry * rule_map_end = m_ruleMap + be::peek<uint16>(o_rule_map + m_sSuccess*sizeof(uint16));
-    for (size_t n = m_sRows; n; --n, ++s)
+          * const success_begin = m_states + m_numStates - m_numSuccess;
+    const RuleEntry * rule_map_end = m_ruleMap + be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
+    for (size_t n = m_numStates; n; --n, ++s)
     {
-        s->transitions = s < transitions_end ? m_sTable + (s-m_states)*m_sColumns : 0;
         RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read<uint16>(o_rule_map),
                   * const end   = s < success_begin ? 0 : m_ruleMap + be::peek<uint16>(o_rule_map);
 
         if (begin >= rule_map_end || end > rule_map_end || begin > end)
             return false;
         s->rules = begin;
         s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
             begin + FiniteStateMachine::MAX_RULES;
@@ -274,17 +285,17 @@ bool Pass::readRanges(const byte * range
     m_cols = gralloc<uint16>(m_numGlyphs);
     memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16));
     for (size_t n = num_ranges; n; --n)
     {
         uint16     * ci     = m_cols + be::read<uint16>(ranges),
                    * ci_end = m_cols + be::read<uint16>(ranges) + 1,
                      col    = be::read<uint16>(ranges);
 
-        if (ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_sColumns)
+        if (ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns)
             return false;
 
         // A glyph must only belong to one column at a time
         while (ci != ci_end && *ci == 0xffff)
             *ci++ = col;
 
         if (ci != ci_end)
             return false;
@@ -317,41 +328,40 @@ void Pass::runGraphite(Machine & m, Fini
         	}
         	lc = m_iMaxLoop;
             if (s)
             	m.slotMap().highwater(s->next());
         }
     } while (s);
 }
 
-inline uint16 Pass::glyphToCol(const uint16 gid) const
-{
-    return gid < m_numGlyphs ? m_cols[gid] : 0xffffU;
-}
-
 bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const
 {
 	fsm.reset(slot, m_maxPreCtxt);
     if (fsm.slots.context() < m_minPreCtxt)
         return false;
 
-    const State * state = m_startStates[m_maxPreCtxt - fsm.slots.context()];
+    uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()];
+    uint8  free_slots = SlotMap::MAX_SLOTS;
     do
     {
         fsm.slots.pushSlot(slot);
-        if (fsm.slots.size() >= SlotMap::MAX_SLOTS) return false;
-        const uint16 col = glyphToCol(slot->gid());
-        if (col == 0xffffU || !state->is_transition()) return true;
+        if (--free_slots == 0
+         || slot->gid() >= m_numGlyphs
+         || m_cols[slot->gid()] == 0xffffU
+         || state >= m_numTransition)
+            return free_slots != 0;
 
-        state = state->transitions[col];
-        if (state->is_success())
-            fsm.rules.accumulate_rules(*state);
+        const uint16 * transitions = m_transitions + state*m_numColumns;
+        state = transitions[m_cols[slot->gid()]];
+        if (state >= m_successStart)
+            fsm.rules.accumulate_rules(m_states[state]);
 
         slot = slot->next();
-    } while (state != m_states && slot);
+    } while (state != 0 && slot);
 
     fsm.slots.pushSlot(slot);
     return true;
 }
 
 #if !defined GRAPHITE2_NTRACING
 
 inline
--- a/gfx/graphite2/src/Segment.cpp
+++ b/gfx/graphite2/src/Segment.cpp
@@ -47,16 +47,17 @@ Segment::Segment(unsigned int numchars, 
   m_charinfo(new CharInfo[numchars]),
   m_face(face),
   m_silf(face->chooseSilf(script)),
   m_first(NULL),
   m_last(NULL),
   m_bufSize(numchars + 10),
   m_numGlyphs(numchars),
   m_numCharinfo(numchars),
+  m_passBits(m_silf->aPassBits() ? -1 : 0),
   m_defaultOriginal(0),
   m_dir(textDir)
 {
     freeSlot(newSlot());
     m_bufSize = log_binary(numchars)+1;
 }
 
 Segment::~Segment()
@@ -123,16 +124,17 @@ void Segment::append(const Segment &othe
     pNewCharInfo += m_numCharinfo ;
     for (unsigned int i=0 ; i<m_numCharinfo ; ++i)
         pNewCharInfo[i] = other.m_charinfo[i];
  
     m_numCharinfo += other.m_numCharinfo;
     m_numGlyphs += other.m_numGlyphs;
     m_advance = m_advance + other.m_advance;
     m_bbox = m_bbox.widen(bbox);
+    m_passBits &= other.passBits();
 }
 #endif // GRAPHITE2_NSEGCACHE
 
 void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
 {
     Slot *aSlot = newSlot();
     
     m_charinfo[id].init(cid);
@@ -145,16 +147,19 @@ void Segment::appendSlot(int id, int cid
     aSlot->setGlyph(this, gid, theGlyph);
     aSlot->originate(id);
     aSlot->before(id);
     aSlot->after(id);
     if (m_last) m_last->next(aSlot);
     aSlot->prev(m_last);
     m_last = aSlot;
     if (!m_first) m_first = aSlot;
+    if (theGlyph && m_silf->aPassBits())
+        m_passBits &= theGlyph->attrs()[m_silf->aPassBits()] 
+                    | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
 }
 
 Slot *Segment::newSlot()
 {
     if (!m_freeSlots)
     {
         int numUser = m_silf->numUser();
 #if !defined GRAPHITE2_NTRACING
@@ -180,16 +185,23 @@ Slot *Segment::newSlot()
     res->next(NULL);
     return res;
 }
 
 void Segment::freeSlot(Slot *aSlot)
 {
     if (m_last == aSlot) m_last = aSlot->prev();
     if (m_first == aSlot) m_first = aSlot->next();
+    if (aSlot->attachedTo())
+        aSlot->attachedTo()->removeChild(aSlot);
+    while (aSlot->firstChild())
+    {
+        aSlot->firstChild()->attachTo(NULL);
+        aSlot->removeChild(aSlot->firstChild());
+    }
     // reset the slot incase it is reused
     ::new (aSlot) Slot;
     memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
     // Update generation counter for debug
 #if !defined GRAPHITE2_NTRACING
     if (m_face->logger())
         ++aSlot->userAttrs()[m_silf->numUser()];
 #endif
--- a/gfx/graphite2/src/Silf.cpp
+++ b/gfx/graphite2/src/Silf.cpp
@@ -50,16 +50,17 @@ Silf::Silf() throw()
   m_jPass(0),
   m_bPass(0),
   m_flags(0),
   m_aPseudo(0),
   m_aBreak(0),
   m_aUser(0),
   m_aBidi(0),
   m_aMirror(0),
+  m_aPassBits(0),
   m_iMaxComp(0),
   m_aLig(0),
   m_numPseudo(0),
   m_nClass(0),
   m_nLinear(0),
   m_gEndLine(0)
 {
     memset(&m_silfinfo, 0, sizeof m_silfinfo);
@@ -106,17 +107,17 @@ bool Silf::readGraphite(const byte * con
     m_jPass     = be::read<uint8>(p);
     m_bPass     = be::read<uint8>(p);
     m_flags     = be::read<uint8>(p);
     be::skip<uint8>(p,2); //  max{Pre,Post}Context.
     m_aPseudo   = be::read<uint8>(p);
     m_aBreak    = be::read<uint8>(p);
     m_aBidi     = be::read<uint8>(p);
     m_aMirror   = be::read<uint8>(p);
-    be::skip<byte>(p);     // skip reserved stuff
+    m_aPassBits = be::read<uint8>(p);
 
     // Read Justification levels.
     m_numJusts  = be::read<uint8>(p);
     if (maxGlyph >= face.glyphs().numGlyphs()
         || p + m_numJusts * 8 >= silf_end)  { releaseBuffers(); return false; }
     m_justs = gralloc<Justinfo>(m_numJusts);
     for (uint8 i = 0; i < m_numJusts; i++)
     {
@@ -369,17 +370,18 @@ bool Silf::runGraphite(Segment *seg, uin
     		seg->positionSlots(0);
     		for(Slot * s = seg->first(); s; s = s->next())
     			*dbgout		<< dslot(seg, s);
     		*dbgout			<< json::close;
     	}
 #endif
 
         // test whether to reorder, prepare for positioning
-        m_passes[i].runGraphite(m, fsm);
+        if (i >= 32 || (seg->passBits() & (1 << i)) == 0)
+            m_passes[i].runGraphite(m, fsm);
         // only subsitution passes can change segment length, cached subsegments are short for their text
         if (m.status() != vm::Machine::finished
         	|| (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR
             || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))))
             return false;
     }
     return true;
 }
--- a/gfx/graphite2/src/Slot.cpp
+++ b/gfx/graphite2/src/Slot.cpp
@@ -82,17 +82,17 @@ void Slot::update(int /*numGrSlots*/, in
     m_after += numCharInfo;
     m_position = m_position + relpos;
 }
 
 Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin)
 {
     if (attrLevel && m_attLevel > attrLevel) return Position(0, 0);
     float scale = 1.0;
-    Position shift = m_shift + Position(m_just, 0);
+    Position shift(m_shift.x * ((seg->dir() & 1) * -2 + 1) + m_just, m_shift.y);
     float tAdvance = m_advance.x + m_just;
     const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph());
     if (font)
     {
         scale = font->scale();
         shift *= scale;
         if (font->isHinted() && glyphFace)
             tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(m_glyphid);
@@ -139,17 +139,17 @@ Position Slot::finalise(const Segment *s
         Position adj = Position(base.x - clusterMin, 0.);
         res += adj;
         m_position += adj;
         if (m_child) m_child->floodShift(adj);
     }
     return res;
 }
 
-uint32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
+int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
 {
     Position base;
     Rect bbox = seg->theGlyphBBoxTemporary(gid());
     float clusterMin = 0.;
     Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin);
 
     switch (metrics(metric))
     {
@@ -242,17 +242,19 @@ void Slot::setAttr(Segment *seg, attrCod
     case gr_slatAdvX :	m_advance.x = value; break;
     case gr_slatAdvY :	m_advance.y = value; break;
     case gr_slatAttTo :
     {
         const uint16 idx = uint16(value);
         if (idx < map.size() && map[idx])
         {
             Slot *other = map[idx];
-            if (other != this && other->child(this))
+            if (other == this) break;
+            if (m_parent) m_parent->removeChild(this);
+            if (other->child(this))
             {
                 attachTo(other);
                 if (((seg->dir() & 1) != 0) ^ (idx > subindex))
                     m_with = Position(advance(), 0);
                 else        // normal match to previous root
                     m_attach = Position(other->advance(), 0);
             }
         }
@@ -308,17 +310,17 @@ int Slot::getJustify(const Segment *seg,
         case 3 : return seg->glyphAttr(gid(), jAttrs->attrWeight());
         case 4 : return 0;      // not been set yet, so clearly 0
         default: return 0;
     }
 }
 
 void Slot::setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value)
 {
-    if (level >= seg->silf()->numJustLevels()) return;
+    if (level && level >= seg->silf()->numJustLevels()) return;
     if (!m_justs)
     {
         SlotJustify *j = seg->newJustify();
         j->LoadSlot(this, seg);
         m_justs = j;
     }
     m_justs->values[level * SlotJustify::NUMJUSTPARAMS + subindex] = value;
 }
@@ -340,16 +342,44 @@ bool Slot::sibling(Slot *ap)
     else if (ap == m_sibling) return true;
     else if (!m_sibling || !ap)
         m_sibling = ap;
     else
         return m_sibling->sibling(ap);
     return true;
 }
 
+bool Slot::removeChild(Slot *ap)
+{
+    if (this == ap || !m_child) return false;
+    else if (ap == m_child)
+    {
+        Slot *nSibling = m_child->nextSibling();
+        m_child->sibling(NULL);
+        m_child = nSibling;
+        return true;
+    }
+    else
+        return m_child->removeSibling(ap);
+    return true;
+}
+
+bool Slot::removeSibling(Slot *ap)
+{
+    if (this == ap || !m_sibling) return false;
+    else if (ap == m_sibling)
+    {
+        m_sibling = m_sibling->nextSibling();
+        return true;
+    }
+    else
+        return m_sibling->removeSibling(ap);
+    return true;
+}
+
 void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
 {
     m_glyphid = glyphid;
     if (!theGlyph)
     {
         theGlyph = seg->getFace()->glyphs().glyphSafe(glyphid);
         if (!theGlyph)
         {
@@ -360,16 +390,18 @@ void Slot::setGlyph(Segment *seg, uint16
     }
     m_realglyphid = theGlyph->attrs()[seg->silf()->aPseudo()];
     if (m_realglyphid)
     {
         const GlyphFace *aGlyph = seg->getFace()->glyphs().glyphSafe(m_realglyphid);
         if (aGlyph) theGlyph = aGlyph;
     }
     m_advance = Position(theGlyph->theAdvance().x, 0.);
+    if (seg->silf()->aPassBits())
+        seg->mergePassBits(theGlyph->attrs()[seg->silf()->aPassBits()]);
 }
 
 void Slot::floodShift(Position adj)
 {
     m_position += adj;
     if (m_child) m_child->floodShift(adj);
     if (m_sibling) m_sibling->floodShift(adj);
 }
--- a/gfx/graphite2/src/gr_face.cpp
+++ b/gfx/graphite2/src/gr_face.cpp
@@ -33,16 +33,19 @@ of the License or (at your option) any l
 #include "inc/Silf.h"
 
 using namespace graphite2;
 
 namespace
 {
     bool load_face(Face & face, unsigned int options)
     {
+#ifdef GRAPHITE2_TELEMETRY
+        telemetry::category _misc_cat(face.tele.misc);
+#endif
         Face::Table silf(face, Tag::Silf);
         if (silf)   options &= ~gr_face_dumbRendering;
         else if (!(options &  gr_face_dumbRendering))
             return false;
 
         if (!face.readGlyphs(options))
             return false;
 
--- a/gfx/graphite2/src/gr_logging.cpp
+++ b/gfx/graphite2/src/gr_logging.cpp
@@ -33,17 +33,16 @@ of the License or (at your option) any l
 #include "inc/Segment.h"
 
 #if defined _WIN32
 #include "windows.h"
 #endif
 
 using namespace graphite2;
 
-
 extern "C" {
 
 
 bool gr_start_logging(gr_face * face, const char *log_path)
 {
 	if (!face || !log_path)	return false;
 
 #if !defined GRAPHITE2_NTRACING
@@ -53,29 +52,33 @@ bool gr_start_logging(gr_face * face, co
 	if (n == 0 || n > MAX_PATH - 12) return false;
 
 	LPWSTR wlog_path = gralloc<WCHAR>(n);
 	FILE *log = 0;
 	if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n))
 		log = _wfopen(wlog_path, L"wt");
 
 	free(wlog_path);
-#else
+#else   // _WIN32
 	FILE *log = fopen(log_path, "wt");
-#endif
+#endif  // _WIN32
 	if (!log)	return false;
 
 	face->setLogger(log);
 	if (!face->logger()) return false;
 
 	*face->logger() << json::array;
+#ifdef GRAPHITE2_TELEMETRY
+    *face->logger() << face->tele;
+#endif
+
 	return true;
-#else
+#else   // GRAPHITE2_NTRACING
 	return false;
-#endif
+#endif  // GRAPHITE2_NTRACING
 }
 
 bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */)
 {
 //#if !defined GRAPHITE2_NTRACING
 //	graphite_stop_logging();
 //
 //    if (!log) return false;
@@ -107,18 +110,47 @@ void gr_stop_logging(gr_face * face)
 void graphite_stop_logging()
 {
 //    if (dbgout) delete dbgout;
 //    dbgout = 0;
 }
 
 } // extern "C"
 
+#ifdef GRAPHITE2_TELEMETRY
+size_t   * graphite2::telemetry::_category = 0UL;
+#endif
+
 #if !defined GRAPHITE2_NTRACING
 
+#ifdef GRAPHITE2_TELEMETRY
+
+json & graphite2::operator << (json & j, const telemetry & t) throw()
+{
+    j << json::object
+            << "type"   << "telemetry"
+            << "silf"   << t.silf
+            << "states" << t.states
+            << "starts" << t.starts
+            << "transitions" << t.transitions
+            << "glyphs" << t.glyph
+            << "code"   << t.code
+            << "misc"   << t.misc
+            << "total"  << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc)
+        << json::close;
+    return j;
+}
+#else
+json & graphite2::operator << (json & j, const telemetry &) throw()
+{
+    return j;
+}
+#endif
+
+
 json & graphite2::operator << (json & j, const CharInfo & ci) throw()
 {
 	return j << json::object
 				<< "offset"			<< ci.base()
 				<< "unicode"		<< ci.unicodeChar()
 				<< "break"			<< ci.breakWeight()
 				<< "flags"          << ci.flags()
 				<< "slot" << json::flat << json::object
--- a/gfx/graphite2/src/inc/Face.h
+++ b/gfx/graphite2/src/inc/Face.h
@@ -38,16 +38,17 @@ of the License or (at your option) any l
 namespace graphite2 {
 
 class Cmap;
 class FileFace;
 class GlyphCache;
 class NameTable;
 class json;
 
+
 using TtfUtil::Tag;
 
 // These are the actual tags, as distinct from the consecutive IDs in TtfUtil.h
 
 class Face
 {
     // Prevent any kind of copying
     Face(const Face&);
@@ -98,16 +99,20 @@ private:
     mutable NameTable     * m_pNames;
     mutable json          * m_logger;
 protected:
     Silf                  * m_silfs;    // silf subtables.
     uint16                  m_numSilf;  // num silf subtables in the silf table
 private:
     uint16 m_ascent,
            m_descent;
+#ifdef GRAPHITE2_TELEMETRY
+public:
+    mutable telemetry   tele;
+#endif
 };
 
 
 
 inline
 const SillMap & Face::theSill() const
 {
     return m_Sill;
--- a/gfx/graphite2/src/inc/Machine.h
+++ b/gfx/graphite2/src/inc/Machine.h
@@ -31,17 +31,17 @@ of the License or (at your option) any l
 // interface.
 
 #pragma once
 #include <cstring>
 #include <graphite2/Types.h>
 #include "inc/Main.h"
 
 #if defined(__GNUC__)
-#if defined(__clang__)
+#if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430
 #define     HOT
 #if defined(__x86_64)
 #define     REGPARM(n)      __attribute__((regparm(n)))
 #else
 #define     REGPARM(n)
 #endif
 #else
 #define     HOT             __attribute__((hot))
--- a/gfx/graphite2/src/inc/Main.h
+++ b/gfx/graphite2/src/inc/Main.h
@@ -39,25 +39,65 @@ typedef gr_uint8        uint8;
 typedef gr_uint8        byte;
 typedef gr_uint16       uint16;
 typedef gr_uint32       uint32;
 typedef gr_int8         int8;
 typedef gr_int16        int16;
 typedef gr_int32        int32;
 typedef size_t          uintptr;
 
+#if GRAPHITE2_TELEMETRY
+struct telemetry
+{
+    class category;
+
+    static size_t   * _category;
+    static void set_category(size_t & t) throw()    { _category = &t; }
+    static void stop() throw()                      { _category = 0; }
+    static void count_bytes(size_t n) throw()       { if (_category) *_category += n; }
+
+    size_t  misc,
+            silf,
+            glyph,
+            code,
+            states,
+            starts,
+            transitions;
+
+    telemetry() : misc(0), silf(0), glyph(0), code(0), states(0), starts(0), transitions(0) {}
+};
+
+class telemetry::category
+{
+    size_t * _prev;
+public:
+    category(size_t & t) : _prev(_category) { _category = &t; }
+    ~category() { _category = _prev; }
+};
+
+#else
+struct telemetry  {};
+#endif
+
 // typesafe wrapper around malloc for simple types
 // use free(pointer) to deallocate
+
 template <typename T> T * gralloc(size_t n)
 {
+#if GRAPHITE2_TELEMETRY
+    telemetry::count_bytes(sizeof(T) * n);
+#endif
     return reinterpret_cast<T*>(malloc(sizeof(T) * n));
 }
 
 template <typename T> T * grzeroalloc(size_t n)
 {
+#if GRAPHITE2_TELEMETRY
+    telemetry::count_bytes(sizeof(T) * n);
+#endif
     return reinterpret_cast<T*>(calloc(n, sizeof(T)));
 }
 
 template <typename T>
 inline T min(const T a, const T b)
 {
     return a < b ? a : b;
 }
@@ -66,19 +106,19 @@ template <typename T>
 inline T max(const T a, const T b)
 {
     return a > b ? a : b;
 }
 
 } // namespace graphite2
 
 #define CLASS_NEW_DELETE \
-    void * operator new   (size_t size){ return malloc(size);} \
+    void * operator new   (size_t size){ return gralloc<byte>(size);} \
     void * operator new   (size_t, void * p) throw() { return p; } \
-    void * operator new[] (size_t size) {return malloc(size);} \
+    void * operator new[] (size_t size) {return gralloc<byte>(size);} \
     void * operator new[] (size_t, void * p) throw() { return p; } \
     void operator delete   (void * p) throw() { free(p);} \
     void operator delete   (void *, void *) throw() {} \
     void operator delete[] (void * p)throw() { free(p); } \
     void operator delete[] (void *, void *) throw() {}
 
 #ifdef __GNUC__
 #define GR_MAYBE_UNUSED __attribute__((unused))
--- a/gfx/graphite2/src/inc/Pass.h
+++ b/gfx/graphite2/src/inc/Pass.h
@@ -56,39 +56,40 @@ private:
     int   	doAction(const vm::Machine::Code* codeptr, Slot * & slot_out, vm::Machine &) const;
     bool   	testPassConstraint(vm::Machine & m) const;
     bool   	testConstraint(const Rule & r, vm::Machine &) const;
     bool   	readRules(const byte * rule_map, const size_t num_entries,
                      const byte *precontext, const uint16 * sort_key,
                      const uint16 * o_constraint, const byte *constraint_data, 
                      const uint16 * o_action, const byte * action_data,
                      const Face &);
-    bool   	readStates(const byte * starts, const byte * states, const byte * o_rule_map);
+    bool   	readStates(const byte * starts, const byte * states, const byte * o_rule_map, const Face &);
     bool   	readRanges(const byte * ranges, size_t num_ranges);
     uint16 	glyphToCol(const uint16 gid) const;
     bool   	runFSM(FiniteStateMachine & fsm, Slot * slot) const;
     void	dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const;
     void	dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const;
     void	adjustSlot(int delta, Slot * & slot_out, SlotMap &) const;
     const Silf* m_silf;
     uint16    * m_cols;
     Rule      * m_rules; // rules
     RuleEntry * m_ruleMap;
-    State *   * m_startStates; // prectxt length
-    State *   * m_sTable;
+    uint16    * m_startStates; // prectxt length
+    uint16    * m_transitions;
     State     * m_states;
     
     byte   m_flags;
     byte   m_iMaxLoop;
     uint16 m_numGlyphs;
     uint16 m_numRules;
-    uint16 m_sRows;
-    uint16 m_sTransition;
-    uint16 m_sSuccess;
-    uint16 m_sColumns;
+    uint16 m_numStates;
+    uint16 m_numTransition;
+    uint16 m_numSuccess;
+    uint16 m_successStart;
+    uint16 m_numColumns;
     byte m_minPreCtxt;
     byte m_maxPreCtxt;
     vm::Machine::Code m_cPConstraint;
     
 private:		//defensive
     Pass(const Pass&);
     Pass& operator=(const Pass&);
 };
--- a/gfx/graphite2/src/inc/Rule.h
+++ b/gfx/graphite2/src/inc/Rule.h
@@ -59,56 +59,41 @@ inline Rule::~Rule()
 
 
 struct RuleEntry
 {
   const Rule   * rule;
 
   inline
   bool operator < (const RuleEntry &r) const
-  { 
-    const unsigned short lsort = rule->sort, rsort = r.rule->sort; 
+  {
+    const unsigned short lsort = rule->sort, rsort = r.rule->sort;
     return lsort > rsort || (lsort == rsort && rule < r.rule);
   }
-  
+
   inline
   bool operator == (const RuleEntry &r) const
   {
     return rule == r.rule;
   }
 };
 
 
 struct State
 {
   const RuleEntry     * rules,
                       * rules_end;
-  const State * const * transitions;
   
-  size_t size() const;
-  bool   is_success() const;
-  bool   is_transition() const;
+  bool   empty() const;
 };
 
 inline
-size_t State::size() const
-{
-  return rules_end - rules;
-}
-
-inline
-bool State::is_success() const
+bool State::empty() const
 {
-  return (rules != NULL);
-}
-
-inline
-bool State::is_transition() const
-{
-  return (transitions != NULL);
+    return rules_end == rules;
 }
 
 
 class SlotMap
 {
 public:
   enum {MAX_SLOTS=64};
   SlotMap(Segment & seg);
@@ -217,37 +202,38 @@ size_t FiniteStateMachine::Rules::size()
 {
   return m_end - m_begin;
 }
 
 inline
 void FiniteStateMachine::Rules::accumulate_rules(const State &state)
 {
   // Only bother if there are rules in the State object.
-  if (state.size() == 0) return;
+  if (state.empty()) return;
   
   // Merge the new sorted rules list into the current sorted result set.
   const RuleEntry * lre = begin(), * rre = state.rules;
   RuleEntry * out = m_rules + (m_begin == m_rules)*MAX_RULES;    
-  const RuleEntry * lrend = out + MAX_RULES;
+  const RuleEntry * const lrend = out + MAX_RULES,
+                  * const rrend = state.rules_end;
   m_begin = out; 
   while (lre != end() && out != lrend)
   {
     if (*lre < *rre)      *out++ = *lre++;
     else if (*rre < *lre) { *out++ = *rre++; }
     else                { *out++ = *lre++; ++rre; }
 
-    if (rre == state.rules_end) 
+    if (rre == rrend)
     { 
       while (lre != end() && out != lrend) { *out++ = *lre++; }
       m_end = out;
       return;
     }
   }
-  while (rre != state.rules_end && out != lrend) { *out++ = *rre++; }
+  while (rre != rrend && out != lrend) { *out++ = *rre++; }
   m_end = out;
 }
 
 inline
 SlotMap::SlotMap(Segment & seg)
 : segment(seg), m_size(0), m_precontext(0), m_highwater(0), m_highpassed(false)
 {
     m_slot_map[0] = 0;
@@ -284,17 +270,17 @@ void SlotMap::reset(Slot & slot, short u
   m_size = 0;
   m_precontext = ctxt;
   *m_slot_map = slot.prev();
 }
 
 inline
 void SlotMap::pushSlot(Slot*const slot)
 {
-  m_slot_map[m_size++ + 1] = slot;
+  m_slot_map[++m_size] = slot;
 }
 
 inline
 Slot * const & SlotMap::operator[](int n) const
 {
   return m_slot_map[n + 1];
 }
 
--- a/gfx/graphite2/src/inc/Segment.h
+++ b/gfx/graphite2/src/inc/Segment.h
@@ -118,18 +118,20 @@ public:
     Position positionSlots(const Font *font, Slot *first=0, Slot *last=0);
     void associateChars();
     void linkClusters(Slot *first, Slot *last);
     uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); }
     uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); }
     int addFeatures(const Features& feats) { m_feats.push_back(feats); return m_feats.size() - 1; }
     uint32 getFeature(int index, uint8 findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); }
     void dir(int8 val) { m_dir = val; }
-    uint16 glyphAttr(uint16 gid, uint16 gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; }
-    uint16 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const;
+    unsigned int passBits() const { return m_passBits; }
+    void mergePassBits(const unsigned int val) { m_passBits &= val; }
+    int16 glyphAttr(uint16 gid, uint16 gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; }
+    int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const;
     float glyphAdvance(uint16 gid) const { return m_face->glyphs().glyph(gid)->theAdvance().x; }
     const Rect &theGlyphBBoxTemporary(uint16 gid) const { return m_face->glyphs().glyph(gid)->theBBox(); }   //warning value may become invalid when another glyph is accessed
     Slot *findRoot(Slot *is) const { return is->attachedTo() ? findRoot(is->attachedTo()) : is; }
     int numAttrs() const { return m_silf->numUser(); }
     int defaultOriginal() const { return m_defaultOriginal; }
     const Face * getFace() const { return m_face; }
     const Features & getFeatures(unsigned int /*charIndex*/) { assert(m_feats.size() == 1); return m_feats[0]; }
     void bidiPass(uint8 aBidi, int paradir, uint8 aMirror);
@@ -158,17 +160,18 @@ private:
     SlotJustify   * m_freeJustifies;    // Slot justification blocks free list
     CharInfo      * m_charinfo;         // character info, one per input character
     const Face    * m_face;             // GrFace
     const Silf    * m_silf;
     Slot          * m_first;            // first slot in segment
     Slot          * m_last;             // last slot in segment
     unsigned int    m_bufSize,          // how big a buffer to create when need more slots
                     m_numGlyphs,
-                    m_numCharinfo;      // size of the array and number of input characters
+                    m_numCharinfo,      // size of the array and number of input characters
+                    m_passBits;         // if bit set then skip pass
     int             m_defaultOriginal;  // number of whitespace chars in the string
     int8            m_dir;
 };
 
 
 
 inline
 void Segment::finalise(const Font *font)
@@ -176,17 +179,17 @@ void Segment::finalise(const Font *font)
 	if (!m_first) return;
 
     m_advance = positionSlots(font);
     associateChars();
     linkClusters(m_first, m_last);
 }
 
 inline
-uint16 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const {
+int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const {
     if (attrLevel > 0)
     {
         Slot *is = findRoot(iSlot);
         return is->clusterMetric(this, metric, attrLevel);
     }
     else
         return m_face->getGlyphMetric(iSlot->gid(), metric);
 }
--- a/gfx/graphite2/src/inc/Silf.h
+++ b/gfx/graphite2/src/inc/Silf.h
@@ -77,16 +77,17 @@ public:
     bool runGraphite(Segment *seg, uint8 firstPass=0, uint8 lastPass=0) const;
     uint16 findClassIndex(uint16 cid, uint16 gid) const;
     uint16 getClassGlyph(uint16 cid, unsigned int index) const;
     uint16 findPseudo(uint32 uid) const;
     uint8 numUser() const { return m_aUser; }
     uint8 aPseudo() const { return m_aPseudo; }
     uint8 aBreak() const { return m_aBreak; }
     uint8 aMirror() const {return m_aMirror; }
+    uint8 aPassBits() const { return m_aPassBits; }
     uint8 substitutionPass() const { return m_sPass; }
     uint8 positionPass() const { return m_pPass; }
     uint8 justificationPass() const { return m_jPass; }
     uint8 bidiPass() const { return m_bPass; }
     uint8 numPasses() const { return m_numPasses; }
     uint8 maxCompPerLig() const { return m_iMaxComp; }
     uint16 numClasses() const { return m_nClass; }
     byte  flags() const { return m_flags; }
@@ -106,17 +107,17 @@ private:
     uint32        * m_classOffsets;
     uint16        * m_classData;
     Justinfo      * m_justs;
     uint8           m_numPasses;
     uint8           m_numJusts;
     uint8           m_sPass, m_pPass, m_jPass, m_bPass,
                     m_flags;
 
-    uint8   m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror,
+    uint8   m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
             m_iMaxComp;
     uint16  m_aLig,
             m_numPseudo,
             m_nClass,
             m_nLinear,
             m_gEndLine;
     gr_faceinfo m_silfinfo;
     
--- a/gfx/graphite2/src/inc/Slot.h
+++ b/gfx/graphite2/src/inc/Slot.h
@@ -120,17 +120,19 @@ public:
     bool isLocalJustify() const { return m_justs != NULL; };
     void attachTo(Slot *ap) { m_parent = ap; }
     Slot *attachedTo() const { return m_parent; }
     Position attachOffset() const { return m_attach - m_with; }
     Slot* firstChild() const { return m_child; }
     bool child(Slot *ap);
     Slot* nextSibling() const { return m_sibling; }
     bool sibling(Slot *ap);
-    uint32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel);
+    bool removeChild(Slot *ap);
+    bool removeSibling(Slot *ap);
+    int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel);
     void positionShift(Position a) { m_position += a; }
     void floodShift(Position adj);
     float just() const { return m_just; }
     void just(float j) { m_just = j; }
 
     CLASS_NEW_DELETE
 
 private:
--- a/gfx/graphite2/src/inc/debug.h
+++ b/gfx/graphite2/src/inc/debug.h
@@ -47,20 +47,23 @@ class Slot;
 typedef std::pair<const Segment * const, const Slot * const>	dslot;
 struct objectid
 {
 	char name[16];
 	objectid(const dslot &) throw();
 	objectid(const Segment * const p) throw();
 };
 
+
 json & operator << (json & j, const Position &) throw();
 json & operator << (json & j, const CharInfo &) throw();
 json & operator << (json & j, const dslot &) throw();
 json & operator << (json & j, const objectid &) throw();
+json & operator << (json & j, const telemetry &) throw();
+
 
 
 inline
 json & operator << (json & j, const Position & p) throw()
 {
 	return j << json::flat << json::array << p.x << p.y << json::close;
 }
 
--- a/gfx/graphite2/src/inc/opcodes.h
+++ b/gfx/graphite2/src/inc/opcodes.h
@@ -438,17 +438,17 @@ STARTOP(push_slot_attr)
 ENDOP
 
 STARTOP(push_glyph_attr_obs)
     declare_params(2);
     const unsigned int  glyph_attr = uint8(param[0]);
     const int           slot_ref   = int8(param[1]);
     slotref slot = slotat(slot_ref);
     if (slot)
-        push(seg.glyphAttr(slot->gid(), glyph_attr));
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
 ENDOP
 
 STARTOP(push_glyph_metric)
     declare_params(3);
     const unsigned int  glyph_attr  = uint8(param[0]);
     const int           slot_ref    = int8(param[1]);
     const signed int    attr_level  = uint8(param[2]);
     slotref slot = slotat(slot_ref);
@@ -472,31 +472,31 @@ STARTOP(push_att_to_gattr_obs)
     declare_params(2);
     const unsigned int  glyph_attr  = uint8(param[0]);
     const int           slot_ref    = int8(param[1]);
     slotref slot = slotat(slot_ref);
     if (slot)
     {
         slotref att = slot->attachedTo();
         if (att) slot = att;
-        push(seg.glyphAttr(slot->gid(), glyph_attr));
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
     }
 ENDOP
 
 STARTOP(push_att_to_glyph_metric)
     declare_params(3);
     const unsigned int  glyph_attr  = uint8(param[0]);
     const int           slot_ref    = int8(param[1]);
     const signed int    attr_level  = uint8(param[2]);
     slotref slot = slotat(slot_ref);
     if (slot)
     {
         slotref att = slot->attachedTo();
         if (att) slot = att;
-        push(seg.getGlyphMetric(slot, glyph_attr, attr_level));
+        push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level)));
     }
 ENDOP
 
 STARTOP(push_islot_attr)
     declare_params(3);
     const attrCode	slat     = attrCode(uint8(param[0]));
     const int           slot_ref = int8(param[1]),
                         idx      = uint8(param[2]);
@@ -611,30 +611,30 @@ ENDOP
 
 STARTOP(push_glyph_attr)
     declare_params(3);
     const unsigned int  glyph_attr  = uint8(param[0]) << 8
                                     | uint8(param[1]);
     const int           slot_ref    = int8(param[2]);
     slotref slot = slotat(slot_ref);
     if (slot)
-        push(seg.glyphAttr(slot->gid(), glyph_attr));
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
 ENDOP
 
 STARTOP(push_att_to_glyph_attr)
     declare_params(3);
     const unsigned int  glyph_attr  = uint8(param[0]) << 8
                                     | uint8(param[1]);
     const int           slot_ref    = int8(param[2]);
     slotref slot = slotat(slot_ref);
     if (slot)
     {
         slotref att = slot->attachedTo();
         if (att) slot = att;
-        push(seg.glyphAttr(slot->gid(), glyph_attr));
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
     }
 ENDOP
 
 STARTOP(temp_copy)
     slotref newSlot = seg.newSlot();
     int16 *tempUserAttrs = newSlot->userAttrs();
     memcpy(newSlot, is, sizeof(Slot));
     memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16));
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -5,16 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "mozilla/layers/PLayerTransaction.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/Telemetry.h"
+#include "CompositableHost.h"
 
 #include "ImageLayers.h"
 #include "ImageContainer.h"
 #include "Layers.h"
 #include "gfxPlatform.h"
 #include "ReadbackLayer.h"
 #include "gfxUtils.h"
 #include "nsPrintfCString.h"
@@ -976,16 +977,19 @@ LayerManager::BeginTabSwitch()
 #ifdef MOZ_LAYERS_HAVE_LOG
 
 static nsACString& PrintInfo(nsACString& aTo, LayerComposite* aLayerComposite);
 
 #ifdef MOZ_DUMP_PAINTING
 template <typename T>
 void WriteSnapshotLinkToDumpFile(T* aObj, FILE* aFile)
 {
+  if (!aObj) {
+    return;
+  }
   nsCString string(aObj->Name());
   string.Append("-");
   string.AppendInt((uint64_t)aObj);
   fprintf(aFile, "href=\"javascript:ViewImage('%s')\"", string.BeginReading());
 }
 
 template <typename T>
 void WriteSnapshotToDumpFile_internal(T* aObj, gfxASurface* aSurf)
@@ -1024,33 +1028,36 @@ Layer::Dump(FILE* aFile, const char* aPr
 #ifdef MOZ_DUMP_PAINTING
     if (GetType() == TYPE_CONTAINER || GetType() == TYPE_THEBES) {
       WriteSnapshotLinkToDumpFile(this, aFile);
     }
 #endif
     fprintf(aFile, ">");
   }
   DumpSelf(aFile, aPrefix);
+  if (AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) {
+    AsLayerComposite()->GetCompositableHost()->Dump(aFile, aPrefix, aDumpHtml);
+  }
   if (aDumpHtml) {
     fprintf(aFile, "</a>");
   }
 
   if (Layer* mask = GetMaskLayer()) {
     nsAutoCString pfx(aPrefix);
     pfx += "  Mask layer: ";
-    mask->Dump(aFile, pfx.get());
+    mask->Dump(aFile, pfx.get(), aDumpHtml);
   }
 
   if (Layer* kid = GetFirstChild()) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
     if (aDumpHtml) {
       fprintf(aFile, "<ul>");
     }
-    kid->Dump(aFile, pfx.get());
+    kid->Dump(aFile, pfx.get(), aDumpHtml);
     if (aDumpHtml) {
       fprintf(aFile, "</ul>");
     }
   }
 
   if (aDumpHtml) {
     fprintf(aFile, "</li>");
   }
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -140,16 +140,20 @@ public:
       return;
     }
 
     Update(nsIntRegion(), nsIntRegion());
     mResolution = aResolution;
   }
   bool IsLowPrecision() const { return mResolution < 1; }
 
+  typedef Tile* Iterator;
+  Iterator TilesBegin() { return mRetainedTiles.Elements(); }
+  Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
+
 protected:
   // The implementor should call Update() to change
   // the new valid region. This implementation will call
   // validateTile on each tile that is dirty, which is left
   // to the implementor.
   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
 
   nsIntRegion     mValidRegion;
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -54,16 +54,24 @@ protected:
       mSurface = mCompositor->GetDrawTarget()->CreateSourceSurfaceFromData(mThebesImage->Data(),
                                                                            mSize,
                                                                            mThebesImage->Stride(),
                                                                            mFormat);
     }
     return true;
   }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE {
+    if (!mThebesImage) {
+      mThebesImage = mThebesSurface->GetAsImageSurface();
+    }
+    nsRefPtr<gfxImageSurface> result = mThebesImage;
+    return result.forget();
+  }
+
   BasicCompositor *mCompositor;
   RefPtr<SourceSurface> mSurface;
   nsRefPtr<gfxImageSurface> mThebesImage;
   nsRefPtr<gfxASurface> mThebesSurface;
   IntSize mSize;
 };
 
 TemporaryRef<TextureHost>
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -47,17 +47,17 @@ CanvasClient2D::CanvasClient2D(Composita
 {
   mTextureInfo.mCompositableType = BUFFER_IMAGE_SINGLE;
 }
 
 void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   if (!mTextureClient) {
-    mTextureClient = CreateTextureClient(TEXTURE_SHMEM);
+    mTextureClient = CreateTextureClient(TEXTURE_CONTENT);
     MOZ_ASSERT(mTextureClient, "Failed to create texture client");
   }
 
   bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
   gfxASurface::gfxContentType contentType = isOpaque
                                               ? gfxASurface::CONTENT_COLOR
                                               : gfxASurface::CONTENT_COLOR_ALPHA;
   mTextureClient->EnsureAllocated(aSize, contentType);
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -6,16 +6,17 @@
 #include "ipc/AutoOpenSurface.h"
 #include "mozilla/layers/PLayerTransaction.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/CompositorTypes.h" // for TextureInfo
 #include "mozilla/layers/Effects.h"
 
 #include "CanvasLayerComposite.h"
 #include "ImageHost.h"
+#include "gfxUtils.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
 CanvasLayerComposite::CanvasLayerComposite(LayerManagerComposite* aManager)
   : CanvasLayer(aManager, nullptr)
   , LayerComposite(aManager)
@@ -56,16 +57,23 @@ CanvasLayerComposite::RenderLayer(const 
                                   const nsIntRect& aClipRect)
 {
   if (!mImageHost) {
     return;
   }
 
   mCompositor->MakeCurrent();
 
+#ifdef MOZ_DUMP_PAINTING
+  if (gfxUtils::sDumpPainting) {
+    nsRefPtr<gfxImageSurface> surf = mImageHost->GetAsSurface();
+    WriteSnapshotToDumpFile(this, surf);
+  }
+#endif
+
   gfxPattern::GraphicsFilter filter = mFilter;
 #ifdef ANDROID
   // Bug 691354
   // Using the LINEAR filter we get unexplained artifacts.
   // Use NEAREST when no scaling is required.
   gfxMatrix matrix;
   bool is2D = GetEffectiveTransform().Is2D(&matrix);
   if (is2D && !matrix.HasNonTranslationOrFlip()) {
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -73,16 +73,29 @@ CompositableHost::Create(const TextureIn
     return result;
   default:
     MOZ_NOT_REACHED("Unknown CompositableType");
     return nullptr;
   }
 }
 
 void
+CompositableHost::DumpTextureHost(FILE* aFile, TextureHost* aTexture)
+{
+  if (!aTexture) {
+    return;
+  }
+  nsRefPtr<gfxImageSurface> surf = aTexture->GetAsSurface();
+  if (!surf) {
+    return;
+  }
+  surf->DumpAsDataURL(aFile ? aFile : stderr);
+}
+
+void
 CompositableParent::ActorDestroy(ActorDestroyReason why)
 {
   if (mHost) {
     mHost->Detach();
   }
 }
 
 CompositableParent::CompositableParent(CompositableParentManager* aMgr,
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -189,16 +189,25 @@ public:
     SetCompositor(aCompositor);
     SetLayer(aLayer);
   }
   void Detach() {
     SetLayer(nullptr);
     SetCompositor(nullptr);
   }
 
+  virtual void Dump(FILE* aFile=NULL,
+                    const char* aPrefix="",
+                    bool aDumpHtml=false) { }
+  static void DumpTextureHost(FILE* aFile, TextureHost* aTexture);
+
+#ifdef MOZ_DUMP_PAINTING
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() { return nullptr; }
+#endif
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix) { }
 #endif
 
 protected:
   TextureInfo mTextureInfo;
   Compositor* mCompositor;
   Layer* mLayer;
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -202,16 +202,45 @@ ContentHostBase::SetCompositor(Composito
   if (mTextureHost) {
     mTextureHost->SetCompositor(aCompositor);
   }
   if (mTextureHostOnWhite) {
     mTextureHostOnWhite->SetCompositor(aCompositor);
   }
 }
 
+void
+ContentHostBase::Dump(FILE* aFile,
+                      const char* aPrefix,
+                      bool aDumpHtml)
+{
+  if (!aFile) {
+    aFile = stderr;
+  }
+  if (aDumpHtml) {
+    fprintf(aFile, "<ul>");
+  }
+  if (mTextureHost) {
+    fprintf(aFile, aPrefix);
+    fprintf(aFile, aDumpHtml ? "<li> <a href=" : "Front buffer: ");
+    DumpTextureHost(aFile, mTextureHost);
+    fprintf(aFile, aDumpHtml ? "> Front buffer </a></li> " : " ");
+  }
+  if (mTextureHostOnWhite) {
+    fprintf(aFile, aPrefix);
+    fprintf(aFile, aDumpHtml ? "<li> <a href=" : "TextureHost on white: ");
+    DumpTextureHost(aFile, mTextureHostOnWhite);
+    fprintf(aFile, aDumpHtml ? "> Front buffer on white </a> </li> " : " ");
+  }
+  if (aDumpHtml) {
+    fprintf(aFile, "</ul>");
+  }
+
+}
+
 ContentHostSingleBuffered::~ContentHostSingleBuffered()
 {
   DestroyTextures();
   DestroyFrontHost();
 }
 
 void
 ContentHostSingleBuffered::EnsureTextureHost(TextureIdentifier aTextureId,
@@ -689,11 +718,40 @@ ContentHostDoubleBuffered::PrintInfo(nsA
 
   if (mBackHost) {
     aTo += "\n";
     mBackHost->PrintInfo(aTo, prefix.get());
   }
 }
 #endif
 
+void
+ContentHostDoubleBuffered::Dump(FILE* aFile,
+                                const char* aPrefix,
+                                bool aDumpHtml)
+{
+  ContentHostBase::Dump(aFile, aPrefix, aDumpHtml);
+  if (!aFile) {
+    aFile = stderr;
+  }
+  if (aDumpHtml) {
+    fprintf(aFile, "<ul>");
+  }
+  if (mBackHost) {
+    fprintf(aFile, aPrefix);
+    fprintf(aFile, aDumpHtml ? "<li> <a href=" : "Back buffer: ");
+    DumpTextureHost(aFile, mBackHost);
+    fprintf(aFile, aDumpHtml ? " >Back buffer</a></li>" : " ");
+  }
+  if (mBackHostOnWhite) {
+    fprintf(aFile, aPrefix);
+    fprintf(aFile, aDumpHtml ? "<li> <a href=" : "Back buffer on white: ");
+    DumpTextureHost(aFile, mBackHostOnWhite);
+    fprintf(aFile, aDumpHtml ? " >Back buffer on white</a> </li>" : " ");
+  }
+  if (aDumpHtml) {
+    fprintf(aFile, "</ul>");
+  }
+
+}
 
 } // namespace
 } // namespace
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -29,20 +29,16 @@ public:
   // tiling.
   virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; }
 
   virtual void UpdateThebes(const ThebesBufferData& aData,
                             const nsIntRegion& aUpdated,
                             const nsIntRegion& aOldValidRegionBack,
                             nsIntRegion* aUpdatedRegionBack) = 0;
 
-#ifdef MOZ_DUMP_PAINTING
-  virtual already_AddRefed<gfxImageSurface> Dump() { return nullptr; }
-#endif
-  
   virtual void SetPaintWillResample(bool aResample) { }
 
 protected:
   ContentHost(const TextureInfo& aTextureInfo)
     : CompositableHost(aTextureInfo)
   {}
 };
 
@@ -88,22 +84,26 @@ public:
     result.mFlags = (mBufferRotation != nsIntPoint()) ?
                     LAYER_RENDER_STATE_BUFFER_ROTATION : 0;
     return result;
   }
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
 #ifdef MOZ_DUMP_PAINTING
-  virtual already_AddRefed<gfxImageSurface> Dump()
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface()
   {
-    return mTextureHost->Dump();
+    return mTextureHost->GetAsSurface();
   }
 #endif
 
+  virtual void Dump(FILE* aFile=NULL,
+                    const char* aPrefix="",
+                    bool aDumpHtml=false) MOZ_OVERRIDE;
+
   virtual TextureHost* GetTextureHost() MOZ_OVERRIDE;
 
   virtual void SetPaintWillResample(bool aResample) { mPaintWillResample = aResample; }
   // The client has destroyed its texture clients and we should destroy our
   // texture hosts and SurfaceDescriptors. Note that we don't immediately
   // destroy our front buffer so that we can continue to composite.
   virtual void DestroyTextures() = 0;
 
@@ -152,16 +152,20 @@ public:
                             nsIntRegion* aUpdatedRegionBack);
 
   virtual void EnsureTextureHost(TextureIdentifier aTextureId,
                                  const SurfaceDescriptor& aSurface,
                                  ISurfaceAllocator* aAllocator,
                                  const TextureInfo& aTextureInfo) MOZ_OVERRIDE;
   virtual void DestroyTextures() MOZ_OVERRIDE;
 
+  virtual void Dump(FILE* aFile=NULL,
+                    const char* aPrefix="",
+                    bool aDumpHtml=false) MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix);
 #endif
 protected:
   nsIntRegion mValidRegionForNextBackBuffer;
   // Texture host for the back buffer. We never read or write this buffer. We
   // only swap it with the front buffer (mTextureHost) when we are told by the
   // content thread.
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -175,10 +175,27 @@ ImageHostBuffered::MakeTextureHost(Textu
                                    aSurface,
                                    aAllocator,
                                    aTextureInfo);
   if (mTextureHost) {
     mTextureHost->SetBuffer(new SurfaceDescriptor(null_t()), aAllocator);
   }
 }
 
+void
+ImageHostSingle::Dump(FILE* aFile,
+                      const char* aPrefix,
+                      bool aDumpHtml)
+{
+  if (!aFile) {
+    aFile = stderr;
+  }
+  if (mTextureHost) {
+    fprintf(aFile, aPrefix);
+    fprintf(aFile, aDumpHtml ? "<ul><li>TextureHost: "
+                             : "TextureHost: ");
+    DumpTextureHost(aFile, mTextureHost);
+    fprintf(aFile, aDumpHtml ? " </li></ul> " : " ");
+  }
+}
+
 }
 }
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -78,20 +78,31 @@ public:
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE
   {
     return mTextureHost->GetRenderState();
   }
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
+  virtual void Dump(FILE* aFile=NULL,
+                    const char* aPrefix="",
+                    bool aDumpHtml=false) MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix);
 #endif
 
+#ifdef MOZ_DUMP_PAINTING
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE
+  {
+    return mTextureHost->GetAsSurface();
+  }
+#endif
+
 protected:
   virtual void MakeTextureHost(TextureIdentifier aTextureId,
                                const SurfaceDescriptor& aSurface,
                                ISurfaceAllocator* aAllocator,
                                const TextureInfo& aTextureInfo);
 
   RefPtr<TextureHost> mTextureHost;
   nsIntRect mPictureRect;
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -5,16 +5,17 @@
 
 #include "gfxSharedImageSurface.h"
 
 #include "ipc/AutoOpenSurface.h"
 #include "ImageLayerComposite.h"
 #include "ImageHost.h"
 #include "gfxImageSurface.h"
 #include "gfx2DGlue.h"
+#include "gfxUtils.h"
 
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorTypes.h" // for TextureInfo
 #include "mozilla/layers/Effects.h"
 #include "CompositableHost.h"
 
 using namespace mozilla::gfx;
 
@@ -68,16 +69,23 @@ ImageLayerComposite::GetLayer()
 void
 ImageLayerComposite::RenderLayer(const nsIntPoint& aOffset,
                                  const nsIntRect& aClipRect)
 {
   if (!mImageHost) {
     return;
   }
 
+#ifdef MOZ_DUMP_PAINTING
+  if (gfxUtils::sDumpPainting) {
+    nsRefPtr<gfxImageSurface> surf = mImageHost->GetAsSurface();
+    WriteSnapshotToDumpFile(this, surf);
+  }
+#endif
+
   mCompositor->MakeCurrent();
 
   EffectChain effectChain;
   LayerManagerComposite::AddMaskEffect(mMaskLayer, effectChain);
 
   gfx::Matrix4x4 transform;
   ToMatrix4x4(GetEffectiveTransform(), transform);
   gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -223,35 +223,33 @@ public:
    */
   virtual void SetCompositor(Compositor* aCompositor) {}
 
   ISurfaceAllocator* GetDeAllocator()
   {
     return mDeAllocator;
   }
 
-#ifdef MOZ_DUMP_PAINTING
-  virtual already_AddRefed<gfxImageSurface> Dump() { return nullptr; }
-#endif
-
   bool operator== (const TextureHost& o) const
   {
     return GetIdentifier() == o.GetIdentifier();
   }
   bool operator!= (const TextureHost& o) const
   {
     return GetIdentifier() != o.GetIdentifier();
   }
 
   LayerRenderState GetRenderState()
   {
     return LayerRenderState(mBuffer,
                             mFlags & NeedsYFlip ? LAYER_RENDER_STATE_Y_FLIPPED : 0);
   }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() = 0;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char *Name() = 0;
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix);
 #endif
 
   /**
    * TEMPORARY.
    *
--- a/gfx/layers/composite/ThebesLayerComposite.cpp
+++ b/gfx/layers/composite/ThebesLayerComposite.cpp
@@ -96,17 +96,17 @@ ThebesLayerComposite::RenderLayer(const 
   }
 
   gfx::Matrix4x4 transform;
   ToMatrix4x4(GetEffectiveTransform(), transform);
   gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxUtils::sDumpPainting) {
-    nsRefPtr<gfxImageSurface> surf = mBuffer->Dump();
+    nsRefPtr<gfxImageSurface> surf = mBuffer->GetAsSurface();
     WriteSnapshotToDumpFile(this, surf);
   }
 #endif
 
   EffectChain effectChain;
   LayerManagerComposite::AddMaskEffect(mMaskLayer, effectChain);
 
   nsIntRegion visibleRegion = GetEffectiveVisibleRegion();
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -307,11 +307,35 @@ void
 TiledContentHost::PrintInfo(nsACString& aTo, const char* aPrefix)
 {
   aTo += aPrefix;
   aTo += nsPrintfCString("TiledContentHost (0x%p)", this);
 
 }
 #endif
 
+void
+TiledContentHost::Dump(FILE* aFile,
+                       const char* aPrefix,
+                       bool aDumpHtml)
+{
+  if (!aFile) {
+    aFile = stderr;
+  }
+
+  TiledLayerBufferComposite::Iterator it = mVideoMemoryTiledBuffer.TilesBegin();
+  TiledLayerBufferComposite::Iterator stop = mVideoMemoryTiledBuffer.TilesEnd();
+  if (aDumpHtml) {
+    fprintf(aFile, "<ul>");
+  }
+  for (;it != stop; ++it) {
+    fprintf(aFile, aPrefix);
+    fprintf(aFile, aDumpHtml ? "<li> <a href=" : "Tile ");
+    DumpTextureHost(aFile, it->mTextureHost);
+    fprintf(aFile, aDumpHtml ? " >Tile</a></li>" : " ");
+  }
+    if (aDumpHtml) {
+    fprintf(aFile, "</ul>");
+  }
+}
 
 } // namespace
 } // namespace
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -61,16 +61,17 @@ public:
 };
 
 class TiledLayerBufferComposite
   : public TiledLayerBuffer<TiledLayerBufferComposite, TiledTexture>
 {
   friend class TiledLayerBuffer<TiledLayerBufferComposite, TiledTexture>;
 
 public:
+  typedef TiledLayerBuffer<TiledLayerBufferComposite, TiledTexture>::Iterator Iterator;
   TiledLayerBufferComposite()
     : mCompositor(nullptr)
   {}
 
   void Upload(const BasicTiledLayerBuffer* aMainMemoryTiledBuffer,
               const nsIntRegion& aNewValidRegion,
               const nsIntRegion& aInvalidateRegion,
               const gfxSize& aResolution);
@@ -196,16 +197,20 @@ public:
   {
     CompositableHost::SetCompositor(aCompositor);
     mVideoMemoryTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionVideoMemoryTiledBuffer.SetCompositor(aCompositor);
   }
 
   virtual void Attach(Layer* aLayer, Compositor* aCompositor) MOZ_OVERRIDE;
 
+  virtual void Dump(FILE* aFile=NULL,
+                    const char* aPrefix="",
+                    bool aDumpHtml=false) MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix);
 #endif
 
 private:
   void ProcessUploadQueue(nsIntRegion* aNewValidRegion,
                           TiledLayerProperties* aLayerProperties);
   void ProcessLowPrecisionUploadQueue();
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -117,16 +117,21 @@ public:
   }
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual LayerRenderState GetRenderState() { return LayerRenderState(); }
 
   virtual bool Lock() MOZ_OVERRIDE { return true; }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE
+  {
+    return nullptr; // TODO: cf bug 872568
+  }
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "TextureHostShmemD3D11"; }
 #endif
 
   virtual void BeginTileIteration() MOZ_OVERRIDE {
     mIterating = true;
     mCurrentTile = 0;
   }
@@ -170,16 +175,21 @@ public:
 
   virtual TextureSourceD3D11* AsSourceD3D11() MOZ_OVERRIDE { return this; }
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual bool Lock() MOZ_OVERRIDE;
   virtual void Unlock() MOZ_OVERRIDE;
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE
+  {
+    return nullptr; // TODO: cf bug 872568
+  }
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "TextureHostDXGID3D11"; }
 #endif
 
 protected:
   virtual void UpdateImpl(const SurfaceDescriptor& aSurface,
                           nsIntRegion* aRegion,
                           nsIntPoint* aOffset = nullptr) MOZ_OVERRIDE;
@@ -205,16 +215,21 @@ public:
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   virtual TextureSourceD3D11* AsSourceD3D11() MOZ_OVERRIDE { return this; }
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual bool IsYCbCrSource() const MOZ_OVERRIDE { return true; }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE
+  {
+    return nullptr; // TODO: cf bug 872568
+  }
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() MOZ_OVERRIDE { return "TextureImageTextureHostD3D11"; }
 #endif
 
 protected:
   virtual void UpdateImpl(const SurfaceDescriptor& aSurface,
                           nsIntRegion* aRegion,
                           nsIntPoint* aOffset = nullptr) MOZ_OVERRIDE;
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -136,17 +136,16 @@ TextureImageTextureHostOGL::GetSize() co
       nsIntRect rect = mTexture->GetTileRect();
       return gfx::IntSize(rect.width, rect.height);
     }
     return gfx::IntSize(mTexture->GetSize().width, mTexture->GetSize().height);
   }
   return gfx::IntSize(0, 0);
 }
 
-
 void
 TextureImageTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
   CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   GLContext* newGL = glCompositor ? glCompositor->gl() : nullptr;
   if (mGL != newGL) {
     mGL = newGL;
     mTexture = nullptr;
@@ -229,17 +228,17 @@ TextureImageTextureHostOGL::UpdateImpl(c
     mTexture->EndUpdate();
   }
 }
 
 bool
 TextureImageTextureHostOGL::Lock()
 {
   if (!mTexture) {
-    NS_WARNING("TextureImageAsTextureHost to be composited without texture");
+    NS_WARNING("TextureImageTextureHost to be composited without texture");
     return false;
   }
 
   NS_ASSERTION(mTexture->GetContentType() != gfxASurface::CONTENT_ALPHA,
                 "Image layer has alpha image");
 
   mFormat = FormatFromShaderType(mTexture->GetShaderProgramType());
 
@@ -872,10 +871,72 @@ GrallocTextureHostOGL::SetBuffer(Surface
 
   // only done for hacky fix in gecko 23 for bug 862324.
   // Doing this in SwapTextures is not enough, as the crash could occur right after SetBuffer.
   RegisterTextureHostAtGrallocBufferActor(this, *mBuffer);
 }
 
 #endif
 
+already_AddRefed<gfxImageSurface>
+TextureImageTextureHostOGL::GetAsSurface() {
+  nsRefPtr<gfxImageSurface> surf = IsValid() ?
+    mGL->GetTexImage(mTexture->GetTextureID(),
+                     false,
+                     mTexture->GetShaderProgramType())
+    : nullptr;
+  return surf.forget();
+}
+
+already_AddRefed<gfxImageSurface>
+YCbCrTextureHostOGL::GetAsSurface() {
+  nsRefPtr<gfxImageSurface> surf = IsValid() ?
+    mGL->GetTexImage(mYTexture->mTexImage->GetTextureID(),
+                     false,
+                     mYTexture->mTexImage->GetShaderProgramType())
+    : nullptr;
+  return surf.forget();
+}
+
+already_AddRefed<gfxImageSurface>
+SharedTextureHostOGL::GetAsSurface() {
+  nsRefPtr<gfxImageSurface> surf = IsValid() ?
+    mGL->GetTexImage(GetTextureHandle(),
+                     false,
+                     GetShaderProgram())
+    : nullptr;
+  return surf.forget();
+}
+
+already_AddRefed<gfxImageSurface>
+SurfaceStreamHostOGL::GetAsSurface() {
+  nsRefPtr<gfxImageSurface> surf = IsValid() ?
+    mGL->GetTexImage(mTextureHandle,
+                     false,
+                     GetShaderProgram())
+    : nullptr;
+  return surf.forget();
+}
+
+already_AddRefed<gfxImageSurface>
+TiledTextureHostOGL::GetAsSurface() {
+  nsRefPtr<gfxImageSurface> surf = IsValid() ?
+    mGL->GetTexImage(mTextureHandle,
+                     false,
+                     GetShaderProgram())
+    : nullptr;
+  return surf.forget();
+}
+
+#ifdef MOZ_WIDGET_GONK
+already_AddRefed<gfxImageSurface>
+GrallocTextureHostOGL::GetAsSurface() {
+  nsRefPtr<gfxImageSurface> surf = IsValid() && mGLTexture ?
+    mGL->GetTexImage(mGLTexture,
+                     false,
+                     GetShaderProgram())
+    : nullptr;
+  return surf.forget();
+}
+#endif
+
 } // namespace
 } // namespace
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -126,16 +126,18 @@ public:
 
   bool IsValid() const MOZ_OVERRIDE
   {
     return !!mTexture;
   }
 
   virtual bool Lock() MOZ_OVERRIDE;
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
+
   // textureSource
   void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE
   {
     mTexture->BindTexture(aTextureUnit);
   }
 
   gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
@@ -267,17 +269,16 @@ public:
     virtual gfx::IntSize GetSize() const MOZ_OVERRIDE
     {
       return gfx::IntSize(mTexImage->GetSize().width, mTexImage->GetSize().height);
     }
     virtual GLenum GetWrapMode() const MOZ_OVERRIDE
     {
       return mTexImage->GetWrapMode();
     }
-
   };
 
   // TextureSource implementation
 
   TextureSource* GetSubSource(int index) MOZ_OVERRIDE
   {
     switch (index) {
       case 0 : return mYTexture.get();
@@ -291,16 +292,18 @@ public:
   {
     if (!mYTexture->mTexImage) {
       NS_WARNING("YCbCrTextureHost::GetSize called but no data has been set yet");
       return gfx::IntSize(0,0);
     }
     return mYTexture->GetSize();
   }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "YCbCrTextureHostOGL"; }
 #endif
 
 private:
   RefPtr<Channel> mYTexture;
   RefPtr<Channel> mCbTexture;
   RefPtr<Channel> mCrTexture;
@@ -375,16 +378,18 @@ public:
   {
     return (mFormat == gfx::FORMAT_B8G8R8A8) ?
              gfxASurface::CONTENT_COLOR_ALPHA :
              gfxASurface::CONTENT_COLOR;
   }
 
   virtual gfx3DMatrix GetTextureTransform() MOZ_OVERRIDE;
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "SharedTextureHostOGL"; }
 #endif
 
 protected:
   void DeleteTextures();
 
   gfx::IntSize mSize;
@@ -458,16 +463,18 @@ public:
   }
   GLuint GetTextureID() { return mTextureHandle; }
   ContentType GetContentType() {
     return (mFormat == gfx::FORMAT_B8G8R8A8) ?
              gfxASurface::CONTENT_COLOR_ALPHA :
              gfxASurface::CONTENT_COLOR;
   }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "SurfaceStreamHostOGL"; }
 #endif
 
   SurfaceStreamHostOGL()
     : mGL(nullptr)
     , mTextureHandle(0)
     , mUploadTexture(0)
@@ -519,16 +526,18 @@ public:
   {
     return GetProgramTypeForTexture(this);
   }
 
   virtual void SwapTexturesImpl(const SurfaceDescriptor& aImage,
                                 nsIntRegion* aRegion = nullptr)
   { MOZ_ASSERT(false, "Tiles should not use this path"); }
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "TiledTextureHostOGL"; }
 #endif
 
 protected:
   void DeleteTextures();
 
   virtual uint64_t GetIdentifier() const MOZ_OVERRIDE {
@@ -602,16 +611,18 @@ public:
 
   GLenum GetWrapMode() const MOZ_OVERRIDE
   {
     return LOCAL_GL_CLAMP_TO_EDGE;
   }
 
   bool IsValid() const MOZ_OVERRIDE;
 
+  virtual already_AddRefed<gfxImageSurface> GetAsSurface() MOZ_OVERRIDE;
+
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "GrallocTextureHostOGL"; }
 #endif
 
   void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const;
 
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -699,17 +699,16 @@ gfxASurface::BytesPerPixel(gfxImageForma
       return 1; // Close enough
     case ImageFormatUnknown:
     default:
       NS_NOTREACHED("Not really sure what you want me to say here");
       return 0;
   }
 }
 
-#ifdef MOZ_DUMP_IMAGES
 void
 gfxASurface::WriteAsPNG(const char* aFile)
 {
     FILE *file = fopen(aFile, "wb");
     if (file) {
       WriteAsPNG_internal(file, true);
       fclose(file);
     } else {
@@ -879,10 +878,8 @@ gfxASurface::WriteAsPNG_internal(FILE* a
     nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
     if (clipboard) {
       clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr);
     }
   }
 
   return;
 }
-#endif
-
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -237,17 +237,16 @@ public:
      * process's heap.
      */
     virtual MemoryLocation GetMemoryLocation() const;
 
     static int32_t BytePerPixelFromFormat(gfxImageFormat format);
 
     virtual const gfxIntSize GetSize() const { return gfxIntSize(-1, -1); }
 
-#ifdef MOZ_DUMP_IMAGES
     /**
      * Debug functions to encode the current image as a PNG and export it.
      */
 
     /**
      * Writes a binary PNG file.
      */
     void WriteAsPNG(const char* aFile);
@@ -263,17 +262,16 @@ public:
     void PrintAsDataURL();
 
     /**
      * Copy a PNG encoded Data URL to the clipboard.
      */
     void CopyAsDataURL();
     
     void WriteAsPNG_internal(FILE* aFile, bool aBinary);
-#endif
 
     void SetOpaqueRect(const gfxRect& aRect) {
         if (aRect.IsEmpty()) {
             mOpaqueRect = nullptr;
         } else if (mOpaqueRect) {
             *mOpaqueRect = aRect;
         } else {
             mOpaqueRect = new gfxRect(aRect);
--- a/ipc/chromium/src/base/thread.cc
+++ b/ipc/chromium/src/base/thread.cc
@@ -132,17 +132,18 @@ void Thread::StopSoon() {
   // most likely means that the thread terminated unexpectedly, probably due
   // to someone calling Quit() on our message loop directly.
   DCHECK(message_loop_);
 
   message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
 }
 
 void Thread::ThreadMain() {
-  profiler_register_thread(name_.c_str());
+  char aLocal;
+  profiler_register_thread(name_.c_str(), &aLocal);
 
   // The message loop for this thread.
   MessageLoop message_loop(startup_data_->options.message_loop_type);
 
   // Complete the initialization of our Thread object.
   thread_id_ = PlatformThread::CurrentId();
   PlatformThread::SetName(name_.c_str());
   message_loop.set_thread_name(name_);
--- a/js/src/ion/BaselineCompiler.cpp
+++ b/js/src/ion/BaselineCompiler.cpp
@@ -619,16 +619,23 @@ BaselineCompiler::emit_JSOP_NOTEARG()
 bool
 BaselineCompiler::emit_JSOP_POP()
 {
     frame.pop();
     return true;
 }
 
 bool
+BaselineCompiler::emit_JSOP_POPN()
+{
+    frame.popn(GET_UINT16(pc));
+    return true;
+}
+
+bool
 BaselineCompiler::emit_JSOP_DUP()
 {
     // Keep top stack value in R0, sync the rest so that we can use R1. We use
     // separate registers because every register can be used by at most one
     // StackValue.
     frame.popRegsAndSync(1);
     masm.moveValue(R0, R1);
 
@@ -2388,16 +2395,27 @@ BaselineCompiler::emit_JSOP_SETRVAL()
     // Store to the frame's return value slot.
     storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2);
     masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
     frame.pop();
     return true;
 }
 
 bool
+BaselineCompiler::emit_JSOP_CALLEE()
+{
+    JS_ASSERT(function());
+    frame.syncStack(0);
+    masm.loadPtr(frame.addressOfCallee(), R0.scratchReg());
+    masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0);
+    frame.push(R0);
+    return true;
+}
+
+bool
 BaselineCompiler::emit_JSOP_POPV()
 {
     return emit_JSOP_SETRVAL();
 }
 
 typedef bool (*NewArgumentsObjectFn)(JSContext *, BaselineFrame *, MutableHandleValue);
 static const VMFunction NewArgumentsObjectInfo =
     FunctionInfo<NewArgumentsObjectFn>(ion::NewArgumentsObject);
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -30,16 +30,17 @@
 namespace js {
 namespace ion {
 
 #define OPCODE_LIST(_)         \
     _(JSOP_NOP)                \
     _(JSOP_LABEL)              \
     _(JSOP_NOTEARG)            \
     _(JSOP_POP)                \
+    _(JSOP_POPN)               \
     _(JSOP_DUP)                \
     _(JSOP_DUP2)               \
     _(JSOP_SWAP)               \
     _(JSOP_PICK)               \
     _(JSOP_GOTO)               \
     _(JSOP_IFEQ)               \
     _(JSOP_IFNE)               \
     _(JSOP_AND)                \
@@ -154,16 +155,17 @@ namespace ion {
     _(JSOP_ARGUMENTS)          \
     _(JSOP_REST)               \
     _(JSOP_TOID)               \
     _(JSOP_TABLESWITCH)        \
     _(JSOP_ITER)               \
     _(JSOP_MOREITER)           \
     _(JSOP_ITERNEXT)           \
     _(JSOP_ENDITER)            \
+    _(JSOP_CALLEE)             \
     _(JSOP_POPV)               \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETURN)             \
     _(JSOP_STOP)               \
     _(JSOP_RETRVAL)
 
 class BaselineCompiler : public BaselineCompilerSpecific
 {
--- a/js/src/ion/BaselineInspector.cpp
+++ b/js/src/ion/BaselineInspector.cpp
@@ -36,16 +36,30 @@ SetElemICInspector::sawOOBTypedArrayWrit
             continue;
         if (stub->toSetElem_TypedArray()->expectOutOfBounds())
             return true;
     }
     return false;
 }
 
 bool
+SetElemICInspector::sawDenseWrite() const
+{
+    if (!icEntry_)
+        return false;
+
+    // Check for a SetElem_DenseAdd or SetElem_Dense stub.
+    for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
+        if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense())
+            return true;
+    }
+    return false;
+}
+
+bool
 BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, Vector<Shape *> &shapes)
 {
     // Return a list of shapes seen by the baseline IC for the current op.
     // An empty list indicates no shapes are known, or there was an uncacheable
     // access.
     JS_ASSERT(shapes.empty());
 
     if (!hasBaselineScript())
--- a/js/src/ion/BaselineInspector.h
+++ b/js/src/ion/BaselineInspector.h
@@ -36,16 +36,17 @@ class SetElemICInspector : public ICInsp
 
   public:
     SetElemICInspector(BaselineInspector *inspector, jsbytecode *pc, ICEntry *icEntry)
       : ICInspector(inspector, pc, icEntry)
     { }
 
     bool sawOOBDenseWrite() const;
     bool sawOOBTypedArrayWrite() const;
+    bool sawDenseWrite() const;
 };
 
 class BaselineInspector
 {
   private:
     RootedScript script;
     ICEntry *prevLookedUpEntry;
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -1239,16 +1239,21 @@ IonBuilder::inspectOpcode(JSOp op)
         // POP opcodes frequently appear where values are killed, e.g. after
         // SET* opcodes. Place a resume point afterwards to avoid capturing
         // the dead value in later snapshots, except in places where that
         // resume point is obviously unnecessary.
         if (pc[JSOP_POP_LENGTH] == JSOP_POP)
             return true;
         return maybeInsertResume();
 
+      case JSOP_POPN:
+        for (uint32_t i = 0, n = GET_UINT16(pc); i < n; i++)
+            current->pop();
+        return true;
+
       case JSOP_NEWINIT:
       {
         if (GET_UINT8(pc) == JSProto_Array)
             return jsop_newarray(0);
         RootedObject baseObj(cx, NULL);
         return jsop_newobject(baseObj);
       }
 
@@ -6610,16 +6615,23 @@ IonBuilder::jsop_setelem()
             break;
 
         if (!index->mightBeType(MIRType_Int32) &&
             !index->mightBeType(MIRType_String))
         {
             break;
         }
 
+        // TODO: Bug 876650: remove this check:
+        // Temporary disable the cache if non dense native,
+        // untill the cache supports more ics
+        SetElemICInspector icInspect(inspector->setElemICInspector(pc));
+        if (!icInspect.sawDenseWrite())
+            break;
+
         MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict);
         current->add(ins);
         current->push(value);
 
         return resumeAfter(ins);
     } while (false);
 
     MInstruction *ins = MCallSetElement::New(object, index, value);
@@ -6840,25 +6852,27 @@ IonBuilder::jsop_length_fastPath()
 {
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
 
     if (types->getKnownTypeTag() != JSVAL_TYPE_INT32)
         return false;
 
     MDefinition *obj = current->peek(-1);
 
-    if (obj->type() == MIRType_String) {
+    if (obj->mightBeType(MIRType_String)) {
+        if (obj->mightBeType(MIRType_Object))
+            return false;
         current->pop();
         MStringLength *ins = MStringLength::New(obj);
         current->add(ins);
         current->push(ins);
         return true;
     }
 
-    if (obj->type() == MIRType_Object) {
+    if (obj->mightBeType(MIRType_Object)) {
         types::StackTypeSet *objTypes = obj->resultTypeSet();
 
         if (objTypes &&
             objTypes->getKnownClass() == &ArrayClass &&
             !objTypes->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
         {
             current->pop();
             MElements *elements = MElements::New(obj);
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -852,18 +852,18 @@ LIRGenerator::visitCompare(MCompare *com
 static void
 ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp)
 {
     MDefinition *lhs = *lhsp;
     MDefinition *rhs = *rhsp;
 
     // Ensure that if there is a constant, then it is in rhs.
     // In addition, since clobbering binary operations clobber the left
-    // operand, prefer a lhs operand with no further uses.
-    if (lhs->isConstant() || rhs->useCount() == 1) {
+    // operand, prefer a non-constant lhs operand with no further uses.
+    if (lhs->isConstant() || (!rhs->isConstant() && rhs->useCount() == 1)) {
         *rhsp = lhs;
         *lhsp = rhs;
     }
 }
 
 bool
 LIRGenerator::lowerBitOp(JSOp op, MInstruction *ins)
 {
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -186,16 +186,18 @@ ComparePolicy::adjustInputs(MInstruction
 
         switch (type) {
           case MIRType_Double: {
             MToDouble::ConversionKind convert = MToDouble::NumbersOnly;
             if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceLHS && i == 0)
                 convert = MToDouble::NonNullNonStringPrimitives;
             else if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceRHS && i == 1)
                 convert = MToDouble::NonNullNonStringPrimitives;
+            if (convert == MToDouble::NumbersOnly && in->type() == MIRType_Boolean)
+                in = boxAt(def, in);
             replace = MToDouble::New(in, convert);
             break;
           }
           case MIRType_Int32:
             replace = MToInt32::New(in);
             break;
           case MIRType_Object:
             replace = MUnbox::New(in, MIRType_Object, MUnbox::Infallible);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/callee.js
@@ -0,0 +1,5 @@
+ff = (function g() {
+    for (var i=0; i<15; i++) {}
+    return g;
+});
+assertEq(ff(), ff);
--- a/js/src/jit-test/tests/basic/bug522136.js
+++ b/js/src/jit-test/tests/basic/bug522136.js
@@ -2,9 +2,9 @@ var Q = 0;
 var thrown = false;
 try {
    (function f(i) { Q = i; if (i == 200000) return; f(i+1); })(1)
 } catch (e) {
     thrown = true;
 }
 
 // Exact behavior of recursion check depends on which JIT we use.
-assertEq(thrown && Q > 8000, true);
+assertEq(thrown && Q > 3500, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug876226.js
@@ -0,0 +1,11 @@
+// |jit-test| error: SyntaxError
+try {
+    evaluate("throw 3", {
+	newContext: new Set,
+	saveFrameChain: true
+    });
+} catch(e) {}
+
+evaluate("()", {
+    saveFrameChain: true
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Script-sourceStart-01.js
@@ -0,0 +1,22 @@
+/*
+ * Script.prototype.sourceStart and Script.prototype.sourceLength should both be
+ * a number.
+ */
+let g = newGlobal('new-compartment');
+let dbg = new Debugger(g);
+
+var count = 0;
+function test(string, range) {
+    dbg.onNewScript = function (script) {
+        ++count;
+        assertEq(script.sourceStart, range[0]);
+        assertEq(script.sourceLength, range[1]);
+    };
+
+    g.eval(string);
+};
+
+test("", [0, 0]);
+test("2 * 3", [0, 5]);
+test("2\n*\n3", [0, 5]);
+assertEq(count, 3);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Script-sourceStart-02.js
@@ -0,0 +1,32 @@
+/*
+ * For function statements, Script.prototype.sourceStart and
+ * Script.prototype.sourceLength should comprise both the opening '(' and the
+ * closing '}'.
+ */
+let g = newGlobal('new-compartment');
+let dbg = new Debugger(g);
+
+function test(string, ranges) {
+    var index = 0;
+    dbg.onNewScript = function (script) {
+        function traverse(script) {
+            script.getChildScripts().forEach(function (script) {
+                assertEq(script.sourceStart, ranges[index][0]);
+                assertEq(script.sourceLength, ranges[index][1]);
+                ++index;
+                traverse(script);
+            });
+        }
+        traverse(script);
+    };
+
+    g.eval(string);
+    assertEq(index, ranges.length);
+};
+
+test("function f() {}", [[10, 5]]);
+test("function f() { function g() {} }", [[10, 22], [25, 5]]);
+test("function f() { function g() { function h() {} } }", [[10, 39], [25, 22], [40, 5]]);
+test("function f() { if (true) function g() {} }", [[10, 32], [35, 5]]); 
+test("var o = { get p () {} }", [[16, 5]]);
+test("var o = { set p (x) {} }", [[16, 6]]);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Script-sourceStart-03.js
@@ -0,0 +1,35 @@
+/*
+ * For arrow functions, Script.prototype.sourceStart and
+ * Script.prototype.sourceLength should comprise the entire function expression
+ * (including arguments)
+ */
+let g = newGlobal('new-compartment');
+let dbg = new Debugger(g);
+
+function test(string, ranges) {
+    var index = 0;
+    dbg.onNewScript = function (script) {
+        function traverse(script) {
+            script.getChildScripts().forEach(function (script) {
+                assertEq(script.sourceStart, ranges[index][0]);
+                assertEq(script.sourceLength, ranges[index][1]);
+                ++index;
+                traverse(script);
+            });
+        }
+        traverse(script);
+    };
+
+    g.eval(string);
+
+    /*
+     * In some configurations certain child scripts are listed twice, so we
+     * cannot rely on index always having the exact same value
+     */
+    assertEq(0 < index && index <= ranges.length, true);
+};
+
+test("() => {}", [[0, 8]]);
+test("(x, y) => { x * y }", [[0, 19]]);
+test("x => x * x", [[0, 10]]);
+test("x => x => x * x", [[0, 15], [5, 10], [5, 10]]);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Script-sourceStart-04.js
@@ -0,0 +1,25 @@
+/*
+ * For eval and Function constructors, Script.prototype.sourceStart and
+ * Script.prototype.sourceLength should comprise the entire script (excluding
+ * arguments in the case of Function constructors)
+ */
+let g = newGlobal('new-compartment');
+let dbg = new Debugger(g);
+
+var count = 0;
+function test(string, range) {
+    dbg.onNewScript = function (script) {
+        ++count;
+        if (count % 2 == 0) {
+            assertEq(script.sourceStart, range[0]);
+            assertEq(script.sourceLength, range[1]);
+        }
+    }
+
+    g.eval(string);
+}
+
+test("eval('2 * 3')", [0, 5]);
+test("new Function('2 * 3')", [0, 5]);
+test("new Function('x', 'x * x')", [0, 5]);
+assertEq(count, 6);
copy from js/src/jit-test/tests/debug/Script-url.js
copy to js/src/jit-test/tests/debug/Source-url.js
--- a/js/src/jit-test/tests/debug/Script-url.js
+++ b/js/src/jit-test/tests/debug/Source-url.js
@@ -1,10 +1,10 @@
-// Script.prototype.url can be a string or null.
+// Source.prototype.url can be a string or null.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debugger;
 var gw = dbg.addDebuggee(g);
 for (var fileName of ['file:///var/foo.js', null]) {
     g.evaluate("function f(x) { return 2*x; }", {fileName: fileName});
     var fw = gw.getOwnPropertyDescriptor('f').value;
-    assertEq(fw.script.url, fileName);
+    assertEq(fw.script.source.url, fileName);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug867820.js
@@ -0,0 +1,18 @@
+
+function AddTestCase(expect, actual) {
+  new TestCase(expect, actual);
+}
+function TestCase(e, a) {
+  this.expect = e;
+  getTestCaseResult(e, a);
+}
+function getTestCaseResult(expected, actual) {
+  if (actual != expected) {}
+}
+AddRegExpCases(false, Math.pow(2,31));
+AddRegExpCases("", Math.pow(2,30) - 1);
+function AddRegExpCases(m, l) {
+  AddTestCase("");
+  AddTestCase(m, true);
+  AddTestCase(l, 0);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/popn.js
@@ -0,0 +1,11 @@
+function f() {
+    var t = 0;
+    for (var j = 0; j < 10; j++) {
+	for (var i = 0; i < 9; ++i) {
+            var [r, g, b] = [1, i, -10];
+            t += r + g + b;
+	}
+    }
+    return t;
+}
+assertEq(f(), -450);
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -3192,17 +3192,17 @@ JS_FRIEND_DATA(Class) js::FunctionProxyC
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     proxy_Finalize,          /* finalize */
     NULL,                    /* checkAccess */
     proxy_Call,
-    FunctionClass.hasInstance,
+    proxy_HasInstance,
     proxy_Construct,
     proxy_TraceFunction,     /* trace       */
     PROXY_CLASS_EXT,
     {
         proxy_LookupGeneric,
         proxy_LookupProperty,
         proxy_LookupElement,
         proxy_LookupSpecial,
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -878,16 +878,40 @@ class AutoNewContext
             newRequest.destroy();
             if (throwing)
                 JS_SetPendingException(oldcx, exc);
             DestroyContext(newcx, false);
         }
     }
 };
 
+class AutoSaveFrameChain
+{
+    JSContext *cx_;
+    bool saved_;
+
+  public:
+    AutoSaveFrameChain(JSContext *cx)
+      : cx_(cx),
+        saved_(false)
+    {}
+
+    bool save() {
+        if (!JS_SaveFrameChain(cx_))
+            return false;
+        saved_ = true;
+        return true;
+    }
+
+    ~AutoSaveFrameChain() {
+        if (saved_)
+            JS_RestoreFrameChain(cx_);
+    }
+};
+
 static JSBool
 Evaluate(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1 || args.length() > 2) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
@@ -1020,17 +1044,18 @@ Evaluate(JSContext *cx, unsigned argc, j
 
     AutoNewContext ancx;
     if (newContext) {
         if (!ancx.enter(cx))
             return false;
         cx = ancx.get();
     }
 
-    if (saveFrameChain && !JS_SaveFrameChain(cx))
+    AutoSaveFrameChain asfc(cx);
+    if (saveFrameChain && !asfc.save())
         return false;
 
     {
         JSAutoCompartment ac(cx, global);
         uint32_t saved = JS_GetOptions(cx);
         uint32_t options = saved & ~(JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
         if (compileAndGo)
             options |= JSOPTION_COMPILE_N_GO;
@@ -1059,19 +1084,16 @@ Evaluate(JSContext *cx, unsigned argc, j
                     return false;
                 args.rval().setString(str);
                 return true;
             }
             return false;
         }
     }
 
-    if (saveFrameChain)
-        JS_RestoreFrameChain(cx);
-
     return JS_WrapValue(cx, vp);
 }
 
 static JSString *
 FileAsString(JSContext *cx, const char *pathname)
 {
     FILE *file;
     RootedString str(cx);
@@ -3598,16 +3620,18 @@ static JSFunctionSpecWithHelp shell_func
 "  Evaluate code as though it were the contents of a file.\n"
 "  options is an optional object that may have these properties:\n"
 "      compileAndGo: use the compile-and-go compiler option (default: true)\n"
 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
 "      fileName: filename for error messages and debug info\n"
 "      lineNumber: starting line number for error messages and debug info\n"
 "      global: global in which to execute the code\n"
 "      newContext: if true, create and use a new cx (default: false)\n"
+"      saveFrameChain: if true, save the frame chain before evaluating code\n"
+"         and restore it afterwards\n"
 "      catchTermination: if true, catch termination (failure without\n"
 "         an exception value, as for slow scripts or out-of-memory)\n"
 "          and return 'terminated'\n"),
 
     JS_FN_HELP("run", Run, 1, 0,
 "run('foo.js')",
 "  Run the file named by the first argument, returning the number of\n"
 "  of milliseconds spent compiling and executing it."),
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2844,16 +2844,32 @@ DebuggerScript_getSource(JSContext *cx, 
     if (!sourceObject)
         return false;
 
     args.rval().setObject(*sourceObject);
     return true;
 }
 
 static JSBool
+DebuggerScript_getSourceStart(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceStart)", args, obj, script);
+    args.rval().setNumber(script->sourceStart);
+    return true;
+}
+
+static JSBool
+DebuggerScript_getSourceLength(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceEnd)", args, obj, script);
+    args.rval().setNumber(script->sourceEnd - script->sourceStart);
+    return true;
+}
+
+static JSBool
 DebuggerScript_getStaticLevel(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get staticLevel)", args, obj, script);
     args.rval().setNumber(uint32_t(script->staticLevel));
     return true;
 }
 
 static JSBool
@@ -3480,16 +3496,18 @@ DebuggerScript_construct(JSContext *cx, 
     return false;
 }
 
 static const JSPropertySpec DebuggerScript_properties[] = {
     JS_PSG("url", DebuggerScript_getUrl, 0),
     JS_PSG("startLine", DebuggerScript_getStartLine, 0),
     JS_PSG("lineCount", DebuggerScript_getLineCount, 0),
     JS_PSG("source", DebuggerScript_getSource, 0),
+    JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0),
+    JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0),
     JS_PSG("staticLevel", DebuggerScript_getStaticLevel, 0),
     JS_PSG("sourceMapURL", DebuggerScript_getSourceMapUrl, 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec DebuggerScript_methods[] = {
     JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0),
     JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0),
@@ -3633,18 +3651,36 @@ DebuggerSource_getText(JSContext *cx, un
     JSString *str = ss->substring(cx, 0, ss->length());
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
+static JSBool
+DebuggerSource_getUrl(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, sourceObject);
+
+    ScriptSource *ss = sourceObject->source();
+    if (ss->filename()) {
+        JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->filename());
+        if (!str)
+            return false;
+        args.rval().setString(str);
+    } else {
+        args.rval().setNull();
+    }
+    return true;
+}
+
 static const JSPropertySpec DebuggerSource_properties[] = {
     JS_PSG("text", DebuggerSource_getText, 0),
+    JS_PSG("url", DebuggerSource_getUrl, 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec DebuggerSource_methods[] = {
     JS_FS_END
 };
 
 
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -18,17 +18,16 @@
 #include "nsHashKeys.h"
 #include "jsfriendapi.h"
 #include "dom_quickstubs.h"
 #include "nsNullPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
 #include "nsDOMJSUtils.h"
-#include "nsContentUtils.h"
 
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #ifdef MOZ_JSDEBUGGER
 #include "jsdIDebuggerService.h"
 #endif
--- a/js/xpconnect/tests/mochitest/Makefile.in
+++ b/js/xpconnect/tests/mochitest/Makefile.in
@@ -89,16 +89,17 @@ MOCHITEST_FILES =	chrome_wrappers_helper
 		test_bug800864.html \
 		test_bug802557.html \
 		file_bug802557.html \
 		test_bug803730.html \
 		test_bug809547.html \
 		test_bug829872.html \
 		test_bug862380.html \
 		test_bug865260.html \
+		test_bug870423.html \
 		test_bug871887.html \
 		file_crosscompartment_weakmap.html \
 		test_crosscompartment_weakmap.html \
 		test_asmjs.html \
 		file_asmjs.js \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug870423.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=870423
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 870423</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 cross-scope instanceof. **/
+  SimpleTest.waitForExplicitFinish();
+
+  function go() {
+    var sowin = $('soifr').contentWindow;
+    var xowin = $('xoifr').contentWindow;
+
+    check(window, sowin, 'HTMLBodyElement', function(win) { return win.document.body; });
+    check(window, sowin, 'HTMLDocument', function(win) { return win.document; });
+    check(window, sowin, 'Window', function(win) { return win; });
+    check(window, sowin, 'Location', function(win) { return win.location; });
+
+    todo(xowin instanceof Window, "Cross-origin instanceof should work");
+    todo(xowin.location instanceof Location, "Cross-origin instanceof should work");
+
+    SimpleTest.finish();
+  }
+
+  function check(win1, win2, constructorName, getInstance) {
+    ok(getInstance(win1) instanceof win2[constructorName],
+       "Cross-Scope instanceof works: " + constructorName + ", " + win1.location + ", " + win2.location);
+    ok(getInstance(win2) instanceof win1[constructorName],
+       "Cross-Scope instanceof works: " + constructorName + ", " + win2.location + ", " + win1.location);
+  }
+
+  </script>
+</head>
+<body onload="go();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=870423">Mozilla Bug 870423</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="soifr" src="file_empty.html"></iframe>
+<iframe id="xoifr" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -126,36 +126,16 @@ NS_NewComboboxControlFrame(nsIPresShell*
     it->AddStateBits(aStateFlags);
   }
 
   return it;
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsComboboxControlFrame)
 
-namespace {
-
-class DestroyWidgetRunnable : public nsRunnable {
-public:
-  NS_DECL_NSIRUNNABLE
-
-  explicit DestroyWidgetRunnable(nsIWidget* aWidget) : mWidget(aWidget) {}
-
-private:
-  nsCOMPtr<nsIWidget> mWidget;
-};
-
-NS_IMETHODIMP DestroyWidgetRunnable::Run()
-{
-  mWidget = nullptr;
-  return NS_OK;
-}
-
-}
-
 //-----------------------------------------------------------
 // Reflow Debugging Macros
 // These let us "see" how many reflow counts are happening
 //-----------------------------------------------------------
 #ifdef DO_REFLOW_COUNTER
 
 #define MAX_REFLOW_CNT 1024
 static int32_t gTotalReqs    = 0;;
@@ -384,22 +364,17 @@ nsComboboxControlFrame::ShowList(bool aS
     // ListWasSelected which will stop the capture.
     mListControlFrame->AboutToDropDown();
     mListControlFrame->CaptureMouseEvents(true);
     if (widget) {
       widget->CaptureRollupEvents(this, true);
     }
   } else {
     if (widget) {
-      nsCOMPtr<nsIRunnable> widgetDestroyer =
-        new DestroyWidgetRunnable(widget);
-      // 'widgetDestroyer' now has a strong ref on the widget so calling
-      // DestroyWidget here will not *delete* it.
       view->DestroyWidget();
-      NS_DispatchToMainThread(widgetDestroyer);
     }
   }
 
   return weakFrame.IsAlive();
 }
 
 class nsResizeDropdownAtFinalPosition
   : public nsIReflowCallback, public nsRunnable
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/876074-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+    document.getElementById("c").style.content = "'x'";
+}
+
+</script>
+</head>
+
+<body style="display: inline-flex;" onload="boom();"><div></div><div style="display: table-caption;"></div><canvas id="c"></canvas></body>
+
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -491,8 +491,9 @@ load 847208.html
 asserts(4) load 847209.html # bug 847368
 test-pref(layout.css.flexbox.enabled,true) load 847211-1.html
 load 849603.html
 test-pref(layout.css.flexbox.enabled,true) load 851396-1.html
 test-pref(layout.css.flexbox.enabled,true) load 854263-1.html
 test-pref(layout.css.flexbox.enabled,true) load 862947-1.html
 needs-focus pref(accessibility.browsewithcaret,true) load 868906.html
 test-pref(layout.css.flexbox.enabled,true) load 866547-1.html
+asserts(1-4) test-pref(layout.css.flexbox.enabled,true) load 876074-1.html # bug 876749
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1133,18 +1133,19 @@ GetDisplayFlagsForFlexItem(nsIFrame* aFr
   return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
 }
 
 void
 nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                        const nsRect&           aDirtyRect,
                                        const nsDisplayListSet& aLists)
 {
-  MOZ_ASSERT(nsLayoutUtils::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
-             "Frame list should've been sorted in reflow");
+  NS_ASSERTION(
+    nsLayoutUtils::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
+    "Child frames aren't sorted correctly");
 
   DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   // Our children are all block-level, so their borders/backgrounds all go on
   // the BlockBorderBackgrounds list.
   nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
     BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, childLists,
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -229,16 +229,30 @@ nsVideoFrame::BuildLayer(nsDisplayListBu
   transform.Translate(r.TopLeft() + aContainerParameters.mOffset);
   transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height);
   layer->SetBaseTransform(gfx3DMatrix::From2D(transform));
   layer->SetVisibleRegion(nsIntRect(0, 0, frameSize.width, frameSize.height));
   nsRefPtr<Layer> result = layer.forget();
   return result.forget();
 }
 
+class DispatchResizeToControls : public nsRunnable
+{
+public:
+  DispatchResizeToControls(nsIContent* aContent)
+    : mContent(aContent) {}
+  NS_IMETHOD Run() {
+    nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
+                                         NS_LITERAL_STRING("resizevideocontrols"),
+                                         false, false);
+    return NS_OK;
+  }
+  nsCOMPtr<nsIContent> mContent;
+};
+
 NS_IMETHODIMP
 nsVideoFrame::Reflow(nsPresContext*           aPresContext,
                      nsHTMLReflowMetrics&     aMetrics,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
@@ -302,22 +316,27 @@ nsVideoFrame::Reflow(nsPresContext*     
 
       ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState,
                         posterTopLeft.x, posterTopLeft.y, 0, aStatus);
       FinishReflowChild(imageFrame, aPresContext, &kidReflowState, kidDesiredSize,
                         posterTopLeft.x, posterTopLeft.y, 0);
     } else if (child->GetContent() == mVideoControls) {
       // Reflow the video controls frame.
       nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext);
+      nsSize size = child->GetSize();
       nsBoxFrame::LayoutChildAt(boxState,
                                 child,
                                 nsRect(mBorderPadding.left,
                                        mBorderPadding.top,
                                        aReflowState.ComputedWidth(),
                                        aReflowState.ComputedHeight()));
+      if (child->GetSize() != size) {
+        nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent());
+        nsContentUtils::AddScriptRunner(event);
+      }
     } else if (child->GetContent() == mCaptionDiv) {
       // Reflow to caption div
       nsHTMLReflowMetrics kidDesiredSize;
       nsSize availableSize = nsSize(aReflowState.availableWidth,
                                     aReflowState.availableHeight);
       nsHTMLReflowState kidReflowState(aPresContext,
                                        aReflowState,
                                        child,
--- a/mfbt/Atomics.h
+++ b/mfbt/Atomics.h
@@ -615,17 +615,17 @@ struct PrimitiveIntrinsics<8>
       return _InterlockedAnd64(ptr, val);
     }
     static void store(Type* ptr, Type val) {
       _InterlockedExchange64(ptr, val);
     }
     static Type exchange(Type* ptr, Type val) {
       return _InterlockedExchange64(ptr, val);
     }
-    static bool compareExchange(T* ptr, T oldVal, T newVal) {
+    static bool compareExchange(Type* ptr, Type oldVal, Type newVal) {
       return _InterlockedCompareExchange64(ptr, newVal, oldVal) == oldVal;
     }
 };
 
 #  endif
 
 extern "C" { void _ReadWriteBarrier(); }
 
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -352,21 +352,16 @@ pref("gfx.displayport.strategy_vb.revers
 pref("gfx.displayport.strategy_vb.danger_x_base", -1); // danger zone on x-axis when multiplied by viewport width
 pref("gfx.displayport.strategy_vb.danger_y_base", -1); // danger zone on y-axis when multiplied by viewport height
 pref("gfx.displayport.strategy_vb.danger_x_incr", -1); // additional danger zone on x-axis when multiplied by viewport width and velocity
 pref("gfx.displayport.strategy_vb.danger_y_incr", -1); // additional danger zone on y-axis when multiplied by viewport height and velocity
 
 // prediction bias strategy options
 pref("gfx.displayport.strategy_pb.threshold", -1); // velocity threshold in inches/frame
 
-// disable Graphite font shaping by default on Android until memory footprint
-// of using the Charis SIL fonts that we ship with the product is addressed
-// (see bug 700023, bug 846832, bug 847344)
-pref("gfx.font_rendering.graphite.enabled", false);
-
 // don't allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize", true);
 
 // prevent click image resizing for nsImageDocument
 pref("browser.enable_click_image_resizing", false);
 
 // open in tab preferences
 // 0=default window, 1=current window/tab, 2=new window, 3=new tab in most window
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -459,32 +459,30 @@ public class BrowserToolbar implements T
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     updateBackButton(tab.canDoBack());
                     updateForwardButton(tab.canDoForward());
                     setProgressVisibility(false);
                     // Reset the title in case we haven't navigated to a new page yet.
                     updateTitle();
                 }
                 break;
-            case RESTORED:
+            case SELECTED:
                 updateTabCount(Tabs.getInstance().getDisplayCount());
-                break;
-            case SELECTED:
                 mSwitchingTabs = true;
                 // fall through
             case LOCATION_CHANGE:
             case LOAD_ERROR:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     refresh();
                 }
                 mSwitchingTabs = false;
                 break;
             case CLOSED:
             case ADDED:
-                updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount());
+                updateTabCount(Tabs.getInstance().getDisplayCount());
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     updateBackButton(tab.canDoBack());
                     updateForwardButton(tab.canDoForward());
                 }
                 break;
             case FAVICON:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     setFavicon(tab.getFavicon());
@@ -642,17 +640,17 @@ public class BrowserToolbar implements T
                                        1);
 
                 buttonsAnimator.start();
 
                 mAnimatingEntry = false;
 
                 // Trigger animation to update the tabs counter once the
                 // tabs button is back on screen.
-                updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount());
+                updateTabCount(Tabs.getInstance().getDisplayCount());
             }
         });
 
         mAnimatingEntry = true;
 
         mHandler.postDelayed(new Runnable() {
             @Override
             public void run() {
@@ -743,46 +741,33 @@ public class BrowserToolbar implements T
                 if (!tab.isPrivate())
                     mActivity.showNormalTabs();
                 else
                     mActivity.showPrivateTabs();
             }
         }
     }
 
-    public void updateTabCountAndAnimate(int count) {
-        // Don't animate if the toolbar is hidden.
-        if (!isVisible()) {
-            updateTabCount(count);
-            return;
-        }
-
-        // If toolbar is selected, this means the entry is expanded and the
-        // tabs button is translated offscreen. Don't trigger tabs counter
-        // updates until the tabs button is back on screen.
-        // See fromAwesomeBarSearch()
-        if (!mLayout.isSelected()) {
-            mTabsCounter.setCount(count);
-
-            mTabs.setContentDescription((count > 1) ?
-                                        mActivity.getString(R.string.num_tabs, count) :
-                                        mActivity.getString(R.string.one_tab));
-        }
-    }
-
     public void updateTabCount(int count) {
         // If toolbar is selected, this means the entry is expanded and the
         // tabs button is translated offscreen. Don't trigger tabs counter
         // updates until the tabs button is back on screen.
         // See fromAwesomeBarSearch()
         if (mLayout.isSelected()) {
             return;
         }
 
-        mTabsCounter.setCurrentText(String.valueOf(count));
+        // Set TabCounter based on visibility
+        if (isVisible() && ViewHelper.getAlpha(mTabsCounter) != 0) {
+            mTabsCounter.setCountWithAnimation(count);
+        } else {
+            mTabsCounter.setCount(count);
+        }
+
+        // Update A11y information
         mTabs.setContentDescription((count > 1) ?
                                     mActivity.getString(R.string.num_tabs, count) :
                                     mActivity.getString(R.string.one_tab));
     }
 
     public void setProgressVisibility(boolean visible) {
         // The "Throbber start" and "Throbber stop" log messages in this method
         // are needed by S1/S2 tests (http://mrcote.info/phonedash/#).
@@ -1051,17 +1036,17 @@ public class BrowserToolbar implements T
 
                     ViewHelper.setTranslationX(mTitle, 0);
                     ViewHelper.setTranslationX(mFavicon, 0);
                     ViewHelper.setTranslationX(mSiteSecurity, 0);
                 }
 
                 ViewGroup.MarginLayoutParams layoutParams =
                     (ViewGroup.MarginLayoutParams)mForward.getLayoutParams();
-                layoutParams.leftMargin = mDefaultForwardMargin + (mForward.isEnabled() ? mForward.getWidth() / 2 : 0);
+                layoutParams.leftMargin = mDefaultForwardMargin + (mForward.isEnabled() ? width : 0);
                 ViewHelper.setTranslationX(mForward, 0);
 
                 mUrlDisplayContainer.requestLayout();
                 mForwardAnim = null;
             }
         });
 
         prepareForwardAnimation(mForwardAnim, enabled, width);
--- a/mobile/android/base/GeckoPreferences.java
+++ b/mobile/android/base/GeckoPreferences.java
@@ -1,16 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
+import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.GeckoPreferenceFragment;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 
 import android.app.AlertDialog;
@@ -284,28 +285,31 @@ public class GeckoPreferences
     public static boolean getCharEncodingState() {
         return sIsCharEncodingEnabled;
     }
 
     /**
      * Broadcast an intent with <code>pref</code>, <code>branch</code>, and
      * <code>enabled</code> extras. This is intended to represent the
      * notification of a preference value to observers.
+     *
+     * The broadcast will be sent only to receivers registered with the
+     * (Fennec-specific) per-Android package permission.
      */
     public static void broadcastPrefAction(final Context context,
                                            final String action,
                                            final String pref,
                                            final boolean value) {
         final Intent intent = new Intent(action);
         intent.setAction(action);
         intent.putExtra("pref", pref);
         intent.putExtra("branch", GeckoApp.PREFS_NAME);
         intent.putExtra("enabled", value);
         Log.d(LOGTAG, "Broadcast: " + action + ", " + pref + ", " + GeckoApp.PREFS_NAME + ", " + value);
-        context.sendBroadcast(intent);
+        context.sendBroadcast(intent, GlobalConstants.PER_ANDROID_PACKAGE_PERMISSION);
     }
 
     /**
      * Broadcast the provided value as the value of the
      * <code>PREFS_ANNOUNCEMENTS_ENABLED</code> pref.
      */
     public static void broadcastAnnouncementsPref(final Context context, final boolean value) {
         broadcastPrefAction(context,
--- a/mobile/android/base/TabCounter.java
+++ b/mobile/android/base/TabCounter.java
@@ -45,47 +45,55 @@ public class TabCounter extends GeckoTex
 
         mFlipInForward = createAnimation(-90, 0, FadeMode.FADE_IN, -1 * Z_DISTANCE, false);
         mFlipInBackward = createAnimation(90, 0, FadeMode.FADE_IN, Z_DISTANCE, false);
         mFlipOutForward = createAnimation(0, -90, FadeMode.FADE_OUT, -1 * Z_DISTANCE, true);
         mFlipOutBackward = createAnimation(0, 90, FadeMode.FADE_OUT, Z_DISTANCE, true);
 
         removeAllViews();
         setFactory(this);
-        setCount(0);
 
         if (Build.VERSION.SDK_INT >= 16) {
             // This adds the TextSwitcher to the a11y node tree, where we in turn
             // could make it return an empty info node. If we don't do this the
             // TextSwitcher's child TextViews get picked up, and we don't want
             // that since the tabs ImageButton is already properly labeled for
             // accessibility.
             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
             setAccessibilityDelegate(new View.AccessibilityDelegate() {
                     @Override
                     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {}
                 });
         }
     }
 
-    public void setCount(int count) {
-        if (mCount > count) {
-            setInAnimation(mFlipInBackward);
-            setOutAnimation(mFlipOutForward);
-        } else if (mCount < count) {
-            setInAnimation(mFlipInForward);
-            setOutAnimation(mFlipOutBackward);
-        } else {
+    public void setCountWithAnimation(int count) {
+        if (mCount == count)
             return;
+
+        // Don't animate from initial state
+        if (mCount != 0) {
+            if (count < mCount) {
+                setInAnimation(mFlipInBackward);
+                setOutAnimation(mFlipOutForward);
+            } else if (count > mCount) {
+                setInAnimation(mFlipInForward);
+                setOutAnimation(mFlipOutBackward);
+            }
         }
 
         setText(String.valueOf(count));
         mCount = count;
     }
 
+    public void setCount(int count) {
+        setCurrentText(String.valueOf(count));
+        mCount = count;
+    }
+
     private AnimationSet createAnimation(float startAngle, float endAngle,
                                          FadeMode fadeMode,
                                          float zEnd, boolean reverse) {
         final Context context = getContext();
         AnimationSet set = new AnimationSet(context, null);
         set.addAnimation(new Rotate3DAnimation(startAngle, endAngle, CENTER_X, CENTER_Y, zEnd, reverse));
         set.addAnimation(fadeMode == FadeMode.FADE_IN ? new AlphaAnimation(0.0f, 1.0f) :
                                                         new AlphaAnimation(1.0f, 0.0f));
--- a/mobile/android/base/background/common/GlobalConstants.java.in
+++ b/mobile/android/base/background/common/GlobalConstants.java.in
@@ -24,16 +24,23 @@ public class GlobalConstants {
 
   public static final long BUILD_TIMESTAMP = @MOZ_BUILD_TIMESTAMP@;
 
   public static final String MOZ_APP_DISPLAYNAME = "@MOZ_APP_DISPLAYNAME@";
   public static final String MOZ_APP_VERSION = "@MOZ_APP_VERSION@";
   public static final String BROWSER_INTENT_PACKAGE = "@ANDROID_PACKAGE_NAME@";
   public static final String BROWSER_INTENT_CLASS = BROWSER_INTENT_PACKAGE + ".App";
 
+  /**
+   * Bug 800244: this signing-level permission protects broadcast intents that
+   * should be received only by the Firefox versions with the given Android
+   * package name.
+   */
+  public static final String PER_ANDROID_PACKAGE_PERMISSION = "@ANDROID_PACKAGE_NAME@.permission.PER_ANDROID_PACKAGE";
+
   public static final int SHARED_PREFERENCES_MODE = 0;
   // These are used to ask Fennec (via reflection) to send
   // us a pref notification. This avoids us having to guess
   // Fennec's prefs branch and pref name.
   // Eventually Fennec might listen to startup notifications and
   // do this automatically, but this will do for now. See Bug 800244.
   public static String GECKO_PREFERENCES_CLASS = "org.mozilla.gecko.GeckoPreferences";
   public static String GECKO_BROADCAST_METHOD  = "broadcastAnnouncementsPref";
--- a/mobile/android/base/background/healthreport/HealthReportGenerator.java
+++ b/mobile/android/base/background/healthreport/HealthReportGenerator.java
@@ -1,18 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.healthreport;
 
-import java.util.HashMap;
-
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
+import org.json.JSONException;
+import org.json.JSONObject;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
 
 import android.database.Cursor;
 import android.util.SparseArray;
 
 public class HealthReportGenerator {
   private static final int PAYLOAD_VERSION = 3;
@@ -27,18 +25,19 @@ public class HealthReportGenerator {
 
   @SuppressWarnings("static-method")
   protected long now() {
     return System.currentTimeMillis();
   }
 
   /**
    * @return null if no environment could be computed, or else the resulting document.
+   * @throws JSONException if there was an error adding environment data to the resulting document.
    */
-  public JSONObject generateDocument(long since, long lastPingTime, String profilePath) {
+  public JSONObject generateDocument(long since, long lastPingTime, String profilePath) throws JSONException {
     Logger.info(LOG_TAG, "Generating FHR document from " + since + "; last ping " + lastPingTime + ", for profile " + profilePath);
     ProfileInformationCache cache = new ProfileInformationCache(profilePath);
     if (!cache.restoreUnlessInitialized()) {
       Logger.warn(LOG_TAG, "Not enough profile information to compute current environment.");
       return null;
     }
     Environment current = EnvironmentBuilder.getCurrentEnvironment(cache);
     return generateDocument(since, lastPingTime, current);
@@ -50,59 +49,54 @@ public class HealthReportGenerator {
    *<ul>
    *<li>Basic metadata: last ping time, current ping time, version.</li>
    *<li>A map of environments: <code>current</code> and others named by hash. <code>current</code> is fully specified,
    * and others are deltas from current.</li>
    *<li>A <code>data</code> object. This includes <code>last</code> and <code>days</code>.</li>
    *</ul>
    *
    * <code>days</code> is a map from date strings to <tt>{hash: {measurement: {_v: version, fields...}}}</tt>.
+   * @throws JSONException if there was an error adding environment data to the resulting document.
    */
-  public JSONObject generateDocument(long since, long lastPingTime, Environment currentEnvironment) {
+  public JSONObject generateDocument(long since, long lastPingTime, Environment currentEnvironment) throws JSONException {
     Logger.debug(LOG_TAG, "Current environment hash: " + currentEnvironment.getHash());
 
     // We want to map field IDs to some strings as we go.
     SparseArray<Environment> envs = storage.getEnvironmentRecordsByID();
 
     JSONObject document = new JSONObject();
 
-    // Defeat "unchecked" warnings with JDK7. See Bug 875088.
-    @SuppressWarnings("unchecked")
-    HashMap<String, Object> doc = ((HashMap<String, Object>) document);
-
     if (lastPingTime >= HealthReportConstants.EARLIEST_LAST_PING) {
-      doc.put("lastPingDate", HealthReportUtils.getDateString(lastPingTime));
+      document.put("lastPingDate", HealthReportUtils.getDateString(lastPingTime));
     }
 
-    doc.put("thisPingDate", HealthReportUtils.getDateString(now()));
-    doc.put("version", PAYLOAD_VERSION);
+    document.put("thisPingDate", HealthReportUtils.getDateString(now()));
+    document.put("version", PAYLOAD_VERSION);
 
-    doc.put("environments", getEnvironmentsJSON(currentEnvironment, envs));
-    doc.put("data", getDataJSON(currentEnvironment, envs, since));
+    document.put("environments", getEnvironmentsJSON(currentEnvironment, envs));
+    document.put("data", getDataJSON(currentEnvironment, envs, since));
 
     return document;
   }
 
-  @SuppressWarnings("unchecked")
   protected JSONObject getDataJSON(Environment currentEnvironment,
-                                   SparseArray<Environment> envs, long since) {
+                                   SparseArray<Environment> envs, long since) throws JSONException {
     SparseArray<Field> fields = storage.getFieldsByID();
 
     JSONObject days = getDaysJSON(currentEnvironment, envs, fields, since);
 
     JSONObject last = new JSONObject();
 
     JSONObject data = new JSONObject();
     data.put("days", days);
     data.put("last", last);
     return data;
   }
 
-  @SuppressWarnings("unchecked")
-  protected JSONObject getDaysJSON(Environment currentEnvironment, SparseArray<Environment> envs, SparseArray<Field> fields, long since) {
+  protected JSONObject getDaysJSON(Environment currentEnvironment, SparseArray<Environment> envs, SparseArray<Field> fields, long since) throws JSONException {
     JSONObject days = new JSONObject();
     Cursor cursor = storage.getRawEventsSince(since);
     try {
       if (!cursor.moveToNext()) {
         return days;
       }
 
       // A classic walking partition.
@@ -135,36 +129,31 @@ public class HealthReportGenerator {
 
         if (dateChanged || envChanged) {
           envObject = new JSONObject();
           dateObject.put(envs.get(cEnv).hash, envObject);
           lastEnv = cEnv;
         }
 
         final Field field = fields.get(cField);
-        JSONObject measurement = (JSONObject) envObject.get(field.measurementName);
+        JSONObject measurement = envObject.optJSONObject(field.measurementName);
         if (measurement == null) {
           // We will never have more than one measurement version within a
           // single environment -- to do so involves changing the build ID. And
           // even if we did, we have no way to represent it. So just build the
           // output object once.
           measurement = new JSONObject();
           measurement.put("_v", field.measurementVersion);
           envObject.put(field.measurementName, measurement);
         }
         if (field.isDiscreteField()) {
-          JSONArray discrete = (JSONArray) measurement.get(field.fieldName);
-          if (discrete == null) {
-            discrete = new JSONArray();
-            measurement.put(field.fieldName, discrete);
-          }
           if (field.isStringField()) {
-            discrete.add(cursor.getString(3));
+            HealthReportUtils.append(measurement, field.fieldName, cursor.getString(3));
           } else if (field.isIntegerField()) {
-            discrete.add(cursor.getLong(3));
+            HealthReportUtils.append(measurement, field.fieldName, cursor.getLong(3));
           } else {
             // Uh oh!
             throw new IllegalStateException("Unknown field type: " + field.flags);
           }
         } else {
           if (field.isStringField()) {
             measurement.put(field.fieldName, cursor.getString(3));
           } else {
@@ -177,37 +166,35 @@ public class HealthReportGenerator {
       }
       days.put(HealthReportUtils.getDateStringForDay(lastDate), dateObject);
     } finally {
       cursor.close();
     }
     return days;
   }
 
-  @SuppressWarnings("unchecked")
   protected JSONObject getEnvironmentsJSON(Environment currentEnvironment,
-                                           SparseArray<Environment> envs) {
+                                           SparseArray<Environment> envs) throws JSONException {
     JSONObject environments = new JSONObject();
 
     // Always do this, even if it hasn't recorded anything in the DB.
     environments.put("current", jsonify(currentEnvironment, null));
 
     String currentHash = currentEnvironment.getHash();
     for (int i = 0; i < envs.size(); i++) {
       Environment e = envs.valueAt(i);
       if (currentHash.equals(e.getHash())) {
         continue;
       }
       environments.put(e.getHash(), jsonify(e, currentEnvironment));
     }
     return environments;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject jsonify(Environment e, Environment current) {
+  private JSONObject jsonify(Environment e, Environment current) throws JSONException {
     JSONObject age = getProfileAge(e, current);
     JSONObject sysinfo = getSysInfo(e, current);
     JSONObject gecko = getGeckoInfo(e, current);
     JSONObject appinfo = getAppInfo(e, current);
     JSONObject counts = getAddonCounts(e, current);
 
     JSONObject out = new JSONObject();
     if (age != null)
@@ -226,33 +213,31 @@ public class HealthReportGenerator {
       out.put("org.mozilla.addons.active", active);
 
     if (current == null) {
       out.put("hash", e.getHash());
     }
     return out;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject getProfileAge(Environment e, Environment current) {
+  private JSONObject getProfileAge(Environment e, Environment current) throws JSONException {
     JSONObject age = new JSONObject();
     int changes = 0;
     if (current == null || current.profileCreation != e.profileCreation) {
       age.put("profileCreation", e.profileCreation);
       changes++;
     }
     if (current != null && changes == 0) {
       return null;
     }
     age.put("_v", 1);
     return age;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject getSysInfo(Environment e, Environment current) {
+  private JSONObject getSysInfo(Environment e, Environment current) throws JSONException {
     JSONObject sysinfo = new JSONObject();
     int changes = 0;
     if (current == null || current.cpuCount != e.cpuCount) {
       sysinfo.put("cpuCount", e.cpuCount);
       changes++;
     }
     if (current == null || current.memoryMB != e.memoryMB) {
       sysinfo.put("memoryMB", e.memoryMB);
@@ -272,18 +257,17 @@ public class HealthReportGenerator {
     }
     if (current != null && changes == 0) {
       return null;
     }
     sysinfo.put("_v", 1);
     return sysinfo;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject getGeckoInfo(Environment e, Environment current) {
+  private JSONObject getGeckoInfo(Environment e, Environment current) throws JSONException {
     JSONObject gecko = new JSONObject();
     int changes = 0;
     if (current == null || !current.vendor.equals(e.vendor)) {
       gecko.put("vendor", e.vendor);
       changes++;
     }
     if (current == null || !current.appName.equals(e.appName)) {
       gecko.put("name", e.appName);
@@ -323,18 +307,17 @@ public class HealthReportGenerator {
     }
     if (current != null && changes == 0) {
       return null;
     }
     gecko.put("_v", 1);
     return gecko;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject getAppInfo(Environment e, Environment current) {
+  private JSONObject getAppInfo(Environment e, Environment current) throws JSONException {
     JSONObject appinfo = new JSONObject();
     int changes = 0;
     if (current == null || current.isBlocklistEnabled != e.isBlocklistEnabled) {
       appinfo.put("isBlocklistEnabled", e.isBlocklistEnabled);
       changes++;
     }
     if (current == null || current.isTelemetryEnabled != e.isTelemetryEnabled) {
       appinfo.put("isTelemetryEnabled", e.isTelemetryEnabled);
@@ -342,18 +325,17 @@ public class HealthReportGenerator {
     }
     if (current != null && changes == 0) {
       return null;
     }
     appinfo.put("_v", 2);
     return appinfo;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject getAddonCounts(Environment e, Environment current) {
+  private JSONObject getAddonCounts(Environment e, Environment current) throws JSONException {
     JSONObject counts = new JSONObject();
     int changes = 0;
     if (current == null || current.extensionCount != e.extensionCount) {
       counts.put("extension", e.extensionCount);
       changes++;
     }
     if (current == null || current.pluginCount != e.pluginCount) {
       counts.put("plugin", e.pluginCount);
@@ -365,18 +347,17 @@ public class HealthReportGenerator {
     }
     if (current != null && changes == 0) {
       return null;
     }
     counts.put("_v", 1);
     return counts;
   }
 
-  @SuppressWarnings("unchecked")
-  private JSONObject getActiveAddons(Environment e, Environment current) {
+  private JSONObject getActiveAddons(Environment e, Environment current) throws JSONException {
     JSONObject active = new JSONObject();
     int changes = 0;
     if (current != null && changes == 0) {
       return null;
     }
     active.put("_v", 1);
     return active;
   }
--- a/mobile/android/base/background/healthreport/HealthReportUtils.java
+++ b/mobile/android/base/background/healthreport/HealthReportUtils.java
@@ -1,18 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.healthreport;
 
 import java.text.SimpleDateFormat;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Locale;
+import java.util.Set;
+import java.util.SortedSet;
 import java.util.TimeZone;
+import java.util.TreeSet;
 
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONArray;
 import org.mozilla.apache.commons.codec.digest.DigestUtils;
 
 import android.content.ContentUris;
 import android.net.Uri;
 
 public class HealthReportUtils {
   public static int getDay(final long time) {
     return (int) Math.floor(time / HealthReportConstants.MILLISECONDS_PER_DAY);
@@ -40,9 +48,93 @@ public class HealthReportUtils {
    *
    * @param environmentURI
    *          the {@link Uri} returned by an environment operation.
    * @return a {@link Uri} to which insertions can be dispatched.
    */
   public static Uri getEventURI(Uri environmentURI) {
     return environmentURI.buildUpon().path("/events/" + ContentUris.parseId(environmentURI) + "/").build();
   }
+
+  /**
+   * Copy the keys from the provided {@link JSONObject} into the provided {@link Set}.
+   */
+  private static <T extends Set<String>> T intoKeySet(T keys, JSONObject o) {
+    if (o == null || o == JSONObject.NULL) {
+      return keys;
+    }
+
+    @SuppressWarnings("unchecked")
+    Iterator<String> it = o.keys();
+    while (it.hasNext()) {
+      keys.add(it.next());
+    }
+    return keys;
+  }
+
+  /**
+   * Produce a {@link SortedSet} containing the string keys of the provided
+   * object.
+   *
+   * @param o a {@link JSONObject} with string keys.
+   * @return a sorted set.
+   */
+  public static SortedSet<String> sortedKeySet(JSONObject o) {
+    return intoKeySet(new TreeSet<String>(), o);
+  }
+
+  /**
+   * Produce a {@link Set} containing the string keys of the provided object.
+   * @param o a {@link JSONObject} with string keys.
+   * @return an unsorted set.
+   */
+  public static Set<String> keySet(JSONObject o) {
+    return intoKeySet(new HashSet<String>(), o);
+  }
+
+  /**
+   * {@link JSONObject} doesn't provide a <code>clone</code> method, nor any
+   * useful constructors, so we do this the hard way.
+   *
+   * @return a new object containing the same keys and values as the old.
+   * @throws JSONException
+   *           if JSONObject is even more stupid than expected and cannot store
+   *           a value from the provided object in the new object. This should
+   *           never happen.
+   */
+  public static JSONObject shallowCopyObject(JSONObject o) throws JSONException {
+    if (o == null) {
+      return null;
+    }
+
+    JSONObject out = new JSONObject();
+    @SuppressWarnings("unchecked")
+    Iterator<String> keys = out.keys();
+    while (keys.hasNext()) {
+      final String key = keys.next();
+      out.put(key, o.get(key));
+    }
+    return out;
+  }
+
+  /**
+   * Just like {@link JSONObject#accumulate(String, Object)}, but doesn't do the wrong thing for single values.
+   * @throws JSONException 
+   */
+  public static void append(JSONObject o, String key, Object value) throws JSONException {
+    if (!o.has(key)) {
+      JSONArray arr = new JSONArray();
+      arr.put(value);
+      o.put(key, arr);
+      return;
+    }
+    Object dest = o.get(key);
+    if (dest instanceof JSONArray) {
+      ((JSONArray) dest).put(value);
+      return;
+    }
+    JSONArray arr = new JSONArray();
+    arr.put(dest);
+    arr.put(value);
+    o.put(key, arr);
+    return;
+  }
 }
--- a/mobile/android/base/resources/values-xlarge-v11/dimens.xml
+++ b/mobile/android/base/resources/values-xlarge-v11/dimens.xml
@@ -16,11 +16,10 @@
     <dimen name="awesomebar_row_height">66dp</dimen>
     <dimen name="awesomebar_tab_transparency_height">46dp</dimen>
     <dimen name="browser_toolbar_height">56dp</dimen>
     <dimen name="remote_tab_child_row_height">56dp</dimen>
     <dimen name="remote_tab_group_row_height">34dp</dimen>
     <dimen name="tabs_counter_size">26sp</dimen>
     <dimen name="tabs_panel_indicator_width">60dp</dimen>
     <dimen name="tabs_panel_list_padding">8dip</dimen>
-    <dimen name="addressbar_offset_left">84dip</dimen>
 
 </resources>
--- a/mobile/android/services/manifests/AnnouncementsAndroidManifest_services.xml.in
+++ b/mobile/android/services/manifests/AnnouncementsAndroidManifest_services.xml.in
@@ -1,8 +1,10 @@
         <service
 		        android:exported="false"
 		        android:name="org.mozilla.gecko.background.announcements.AnnouncementsService" >
         </service>
         <service
 		        android:exported="false"
 		        android:name="org.mozilla.gecko.background.announcements.AnnouncementsBroadcastService" >
         </service>
+
+        <uses-permission android:name="@ANDROID_PACKAGE_NAME@.permission.PER_ANDROID_PACKAGE" />
--- a/mobile/android/services/manifests/SyncAndroidManifest_permissions.xml.in
+++ b/mobile/android/services/manifests/SyncAndroidManifest_permissions.xml.in
@@ -12,8 +12,21 @@
          versions sharing an Android Account type.  This needs to
          agree with GlobalConstants.PER_ACCOUNT_TYPE_PERMISSION. -->
     <permission
         android:name="@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@.permission.PER_ACCOUNT_TYPE"
         android:protectionLevel="signature">
     </permission>
 
     <uses-permission android:name="@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@.permission.PER_ACCOUNT_TYPE" />
+
+    <!-- A signature level permission specific to each Firefox version
+         (Android package name, e.g., org.mozilla.firefox).  Use this
+         permission to broadcast securely within a single Firefox
+         version.  This needs to agree with
+         GlobalConstants.PER_ANDROID_PACKAGE_PERMISSION.
+
+         This is not Sync-specific, but we don't have a better place
+         to put generic background service manifest snippets, so here
+         is expedient. -->
+    <permission
+        android:name="@ANDROID_PACKAGE_NAME@.permission.PER_ANDROID_PACKAGE"
+        android:protectionLevel="signature"/>
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -147,16 +147,19 @@ pref("browser.chrome.image_icons.max_siz
 pref("browser.triple_click_selects_paragraph", true);
 
 // Media cache size in kilobytes
 pref("media.cache_size", 512000);
 
 // Master HTML5 media volume scale.
 pref("media.volume_scale", "1.0");
 
+// Timeout for wakelock release
+pref("media.wakelock_timeout", 2000);
+
 #ifdef MOZ_WMF
 pref("media.windows-media-foundation.enabled", true);
 pref("media.windows-media-foundation.use-dxva", true);
 #endif
 #ifdef MOZ_RAW
 pref("media.raw.enabled", true);
 #endif
 #ifdef MOZ_OGG
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -22,28 +22,43 @@
 #include <time.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <zlib.h>
 #include "dlfcn.h"
 #include "APKOpen.h"
 #include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/prctl.h>
 #include "Zip.h"
 #include "sqlite3.h"
 #include "SQLiteBridge.h"
 #include "NSSBridge.h"
 #include "ElfLoader.h"
 #include "application.ini.h"
 
 /* Android headers don't define RUSAGE_THREAD */
 #ifndef RUSAGE_THREAD
 #define RUSAGE_THREAD 1
 #endif
 
+#ifndef RELEASE_BUILD
+/* Official builds have the debuggable flag set to false, which disables
+ * the backtrace dumper from bionic. However, as it is useful for native
+ * crashes happening before the crash reporter is registered, re-enable
+ * it on non release builds (i.e. nightly and aurora).
+ * Using a constructor so that it is re-enabled as soon as libmozglue.so
+ * is loaded.
+ */
+__attribute__((constructor))
+void make_dumpable() {
+  prctl(PR_SET_DUMPABLE, 1);
+}
+#endif
+
 extern "C" {
 /*
  * To work around http://code.google.com/p/android/issues/detail?id=23203
  * we don't link with the crt objects. In some configurations, this means
  * a lack of the __dso_handle symbol because it is defined there, and
  * depending on the android platform and ndk versions used, it may or may
  * not be defined in libc.so. In the latter case, we fail to link. Defining
  * it here as weak makes us provide the symbol when it's not provided by
@@ -149,18 +164,19 @@ report_mapping(char *name, void *base, u
 }
 
 static mozglueresult
 loadGeckoLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
   uint64_t t0 = TimeStamp_Now();
-  struct rusage usage1;
-  getrusage(RUSAGE_THREAD, &usage1);
+  struct rusage usage1_thread, usage1;
+  getrusage(RUSAGE_THREAD, &usage1_thread);
+  getrusage(RUSAGE_SELF, &usage1);
   
   RefPtr<Zip> zip = ZipCollection::GetZip(apkName);
 
   char *file = new char[strlen(apkName) + sizeof("!/libxul.so")];
   sprintf(file, "%s!/libxul.so", apkName);
   xul_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
   delete[] file;
 
@@ -172,24 +188,32 @@ loadGeckoLibs(const char *apkName)
 #define JNI_BINDINGS
 #include "jni-stubs.inc"
 #undef JNI_BINDINGS
 
   void (*XRE_StartupTimelineRecord)(int, uint64_t);
   xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord);
 
   uint64_t t1 = TimeStamp_Now();
-  struct rusage usage2;
-  getrusage(RUSAGE_THREAD, &usage2);
+  struct rusage usage2_thread, usage2;
+  getrusage(RUSAGE_THREAD, &usage2_thread);
+  getrusage(RUSAGE_SELF, &usage2);
+
+#define RUSAGE_TIMEDIFF(u1, u2, field) \
+  ((u2.ru_ ## field.tv_sec - u1.ru_ ## field.tv_sec) * 1000 + \
+   (u2.ru_ ## field.tv_usec - u1.ru_ ## field.tv_usec) / 1000)
 
-  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %lldms total, %ldms user, %ldms system, %ld faults",
-                      (t1 - t0) / 1000,
-                      (usage2.ru_utime.tv_sec - usage1.ru_utime.tv_sec)*1000 + (usage2.ru_utime.tv_usec - usage1.ru_utime.tv_usec)/1000,
-                      (usage2.ru_stime.tv_sec - usage1.ru_stime.tv_sec)*1000 + (usage2.ru_stime.tv_usec - usage1.ru_stime.tv_usec)/1000,
-                      usage2.ru_majflt-usage1.ru_majflt);
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %lldms total, %ldms(%ldms) user, %ldms(%ldms) system, %ld(%ld) faults",
+                      (t1 - t0) / 1000000,
+                      RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, utime),
+                      RUSAGE_TIMEDIFF(usage1, usage2, utime),
+                      RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, stime),
+                      RUSAGE_TIMEDIFF(usage1, usage2, stime),
+                      usage2_thread.ru_majflt - usage1_thread.ru_majflt,
+                      usage2.ru_majflt - usage1.ru_majflt);
 
   XRE_StartupTimelineRecord(LINKER_INITIALIZED, t0);
   XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1);
   return SUCCESS;
 }
 
 static mozglueresult loadNSSLibs(const char *apkName);
 
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -12,16 +12,17 @@
 #include "nsAtomicRefcnt.h"
 #include "nsIOService.h"
 #include "nsStreamUtils.h"
 #include "nsNetSegmentUtils.h"
 #include "nsNetAddr.h"
 #include "nsTransportUtils.h"
 #include "nsProxyInfo.h"
 #include "nsNetCID.h"
+#include "nsNetUtil.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "netCore.h"
 #include "plstr.h"
 #include "prnetdb.h"
 #include "prerr.h"
 #include "NetworkActivityMonitor.h"
 #include "mozilla/VisualEventTracer.h"
@@ -1853,30 +1854,35 @@ nsSocketTransport::GetSecurityCallbacks(
     MutexAutoLock lock(mLock);
     NS_IF_ADDREF(*callbacks = mCallbacks);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
 {
+    nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
+    NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
+                                           NS_GetCurrentThread(),
+                                           getter_AddRefs(threadsafeCallbacks));
+
     nsCOMPtr<nsISupports> secinfo;
     {
         MutexAutoLock lock(mLock);
-        mCallbacks = callbacks;
+        mCallbacks = threadsafeCallbacks;
         SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",
                     mSecInfo.get(), mCallbacks.get()));
 
         secinfo = mSecInfo;
     }
 
     // don't call into PSM while holding mLock!!
     nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
     if (secCtrl)
-        secCtrl->SetNotificationCallbacks(callbacks);
+        secCtrl->SetNotificationCallbacks(threadsafeCallbacks);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
                                 nsIEventTarget *target)
 {
deleted file mode 100644
--- a/testing/config/Makefile.in
+++ /dev/null
@@ -1,24 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH = @DEPTH@
-topsrcdir = @top_srcdir@
-srcdir = @srcdir@
-VPATH = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
-
-CONFIG_FILES = \
-  mozharness_config.py \
-  $(NULL)
-
-_DEST_DIR = $(DEPTH)/_tests/config
-libs:: $(CONFIG_FILES)
-	$(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR)
-
-stage-package: PKG_STAGE = $(DIST)/test-package-stage
-stage-package:
-	$(NSINSTALL) -D $(PKG_STAGE)/config
-	@(cd $(srcdir) && tar $(TAR_CREATE_FLAGS) - $(CONFIG_FILES)) | (cd $(PKG_STAGE)/config && tar -xf -)
deleted file mode 100644
--- a/testing/config/moz.build
+++ /dev/null
@@ -1,5 +0,0 @@
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -589,20 +589,41 @@ function coordinates(target, x, y) {
   return coords;
 }
 
 /**
  * This function returns if the element is in viewport 
  */
 function elementInViewport(el) {
   let rect = el.getBoundingClientRect();
-  return (rect.top >= curWindow.pageYOffset &&
-          rect.left >= curWindow.pageXOffset &&
-          rect.bottom <= (curWindow.pageYOffset + curWindow.innerHeight) &&
-          rect.right <= (curWindow.pageXOffset + curWindow.innerWidth)
+  return  (/* Top left corner is in view */
+           (rect.top >= curWindow.pageYOffset &&
+            rect.top <= (curWindow.pageYOffset + curWindow.innerHeight) &&
+            rect.left >= curWindow.pageXOffset &&
+            rect.left <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
+           /* Top right corner is in view */ 
+           (rect.top >= curWindow.pageYOffset &&
+            rect.top <= (curWindow.pageYOffset + curWindow.innerHeight) &&
+            rect.right >= curWindow.pageXOffset &&
+            rect.right <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
+           /* Bottom right corner is in view */
+           (rect.bottom >= curWindow.pageYOffset &&
+            rect.bottom <= (curWindow.pageYOffset + curWindow.innerHeight)  &&
+            rect.right >= curWindow.pageXOffset &&
+            rect.right <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
+           /* Bottom left corner is in view */
+           (rect.bottom >= curWindow.pageYOffset &&
+            rect.bottom <= (curWindow.pageYOffset + curWindow.innerHeight)  &&
+            rect.left >= curWindow.pageXOffset &&
+            rect.left <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
+           /* Center of the element is in view if element larger than viewport */
+           ((rect.top + (rect.height/2)) <= curWindow.pageYOffset &&
+            (rect.top + (rect.height/2)) >= (curWindow.pageYOffset + curWindow.innerHeight) &&
+            (rect.left + (rect.width/2)) <= curWindow.pageXOffset &&
+            (rect.left + (rect.width/2)) >= (curWindow.pageXOffset + curWindow.innerWidth))
          );
 }
 
 /**
  * This function throws the visibility of the element error
  */
 function checkVisible(el) {
   //check if the element is visible
--- a/testing/mozbase/mozfile/mozfile/mozfile.py
+++ b/testing/mozbase/mozfile/mozfile/mozfile.py
@@ -1,19 +1,26 @@
 # 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/.
 
 import os
 import tarfile
 import tempfile
 import urlparse
+import urllib2
 import zipfile
 
-__all__ = ['extract_tarball', 'extract_zip', 'extract', 'is_url', 'rmtree', 'NamedTemporaryFile']
+__all__ = ['extract_tarball',
+           'extract_zip',
+           'extract',
+           'is_url',
+           'load',
+           'rmtree',
+           'NamedTemporaryFile']
 
 
 ### utilities for extracting archives
 
 def extract_tarball(src, dest):
     """extract a .tar file"""
 
     bundle = tarfile.open(src)
@@ -45,36 +52,38 @@ def extract_zip(src, dest):
             os.makedirs(filename)
         else:
             path = os.path.dirname(filename)
             if not os.path.isdir(path):
                 os.makedirs(path)
             _dest = open(filename, 'wb')
             _dest.write(bundle.read(name))
             _dest.close()
+        mode = bundle.getinfo(name).external_attr >> 16 & 0x1FF
+        os.chmod(filename, mode)
     bundle.close()
     return namelist
 
 
 def extract(src, dest=None):
     """
     Takes in a tar or zip file and extracts it to dest
 
     If dest is not specified, extracts to os.path.dirname(src)
 
     Returns the list of top level files that were extracted
     """
 
     assert os.path.exists(src), "'%s' does not exist" % src
-    assert not os.path.isfile(dest), "dest cannot be a file"
 
     if dest is None:
         dest = os.path.dirname(src)
     elif not os.path.isdir(dest):
         os.makedirs(dest)
+    assert not os.path.isfile(dest), "dest cannot be a file"
 
     if zipfile.is_zipfile(src):
         namelist = extract_zip(src, dest)
     elif tarfile.is_tarfile(src):
         namelist = extract_tarball(src, dest)
     else:
         raise Exception("mozfile.extract: no archive format found for '%s'" %
                         src)
@@ -155,47 +164,71 @@ class NamedTemporaryFile(object):
     with NamedTemporaryFile() as fh:
         fh.write(b'foobar')
 
         print('Filename: %s' % fh.name)
 
     see https://bugzilla.mozilla.org/show_bug.cgi?id=821362
     """
     def __init__(self, mode='w+b', bufsize=-1, suffix='', prefix='tmp',
-        dir=None):
+                 dir=None, delete=True):
 
         fd, path = tempfile.mkstemp(suffix, prefix, dir, 't' in mode)
         os.close(fd)
 
         self.file = open(path, mode)
         self._path = path
+        self._delete = delete
         self._unlinked = False
 
     def __getattr__(self, k):
         return getattr(self.__dict__['file'], k)
 
+    def __iter__(self):
+        return self.__dict__['file']
+
     def __enter__(self):
         self.file.__enter__()
         return self
 
     def __exit__(self, exc, value, tb):
         self.file.__exit__(exc, value, tb)
-        os.unlink(self.__dict__['_path'])
-        self._unlinked = True
+        if self.__dict__['_delete']:
+            os.unlink(self.__dict__['_path'])
+            self._unlinked = True
 
     def __del__(self):
         if self.__dict__['_unlinked']:
             return
+        self.file.__exit__(None, None, None)
+        if self.__dict__['_delete']:
+            os.unlink(self.__dict__['_path'])
 
-        self.file.__exit__(None, None, None)
-        os.unlink(self.__dict__['_path'])
 
+### utilities dealing with URLs
 
 def is_url(thing):
     """
     Return True if thing looks like a URL.
     """
 
     parsed = urlparse.urlparse(thing)
     if 'scheme' in parsed:
         return len(parsed.scheme) >= 2
     else:
         return len(parsed[0]) >= 2
+
+def load(resource):
+    """
+    open a file or URL for reading.  If the passed resource string is not a URL,
+    or begins with 'file://', return a ``file``.  Otherwise, return the
+    result of urllib2.urlopen()
+    """
+
+    # handle file URLs separately due to python stdlib limitations
+    if resource.startswith('file://'):
+        resource = resource[len('file://'):]
+
+    if not is_url(resource):
+        # if no scheme is given, it is a file path
+        return file(resource)
+
+    return urllib2.urlopen(resource)
--- a/testing/mozbase/mozfile/setup.py
+++ b/testing/mozbase/mozfile/setup.py
@@ -1,23 +1,24 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from setuptools import setup
 
-PACKAGE_VERSION = '0.3'
+PACKAGE_VERSION = '0.7'
 
 setup(name='mozfile',
       version=PACKAGE_VERSION,
       description="Library of file utilities for use in Mozilla testing",
       long_description="see http://mozbase.readthedocs.org/",
       classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
       keywords='mozilla',
       author='Mozilla Automation and Tools team',
       author_email='tools@lists.mozilla.org',
       url='https://wiki.mozilla.org/Auto-tools/Projects/MozBase',
       license='MPL',
       packages=['mozfile'],
       include_package_data=True,
       zip_safe=False,
-      install_requires=[]
+      install_requires=[],
+      tests_require=['mozhttpd']
       )
--- a/testing/mozbase/mozfile/tests/manifest.ini
+++ b/testing/mozbase/mozfile/tests/manifest.ini
@@ -1,2 +1,4 @@
 [test.py]
-[is_url.py]
\ No newline at end of file
+[test_load.py]
+[test_tempfile.py]
+[test_url.py]
new file mode 100755
--- /dev/null
+++ b/testing/mozbase/mozfile/tests/test_load.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+"""
+tests for mozfile.load
+"""
+
+import mozhttpd
+import os
+import tempfile
+import unittest
+from mozfile import load
+
+class TestLoad(unittest.TestCase):
+    """test the load function"""
+
+    def test_http(self):
+        """test with mozhttpd and a http:// URL"""
+
+        def example(request):
+            """example request handler"""
+            body = 'example'
+            return (200, {'Content-type': 'text/plain',
+                          'Content-length': len(body)
+                          }, body)
+
+        host = '127.0.0.1'
+        httpd = mozhttpd.MozHttpd(host=host,
+                                  port=8888,
+                                  urlhandlers=[{'method': 'GET',
+                                                'path': '.*',
+                                                'function': example}])
+        try:
+            httpd.start(block=False)
+            content = load('http://127.0.0.1:8888/foo').read()
+            self.assertEqual(content, 'example')
+        finally:
+            httpd.stop()
+
+
+    def test_file_path(self):
+        """test loading from file path"""
+        try:
+            # create a temporary file
+            tmp = tempfile.NamedTemporaryFile(delete=False)
+            tmp.write('foo bar')
+            tmp.close()
+
+            # read the file
+            contents = file(tmp.name).read()
+            self.assertEqual(contents, 'foo bar')
+
+            # read the file with load and a file path
+            self.assertEqual(load(tmp.name).read(), contents)
+
+            # read the file with load and a file URL
+            self.assertEqual(load('file://%s' % tmp.name).read(), contents)
+        finally:
+            # remove the tempfile
+            if os.path.exists(tmp.name):
+                os.remove(tmp.name)
+
+if __name__ == '__main__':
+    unittest.main()
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozfile/tests/test_tempfile.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+tests for mozfile.NamedTemporaryFile
+"""
+
+import mozfile
+import os
+import unittest
+
+class TestNamedTemporaryFile(unittest.TestCase):
+
+    def test_iteration(self):
+        """ensure the line iterator works"""
+
+        # make a file and write to it
+        tf = mozfile.NamedTemporaryFile()
+        notes = ['doe', 'rae', 'mi']
+        for note in notes:
+            tf.write('%s\n' % note)
+        tf.flush()
+
+        # now read from it
+        tf.seek(0)
+        lines = [line.rstrip('\n') for line in tf.readlines()]
+        self.assertEqual(lines, notes)
+
+        # now read from it iteratively
+        lines = []
+        for line in tf:
+            lines.append(line.strip())
+        self.assertEqual(lines, []) # because we did not seek(0)
+        tf.seek(0)
+        lines = []
+        for line in tf:
+            lines.append(line.strip())
+        self.assertEqual(lines, notes)
+
+    def test_delete(self):
+        """ensure ``delete=True/False`` works as expected"""
+
+        # make a deleteable file; ensure it gets cleaned up
+        path = None
+        with mozfile.NamedTemporaryFile(delete=True) as tf:
+            path = tf.name
+        self.assertTrue(isinstance(path, basestring))
+        self.assertFalse(os.path.exists(path))
+
+        # it is also deleted when __del__ is called
+        # here we will do so explicitly
+        tf = mozfile.NamedTemporaryFile(delete=True)
+        path = tf.name
+        self.assertTrue(os.path.exists(path))
+        del tf
+        self.assertFalse(os.path.exists(path))
+
+        # Now the same thing but we won't delete the file
+        path = None
+        try:
+            with mozfile.NamedTemporaryFile(delete=False) as tf:
+                path = tf.name
+            self.assertTrue(os.path.exists(path))
+        finally:
+            if path and os.path.exists(path):
+                os.remove(path)
+
+        path = None
+        try:
+            tf = mozfile.NamedTemporaryFile(delete=False)
+            path = tf.name
+            self.assertTrue(os.path.exists(path))
+            del tf
+            self.assertTrue(os.path.exists(path))
+        finally:
+            if path and os.path.exists(path):
+                os.remove(path)
+
+if __name__ == '__main__':
+    unittest.main()
old mode 100644
new mode 100755
rename from testing/mozbase/mozfile/tests/is_url.py
rename to testing/mozbase/mozfile/tests/test_url.py
--- a/testing/mozbase/mozprocess/mozprocess/processhandler.py
+++ b/testing/mozbase/mozprocess/mozprocess/processhandler.py
@@ -628,17 +628,24 @@ falling back to not using job objects fo
           'ignore_children=False' (the default) then it will also
           also kill all child processes spawned by it.
           If you specified 'ignore_children=True' when creating the process,
           only the root process will be killed.
 
           Note that this does not manage any state, save any output etc,
           it immediately kills the process.
         """
-        return self.proc.kill()
+        try:
+            return self.proc.kill()
+        except AttributeError:
+            # Try to print a relevant error message.
+            if not self.proc:
+                print >> sys.stderr, "Unable to kill Process because call to ProcessHandler constructor failed."
+            else:
+                raise
 
     def readWithTimeout(self, f, timeout):
         """
           Try to read a line of output from the file object |f|.
           |f| must be a  pipe, like the |stdout| member of a subprocess.Popen
           object created with stdout=PIPE. If no output
           is received within |timeout| seconds, return a blank line.
           Returns a tuple (line, did_timeout), where |did_timeout| is True
--- a/testing/mozbase/mozprocess/setup.py
+++ b/testing/mozbase/mozprocess/setup.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from setuptools import setup
 
-PACKAGE_VERSION = '0.9'
+PACKAGE_VERSION = '0.10'
 
 setup(name='mozprocess',
       version=PACKAGE_VERSION,
       description="Mozilla-authored process handling",
       long_description='see http://mozbase.readthedocs.org/',
       classifiers=['Environment :: Console',
                    'Intended Audience :: Developers',
                    'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
--- a/testing/mozbase/mozprocess/tests/Makefile
+++ b/testing/mozbase/mozprocess/tests/Makefile
@@ -1,57 +1,55 @@
 #
-# proclaunch  tests Makefile
+# mozprocess proclaunch tests Makefile
 #
-UNAME := $(shell uname -s)
-ifeq ($(UNAME), MINGW32_NT-6.1)
-WIN32 = 1
-endif
-ifeq ($(UNAME), MINGW32_NT-5.1)
-WIN32 = 1
-endif
+
+# include rules for platform determination
+include iniparser/platform.mk
 
 ifeq ($(WIN32), 1)
+# Win 32
 CC      = cl
 LINK    = link
 CFLAGS  = //Od //I "iniparser" //D "WIN32" //D "_WIN32" //D "_DEBUG" //D "_CONSOLE" //D "_UNICODE" //D "UNICODE" //Gm //EHsc //RTC1 //MDd //W3 //nologo //c //ZI //TC
-LFLAGS  = //OUT:"proclaunch.exe" //INCREMENTAL //LIBPATH:"iniparser\\" //NOLOGO //DEBUG //SUBSYSTEM:CONSOLE //DYNAMICBASE //NXCOMPAT //MACHINE:X86 //ERRORREPORT:PROMPT iniparser.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
+LFLAGS  = //OUT:"proclaunch.exe" //INCREMENTAL //LIBPATH:"iniparser\\" //NOLOGO //DEBUG //SUBSYSTEM:CONSOLE //DYNAMICBASE //NXCOMPAT //ERRORREPORT:PROMPT iniparser.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
 RM      = rm -f
 
-default: all
 all: iniparser proclaunch
 
 iniparser:
 	$(MAKE) -C iniparser
 
 proclaunch.obj: proclaunch.c
+	@(echo "compiling proclaunch; platform: $(UNAME), WIN32: $(WIN32)")
 	$(CC) $(CFLAGS) proclaunch.c
 
 proclaunch: proclaunch.obj
 	$(LINK) $(LFLAGS) proclaunch.obj
 
+clean:
+	$(RM) proclaunch.exe projloaunch.obj
 else
+# *nix/Mac
+LFLAGS  = -L.. -liniparser
+AR	    = ar
+ARFLAGS = rcv
+RM      = rm -f
 CC      = gcc
 ifeq ($(UNAME), Linux)
 CFLAGS  = -g -v -Iiniparser
 else
 CFLAGS  = -g -v -arch i386 -Iiniparser
 endif
 
-LFLAGS  = -L.. -liniparser
-AR	    = ar
-ARFLAGS = rcv
-RM      = rm -f
-
-
-default: all
-
 all: libiniparser.a proclaunch
 
 libiniparser.a:
 	$(MAKE) -C iniparser
 
 proclaunch: proclaunch.c
+	@(echo "compiling proclaunch; platform: $(UNAME), WIN32: $(WIN32)")
 	$(CC) $(CFLAGS) -o proclaunch proclaunch.c -Iiniparser -Liniparser -liniparser
 
-clean veryclean:
-	$(RM) proclaunch 
+clean:
+	$(RM) proclaunch
+	$(MAKE) -C iniparser clean
 endif
--- a/testing/mozbase/mozprocess/tests/iniparser/Makefile
+++ b/testing/mozbase/mozprocess/tests/iniparser/Makefile
@@ -1,118 +1,85 @@
 #
 # iniparser Makefile
 #
-UNAME := $(shell uname -s)
-ifeq ($(UNAME), MINGW32_NT-6.1)
-WIN32 = 1
-endif
-ifeq ($(UNAME), MINGW32_NT-5.1)
-WIN32 = 1
-endif
+
+# source files
+SRCS = iniparser.c \
+	dictionary.c
 
+# include rules for platform determination
+include platform.mk
+
+# flags for the the various systems
 ifeq ($(UNAME), Linux)
     # Compiler settings
     CC      = gcc
-    # Ar settings to build the library
     AR	    = ar
     ARFLAGS = rcv
     SHLD = ${CC} ${CFLAGS}
     CFLAGS  = -O2 -fPIC -Wall -ansi -pedantic
     LDSHFLAGS = -shared -Wl,-Bsymbolic  -Wl,-rpath -Wl,/usr/lib -Wl,-rpath,/usr/lib
     LDFLAGS = -Wl,-rpath -Wl,/usr/lib -Wl,-rpath,/usr/lib
 endif
-
 ifeq ($(UNAME), Darwin)
     # Compiler settings
     CC      = gcc
     # Ar settings to build the library
     AR	    = ar
     ARFLAGS = rcv
-    #SHLD = ${CC} ${CFLAGS}
     SHLD = libtool
     CFLAGS  = -v -arch i386 -isysroot /Developer/SDKs/MacOSX10.6.sdk -fPIC -Wall -ansi -pedantic
     LDFLAGS = -arch_only i386
 endif
-
 ifeq ($(WIN32), 1)
     CC = cl
     CFLAGS = //Od //D "_WIN32" //D "WIN32" //D "_CONSOLE" //D "_CRT_SECURE_NO_WARNINGS" //D "_UNICODE" //D "UNICODE" //Gm //EHsc //RTC1 //MDd //W3 //nologo //c //ZI //TC
     LDFLAGS = //OUT:"iniparser.lib" //NOLOGO
     LINK = lib
+    RM = rm -f
 endif
-    
-ifeq ($(WIN32), 1)
-SUFFIXES = .obj .c .h .lib
 
-COMPILE.c=$(CC) $(CFLAGS) -c
+# windows build rules
+ifeq ($(WIN32), 1)
 
-#.c.obj:
-#	@(echo "compiling $< ...")
-#	@($(COMPILE.c) $@ $<)
+COMPILE.c = $(CC) $(CFLAGS) -c
+OBJS = $(SRCS:.c=.obj)
 
 all: iniparser.obj dictionary.obj iniparser.lib
 
-SRCS = iniparser.c \
-	dictionary.c
-OBJS = $(SRCS:.c=.obj)
-
 iniparser.obj: dictionary.obj
 	@($(CC) $(CFLAGS) iniparser.c)
 
 dictionary.obj:
+	@(echo "compiling dictionary; WIN32: $(WIN32); platform: $(UNAME)")
 	@($(CC) $(CFLAGS) dictionary.c)
- 
+
 iniparser.lib:	dictionary.obj iniparser.obj
 	@(echo "linking $(OBJS)")
 	@($(LINK) $(LDFLAGS) $(OBJS))
-
 else
-# Set RANLIB to ranlib on systems that require it (Sun OS < 4, Mac OSX)
-# RANLIB  = ranlib
-RANLIB = true
-
-RM      = rm -f
-
-# Implicit rules
 
-SUFFIXES = .o .c .h .a .so .sl
-
-COMPILE.c=$(CC) $(CFLAGS) -c
-.c.o:
-	@(echo "compiling $< ...")
-	@($(COMPILE.c) -o $@ $<)
-
-
-SRCS = iniparser.c \
-	   dictionary.c
-
+# *nix (and Mac) build rules
+RM = rm -f
+COMPILE.c = $(CC) $(CFLAGS) -c
 OBJS = $(SRCS:.c=.o)
 
+all:	libiniparser.a libiniparser.so
 
-default:	libiniparser.a libiniparser.so
+.c.o:
+	@(echo "platform: $(UNAME), WIN32=$(WIN32); compiling $< ...")
+	@($(COMPILE.c) -o $@ $<)
 
 libiniparser.a:	$(OBJS)
 	@($(AR) $(ARFLAGS) libiniparser.a $(OBJS))
-	@($(RANLIB) libiniparser.a)
 
 ifeq ($(UNAME), Linux)
 libiniparser.so:	$(OBJS)
 	@$(SHLD) $(LDSHFLAGS) -o $@.0 $(OBJS) $(LDFLAGS)
 else
 libiniparser.so:	$(OBJS)
 	@$(SHLD) -o $@.0 $(LDFLAGS) $(OBJS)
 endif
 endif
 
 clean:
-	$(RM) $(OBJS)
-
-veryclean:
-	$(RM) $(OBJS) libiniparser.a libiniparser.so*
-	rm -rf ./html ; mkdir html
-	cd test ; $(MAKE) veryclean
-
-docs:
-	@(cd doc ; $(MAKE))
-
-check:
-	@(cd test ; $(MAKE))
+	$(RM) $(OBJS) libiniparser.*
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozprocess/tests/iniparser/platform.mk
@@ -0,0 +1,8 @@
+# System platform
+
+# determine if windows
+WIN32 := 0
+UNAME := $(shell uname -s)
+ifneq (,$(findstring MINGW32_NT,$(UNAME)))
+WIN32 = 1
+endif
--- a/testing/mozbase/mozprocess/tests/manifest.ini
+++ b/testing/mozbase/mozprocess/tests/manifest.ini
@@ -1,1 +1,5 @@
+# does not currently work on windows
+# see https://bugzilla.mozilla.org/show_bug.cgi?id=790765#c51
+
 [test_mozprocess.py]
+skip-if = os == 'win'
\ No newline at end of file
--- a/testing/mozbase/mozprocess/tests/test_mozprocess.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess.py
@@ -1,40 +1,59 @@
 #!/usr/bin/env python
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import optparse
 import os
 import subprocess
 import sys
 import unittest
+from mozprocess import processhandler
 from time import sleep
 
-from mozprocess import processhandler
-
 here = os.path.dirname(os.path.abspath(__file__))
 
 def make_proclaunch(aDir):
     """
         Makes the proclaunch executable.
         Params:
             aDir - the directory in which to issue the make commands
         Returns:
             the path to the proclaunch executable that is generated
     """
-    # Ideally make should take care of this, but since it doesn't,
-    # on windows anyway, let's just call out both targets explicitly.
-    p = subprocess.call(["make", "-C", "iniparser"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=aDir)
-    p = subprocess.call(["make"],stdout=subprocess.PIPE, stderr=subprocess.PIPE ,cwd=aDir)
+
     if sys.platform == "win32":
         exepath = os.path.join(aDir, "proclaunch.exe")
     else:
         exepath = os.path.join(aDir, "proclaunch")
+
+    # remove the launcher, if it already exists
+    # otherwise, if the make fails you may not notice
+    if os.path.exists(exepath):
+        os.remove(exepath)
+
+    # Ideally make should take care of both calls through recursion, but since it doesn't,
+    # on windows anyway (to file?), let's just call out both targets explicitly.
+    for command in [["make", "-C", "iniparser"],
+                    ["make"]]:
+        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=aDir)
+        stdout, stderr = process.communicate()
+        if process.returncode:
+            # SomethingBadHappen; print all the things
+            print "%s: exit %d" % (command, process.returncode)
+            print "stdout:\n%s" % stdout
+            print "stderr:\n%s" % stderr
+            raise subprocess.CalledProcessError(process.returncode, command, stdout)
+
+    # ensure the launcher now exists
+    if not os.path.exists(exepath):
+        raise AssertionError("proclaunch executable '%s' does not exist (sys.platform=%s)" % (exepath, sys.platform))
     return exepath
 
 def check_for_process(processName):
     """
         Use to determine if process of the given name is still running.
 
         Returns:
         detected -- True if process is detected to exist, False otherwise
@@ -67,22 +86,28 @@ def check_for_process(processName):
                 detected = True
                 break
 
     return detected, output
 
 
 class ProcTest(unittest.TestCase):
 
+    # whether to remove created files on exit
+    cleanup = os.environ.get('CLEANUP', 'true').lower() in ('1', 'true')
+
     @classmethod
     def setUpClass(cls):
         cls.proclaunch = make_proclaunch(here)
 
     @classmethod
     def tearDownClass(cls):
+        del cls.proclaunch
+        if not cls.cleanup:
+            return
         files = [('proclaunch',),
                  ('proclaunch.exe',),
                  ('iniparser', 'dictionary.o'),
                  ('iniparser', 'iniparser.lib'),
                  ('iniparser', 'iniparser.o'),
                  ('iniparser', 'libiniparser.a'),
                  ('iniparser', 'libiniparser.so.0'),
                  ]
@@ -91,17 +116,16 @@ class ProcTest(unittest.TestCase):
         for path in files:
             if os.path.exists(path):
                 try:
                     os.remove(path)
                 except OSError as e:
                     errors.append(str(e))
         if errors:
             raise OSError("Error(s) encountered tearing down %s.%s:\n%s" % (cls.__module__, cls.__name__, '\n'.join(errors)))
-        del cls.proclaunch
 
     def test_process_normal_finish(self):
         """Process is started, runs to completion while we wait for it"""
 
         p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"],
                                           cwd=here)
         p.run()
         p.wait()
--- a/testing/mozbase/mozprofile/mozprofile/addons.py
+++ b/testing/mozbase/mozprofile/mozprofile/addons.py
@@ -252,14 +252,14 @@ class AddonManager(object):
         for addon in self._addon_dirs:
             if os.path.isdir(addon):
                 dir_util.remove_tree(addon)
         # restore backups
         if self.backup_dir and os.path.isdir(self.backup_dir):
             extensions_path = os.path.join(self.profile, 'extensions', 'staged')
             for backup in os.listdir(self.backup_dir):
                 backup_path = os.path.join(self.backup_dir, backup)
-                addon_path = os.path.join(extensions_path, addon)
+                addon_path = os.path.join(extensions_path, backup)
                 shutil.move(backup_path, addon_path)
             if not os.listdir(self.backup_dir):
                 shutil.rmtree(self.backup_dir, ignore_errors=True)
 
     __del__ = clean_addons
--- a/testing/mozbase/mozprofile/mozprofile/permissions.py
+++ b/testing/mozbase/mozprofile/mozprofile/permissions.py
@@ -19,17 +19,17 @@ try:
     import sqlite3
 except ImportError:
     from pysqlite2 import dbapi2 as sqlite3
 import urlparse
 
 # http://hg.mozilla.org/mozilla-central/file/b871dfb2186f/build/automation.py.in#l28
 _DEFAULT_PORTS = { 'http': '8888',
                    'https': '4443',
-                   'ws': '9988',
+                   'ws': '4443',
                    'wss': '4443' }
 
 class LocationError(Exception):
     """Signifies an improperly formed location."""
 
     def __str__(self):
         s = "Bad location"
         if self.message:
@@ -204,18 +204,16 @@ class ServerLocations(object):
 
         if self.add_callback:
             self.add_callback(new_locations)
 
 
 class Permissions(object):
     """Allows handling of permissions for ``mozprofile``"""
 
-    _num_permissions = 0
-
     def __init__(self, profileDir, locations=None):
         self._profileDir = profileDir
         self._locations = ServerLocations(add_callback=self.write_db)
         if locations:
             if isinstance(locations, ServerLocations):
                 self._locations = locations
                 self._locations.add_callback = self.write_db
                 self.write_db(self._locations._locations)
@@ -243,36 +241,34 @@ class Permissions(object):
            permission INTEGER,
            expireType INTEGER,
            expireTime INTEGER)""")
 
         for location in locations:
             # set the permissions
             permissions = { 'allowXULXBL': 'noxul' not in location.options }
             for perm, allow in permissions.iteritems():
-                self._num_permissions += 1
                 if allow:
                     permission_type = 1
                 else:
                     permission_type = 2
 
                 rows = cursor.execute("PRAGMA table_info(moz_hosts)")
                 count = len(rows.fetchall())
 
                 # if the db contains 8 columns, we're using user_version 3
                 if count == 8:
-                    statement = "INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)"
+                    statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)"
                     cursor.execute("PRAGMA user_version=3;")
                 else:
-                    statement = "INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)"
+                    statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0)"
                     cursor.execute("PRAGMA user_version=2;")
 
                 cursor.execute(statement,
-                               (self._num_permissions, location.host, perm,
-                               permission_type))
+                               (location.host, perm, permission_type))
 
         # Commit and close
         permDB.commit()
         cursor.close()
 
     def network_prefs(self, proxy=None):
         """
         take known locations and generate preferences to handle permissions and proxy
--- a/testing/mozbase/mozprofile/mozprofile/prefs.py
+++ b/testing/mozbase/mozprofile/mozprofile/prefs.py
@@ -3,16 +3,17 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 """
 user preferences
 """
 
 __all__ = ('PreferencesReadError', 'Preferences')
 
+import mozfile
 import os
 import re
 import tokenize
 from ConfigParser import SafeConfigParser as ConfigParser
 from StringIO import StringIO
 
 try:
     import json
@@ -86,17 +87,17 @@ class Preferences(object):
         """read preferences from a file"""
 
         section = None # for .ini files
         basename = os.path.basename(path)
         if ':' in basename:
             # section of INI file
             path, section = path.rsplit(':', 1)
 
-        if not os.path.exists(path):
+        if not os.path.exists(path) and not mozfile.is_url(path):
             raise PreferencesReadError("'%s' does not exist" % path)
 
         if section:
             try:
                 return cls.read_ini(path, section)
             except PreferencesReadError:
                 raise
             except Exception, e:
@@ -115,33 +116,33 @@ class Preferences(object):
                 raise PreferencesReadError("Could not recognize format of %s" % path)
 
 
     @classmethod
     def read_ini(cls, path, section=None):
         """read preferences from an .ini file"""
 
         parser = ConfigParser()
-        parser.read(path)
+        parser.readfp(mozfile.load(path))
 
         if section:
             if section not in parser.sections():
                 raise PreferencesReadError("No section '%s' in %s" % (section, path))
             retval = parser.items(section, raw=True)
         else:
             retval = parser.defaults().items()
 
         # cast the preferences since .ini is just strings
         return [(i, cls.cast(j)) for i, j in retval]
 
     @classmethod
     def read_json(cls, path):
         """read preferences from a JSON blob"""
 
-        prefs = json.loads(file(path).read())
+        prefs = json.loads(mozfile.load(path).read())
 
         if type(prefs) not in [list, dict]:
             raise PreferencesReadError("Malformed preferences: %s" % path)
         if isinstance(prefs, list):
             if [i for i in prefs if type(i) != list or len(i) != 2]:
                 raise PreferencesReadError("Malformed preferences: %s" % path)
             values = [i[1] for i in prefs]
         elif isinstance(prefs, dict):
@@ -156,17 +157,17 @@ class Preferences(object):
 
     @classmethod
     def read_prefs(cls, path, pref_setter='user_pref'):
         """read preferences from (e.g.) prefs.js"""
 
         comment = re.compile('/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/', re.MULTILINE)
 
         marker = '##//' # magical marker
-        lines = [i.strip() for i in file(path).readlines() if i.strip()]
+        lines = [i.strip() for i in mozfile.load(path).readlines() if i.strip()]
         _lines = []
         for line in lines:
             if line.startswith(('#', '//')):
                 continue
             if '//' in line:
                 line = line.replace('//', marker)
             _lines.append(line)
         string = '\n'.join(_lines)
--- a/testing/mozbase/mozprofile/setup.py
+++ b/testing/mozbase/mozprofile/setup.py
@@ -1,21 +1,22 @@
 # 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/.
 
 import sys
 from setuptools import setup
 
-PACKAGE_VERSION = '0.7'
+PACKAGE_VERSION = '0.9'
 
 # we only support python 2 right now
 assert sys.version_info[0] == 2
 
-deps = ["ManifestDestiny >= 0.5.4"]
+deps = ["ManifestDestiny >= 0.5.4",
+        "mozfile >= 0.6"]
 # version-dependent dependencies
 try:
     import json
 except ImportError:
     deps.append('simplejson')
 try:
     import sqlite3
 except ImportError:
@@ -32,20 +33,21 @@ setup(name='mozprofile',
                    'Natural Language :: English',
                    'Operating System :: OS Independent',
                    'Programming Language :: Python',
                    'Topic :: Software Development :: Libraries :: Python Modules',
                    ],
       keywords='mozilla',
       author='Mozilla Automation and Tools team',
       author_email='tools@lists.mozilla.org',
-      url='https://wiki.mozilla.org/Auto-tools/Projects/MozBase',
+      url='https://wiki.mozilla.org/Auto-tools/Projects/Mozbase',
       license='MPL 2.0',
       packages=['mozprofile'],
       include_package_data=True,
       zip_safe=False,
       install_requires=deps,
+      tests_require=['mozhttpd', 'mozfile'],
       entry_points="""
       # -*- Entry points: -*-
       [console_scripts]
       mozprofile = mozprofile:cli
       """,
     )
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..26f28f099d24bec509e3abd991ff453c667543d6
GIT binary patch
literal 530
zc$^FHW@Zs#U|`^2a4J``_nh^LHI0#hVG9!j12=;VLuOuaNn%cpUQtR~Xb2|*^KFSV
zzmq^*TEWf0$nq7a60Es&lD~hmfyA-%pER}Nt}NMdpu{QSs!RE$=I*NZA5zU0vU;og
zJT9L;^<1u7{@c%g=IyJ$^k&m!)hhP89bUeF3>N%T{k-YsS&^@8S!(}Q8<u)`+}`FW
z%|115f`O-yld_O&#D0+-k(!6C*UVC`Ke0sNd(x&~0;wl+%@&ooIIL@SxBJYuNjjrQ
zH|z3r_nJ7}vq$2yrtb353Q4YrOgcGLV}ar8GmdOVqWWjPl=EydTfKU_s(x$a+i9mx
zI9T56J&_?0m0n!F?UB%p<o^o0-tw&2+S>Q_wz}B%eaXeVcS2^}<v)7i@K#r|O^f5S
zIZfjaTz=EHGxg8)r+Tk0rG+wgugUJe7iE7$!nd_;(JO&t_8Ql@4X&5QcFmDCzkDxg
z?S`AT)Srl(EITUkT<~c{iJew~(ej4FGmhIdrpUzi>?_-5uCg@Xd|ql{f!Lg=1ot~l
zdv0g&-QwD2_-Xa##1r;yH`E0`8D0AC{j8Qbz?+dtju}^ENicu_kjv1}2x6f`9V;a2
W(4sBCo0ScsiIE`?NUsLzW&i+p&C!|w
old mode 100644
new mode 100755
--- a/testing/mozbase/mozprofile/tests/bug785146.py
+++ b/testing/mozbase/mozprofile/tests/bug785146.py
@@ -1,37 +1,36 @@
 #!/usr/bin/env python
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import mozfile
 import os
 import shutil
+import tempfile
+import unittest
+from mozprofile.permissions import Permissions
 try:
     import sqlite3
 except ImportError:
     from pysqlite2 import dbapi2 as sqlite3
-import tempfile
-import unittest
-from mozprofile.permissions import Permissions
+
 
 class PermissionsTest(unittest.TestCase):
 
     locations = """http://mochi.test:8888  primary,privileged
 http://127.0.0.1:80             noxul
 http://127.0.0.1:8888           privileged
 """
 
-    profile_dir = None
-    locations_file = None
-
     def setUp(self):
         self.profile_dir = tempfile.mkdtemp()
-        self.locations_file = tempfile.NamedTemporaryFile()
+        self.locations_file = mozfile.NamedTemporaryFile()
         self.locations_file.write(self.locations)
         self.locations_file.flush()
 
     def tearDown(self):
         if self.profile_dir:
             shutil.rmtree(self.profile_dir)
         if self.locations_file:
             self.locations_file.close()
--- a/testing/mozbase/mozprofile/tests/permissions.py
+++ b/testing/mozbase/mozprofile/tests/permissions.py
@@ -1,37 +1,38 @@
 #!/usr/bin/env python
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import mozfile
 import os
 import shutil
+import tempfile
+import unittest
+from mozprofile.permissions import Permissions
 try:
     import sqlite3
 except ImportError:
     from pysqlite2 import dbapi2 as sqlite3
-import tempfile
-import unittest
-from mozprofile.permissions import Permissions
 
 class PermissionsTest(unittest.TestCase):
 
     locations = """http://mochi.test:8888  primary,privileged
 http://127.0.0.1:80             noxul
 http://127.0.0.1:8888           privileged
 """
 
     profile_dir = None
     locations_file = None
 
     def setUp(self):
         self.profile_dir = tempfile.mkdtemp()
-        self.locations_file = tempfile.NamedTemporaryFile()
+        self.locations_file = mozfile.NamedTemporaryFile()
         self.locations_file.write(self.locations)
         self.locations_file.flush()
 
     def tearDown(self):
         if self.profile_dir:
             shutil.rmtree(self.profile_dir)
         if self.locations_file:
             self.locations_file.close()
@@ -137,17 +138,17 @@ http://127.0.0.1:8888           privileg
         self.assertEqual(user_prefs[0], ('network.proxy.type', 2))
         self.assertEqual(user_prefs[1][0], 'network.proxy.autoconfig_url')
 
         origins_decl = "var origins = ['http://mochi.test:8888', 'http://127.0.0.1:80', 'http://127.0.0.1:8888'];"
         self.assertTrue(origins_decl in user_prefs[1][1])
 
         proxy_check = ("if (isHttp) return 'PROXY mochi.test:8888';",
                        "if (isHttps) return 'PROXY mochi.test:4443';",
-                       "if (isWebSocket) return 'PROXY mochi.test:9988';",
+                       "if (isWebSocket) return 'PROXY mochi.test:4443';",
                        "if (isWebSocketSSL) return 'PROXY mochi.test:4443';")
         self.assertTrue(all(c in user_prefs[1][1] for c in proxy_check))
 
     def verify_user_version(self, version):
         """Verifies that we call INSERT statements using the correct number
         of columns for existing databases.
         """
         self.write_perm_db(version=version)
--- a/testing/mozbase/mozprofile/tests/server_locations.py
+++ b/testing/mozbase/mozprofile/tests/server_locations.py
@@ -1,14 +1,15 @@
 #!/usr/bin/env python
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import mozfile
 import os
 import shutil
 import tempfile
 import unittest
 from mozprofile.permissions import ServerLocations, \
     MissingPrimaryLocationError, MultiplePrimaryLocationsError, \
     DuplicateLocationError, BadPortLocationError, LocationsSyntaxError
 
@@ -41,17 +42,17 @@ http://example.org:80           privileg
 
     def compare_location(self, location, scheme, host, port, options):
         self.assertEqual(location.scheme, scheme)
         self.assertEqual(location.host, host)
         self.assertEqual(location.port, port)
         self.assertEqual(location.options, options)
 
     def create_temp_file(self, contents):
-        f = tempfile.NamedTemporaryFile()
+        f = mozfile.NamedTemporaryFile()
         f.write(contents)
         f.flush()
         return f
 
     def test_server_locations(self):
         # write a permissions file
         f = self.create_temp_file(self.locations)
 
@@ -135,17 +136,17 @@ http://example.org:80           privileg
                               ['privileged'])
 
         locations.add_host('a.b.c')
 
         # callback should be just for one location
         self.assertEqual(len(c.last_locations), 1)
         self.compare_location(c.last_locations[0], 'http', 'a.b.c', '80',
                               ['privileged'])
-        
+
         # read a second file, which should generate a callback with both
         # locations.
         f = self.create_temp_file(self.locations_no_primary)
         locations.read(f.name)
         self.assertEqual(len(c.last_locations), 2)
 
 
 if __name__ == '__main__':
--- a/testing/mozbase/mozprofile/tests/test_preferences.py
+++ b/testing/mozbase/mozprofile/tests/test_preferences.py
@@ -1,27 +1,35 @@
 #!/usr/bin/env python
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import mozfile
+import mozhttpd
 import os
 import shutil
 import tempfile
 import unittest
 from mozprofile.cli import MozProfileCLI
 from mozprofile.prefs import Preferences
 from mozprofile.profile import Profile
 
 here = os.path.dirname(os.path.abspath(__file__))
 
 class PreferencesTest(unittest.TestCase):
     """test mozprofile preference handling"""
 
+    # preferences from files/prefs_with_comments.js
+    _prefs_with_comments = {'browser.startup.homepage': 'http://planet.mozilla.org',
+                            'zoom.minPercent': 30,
+                            'zoom.maxPercent': 300,
+                            'webgl.verbose': 'false'}
+
     def run_command(self, *args):
         """
         invokes mozprofile command line via the CLI factory
         - args : command line arguments (equivalent of sys.argv[1:])
         """
 
         # instantiate the factory
         cli = MozProfileCLI(list(args))
@@ -226,33 +234,54 @@ user_pref("webgl.force-enabled", true);
                   'zoom.minPercent': 30,
                   'zoom.maxPercent': 300}
 
         # make a Preferences manager with the testing preferences
         preferences = Preferences(_prefs)
 
         # write them to a temporary location
         path = None
+        read_prefs = None
         try:
-            with tempfile.NamedTemporaryFile(suffix='.js', delete=False) as f:
+            with mozfile.NamedTemporaryFile(suffix='.js', delete=False) as f:
                 path = f.name
                 preferences.write(f, _prefs)
 
             # read them back and ensure we get what we put in
-            self.assertEqual(dict(Preferences.read_prefs(path)), _prefs)
+            read_prefs = dict(Preferences.read_prefs(path))
 
         finally:
             # cleanup
-            os.remove(path)
+            if path and os.path.exists(path):
+                os.remove(path)
+
+        self.assertEqual(read_prefs, _prefs)
 
     def test_read_prefs_with_comments(self):
         """test reading preferences from a prefs.js file that contains comments"""
 
-        _prefs = {'browser.startup.homepage': 'http://planet.mozilla.org',
-                  'zoom.minPercent': 30,
-                  'zoom.maxPercent': 300,
-                  'webgl.verbose': 'false'}
         path = os.path.join(here, 'files', 'prefs_with_comments.js')
-        self.assertEqual(dict(Preferences.read_prefs(path)), _prefs)
+        self.assertEqual(dict(Preferences.read_prefs(path)), self._prefs_with_comments)
+
+    def test_read_prefs_ttw(self):
+        """test reading preferences through the web via mozhttpd"""
+
+        # create a MozHttpd instance
+        docroot = os.path.join(here, 'files')
+        host = '127.0.0.1'
+        port = 8888
+        httpd = mozhttpd.MozHttpd(host=host, port=port, docroot=docroot)
 
+        # create a preferences instance
+        prefs = Preferences()
+
+        try:
+            # start server
+            httpd.start(block=False)
+
+            # read preferences through the web
+            read = prefs.read_prefs('http://%s:%d/prefs_with_comments.js' % (host, port))
+            self.assertEqual(dict(read), self._prefs_with_comments)
+        finally:
+            httpd.stop()
 
 if __name__ == '__main__':
     unittest.main()
old mode 100644
new mode 100755
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -437,17 +437,17 @@ make-stage-dir:
 	$(NSINSTALL) -D $(PKG_STAGE)/peptest
 	$(NSINSTALL) -D $(PKG_STAGE)/mozbase
 	$(NSINSTALL) -D $(PKG_STAGE)/modules
 
 stage-b2g: make-stage-dir
 	$(NSINSTALL) $(topsrcdir)/b2g/test/b2g-unittest-requirements.txt $(PKG_STAGE)/b2g
 
 stage-config: make-stage-dir
-	$(MAKE) -C $(DEPTH)/testing/config stage-package
+	$(NSINSTALL) $(topsrcdir)/testing/config/mozharness_config.py $(PKG_STAGE)/config
 
 stage-mochitest: make-stage-dir
 	$(MAKE) -C $(DEPTH)/testing/mochitest stage-package
 ifeq ($(MOZ_BUILD_APP),mobile/android)
 	$(NSINSTALL) $(DEPTH)/mobile/android/base/fennec_ids.txt $(PKG_STAGE)/mochitest
 endif
 
 stage-reftest: make-stage-dir
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -343,18 +343,20 @@
                 timeUpdateCount : 0,
                 maxCurrentTimeSeen : 0,
                 isAudioOnly : false,
 
                 setupStatusFader : function(immediate) {
                     // Since the play button will be showing, we don't want to
                     // show the throbber behind it. The throbber here will
                     // only show if needed after the play button has been pressed.
-                    if (!this.clickToPlay.hidden)
+                    if (!this.clickToPlay.hidden) {
+                        this.startFadeOut(this.statusOverlay, true);
                         return;
+                    }
 
                     var show = false;
                     if (this.video.seeking ||
                         this.video.error ||
                         this.video.networkState == this.video.NETWORK_NO_SOURCE ||
                         (this.video.networkState == this.video.NETWORK_LOADING &&
                          (this.video.paused || this.video.ended
                            ? this.video.readyState < this.video.HAVE_CURRENT_DATA
@@ -458,16 +460,36 @@
                     // Preserve Statistics when toggling fullscreen mode due to bug 714071.
                     if (XPCNativeWrapper.unwrap(this.video).mozMediaStatisticsShowing)
                         this.showStatistics(true);
 
                     this._handleCustomEventsBound = this.handleCustomEvents.bind(this);
                     this.video.addEventListener("media-showStatistics", this._handleCustomEventsBound, false, true);
                 },
 
+                setupNewLoadState : function() {
+                    // videocontrols.css hides the control bar by default, because if script
+                    // is disabled our binding's script is disabled too (bug 449358). Thus,
+                    // the controls are broken and we don't want them shown. But if script is
+                    // enabled, the code here will run and can explicitly unhide the controls.
+                    //
+                    // For videos with |autoplay| set, we'll leave the controls initially hidden,
+                    // so that they don't get in the way of the playing video. Otherwise we'll
+                    // go ahead and reveal the controls now, so they're an obvious user cue.
+                    //
+                    // (Note: the |controls| attribute is already handled via layout/style/html.css)
+                    var shouldShow = this.video.paused &&
+                       (!(this.video.autoplay && this.video.mozAutoplayEnabled) ||
+                        !this.dynamicControls);
+                    // Hide the overlay if the video time is non-zero or if an error occurred to workaround bug 718107.
+                    this.startFade(this.clickToPlay, shouldShow && !this.isAudioOnly &&
+                                   this.video.currentTime == 0 && !this.hasError(), true);
+                    this.startFade(this.controlBar, shouldShow, true);
+                },
+
                 handleCustomEvents : function (e) {
                     if (!e.isTrusted)
                         return;
                     this.showStatistics(e.detail);
                 },
 
                 get dynamicControls() {
                     // Don't fade controls for <audio> elements.
@@ -548,16 +570,18 @@
                             break;
                         case "loadstart":
                             this.maxCurrentTimeSeen = 0;
                             this.controlsSpacer.removeAttribute("aria-label");
                             this.statusOverlay.removeAttribute("error");
                             this.statusIcon.setAttribute("type", "throbber");
                             this.isAudioOnly = (this.video instanceof HTMLAudioElement);
                             this.setPlayButtonState(true);
+                            this.setupNewLoadState();
+                            this.setupStatusFader();
                             break;
                         case "progress":
                             this.statusIcon.removeAttribute("stalled");
                             this.showBuffered();
                             this.setupStatusFader();
                             break;
                         case "stalled":
                             this.statusIcon.setAttribute("stalled", "true");
@@ -591,16 +615,17 @@
                             // responses (don't yank the thumb away from the user)
                             if (this.scrubber.isDragging)
                                 return;
 
                             this.showPosition(currentTime, duration);
                             break;
                         case "emptied":
                             this.bufferBar.value = 0;
+                            this.showPosition(0, 0);
                             break;
                         case "seeking":
                             this.showBuffered();
                             this.statusIcon.setAttribute("type", "throbber");
                             this.setupStatusFader();
                             break;
                         case "waiting":
                             this.statusIcon.setAttribute("type", "throbber");
@@ -1374,33 +1399,17 @@
                     this.stats.readyState = document.getAnonymousElementByAttribute(binding, "class", "statReadyState");
                     this.stats.netState   = document.getAnonymousElementByAttribute(binding, "class", "statNetState");
                     this.stats.framesParsed    = document.getAnonymousElementByAttribute(binding, "class", "statFramesParsed");
                     this.stats.framesDecoded   = document.getAnonymousElementByAttribute(binding, "class", "statFramesDecoded");
                     this.stats.framesPresented = document.getAnonymousElementByAttribute(binding, "class", "statFramesPresented");
                     this.stats.framesPainted   = document.getAnonymousElementByAttribute(binding, "class", "statFramesPainted");
 
                     this.setupInitialState();
-
-                    // videocontrols.css hides the control bar by default, because if script
-                    // is disabled our binding's script is disabled too (bug 449358). Thus,
-                    // the controls are broken and we don't want them shown. But if script is
-                    // enabled, the code here will run and can explicitly unhide the controls.
-                    //
-                    // For videos with |autoplay| set, we'll leave the controls initially hidden,
-                    // so that they don't get in the way of the playing video. Otherwise we'll
-                    // go ahead and reveal the controls now, so they're an obvious user cue.
-                    //
-                    // (Note: the |controls| attribute is already handled via layout/style/html.css)
-                    var shouldShow = (!(this.video.autoplay && this.video.mozAutoplayEnabled) || !this.dynamicControls);
-                    // Hide the overlay if the video time is non-zero or if the video is already playing
-                    // or if an error occurred to workaround bug 718107.
-                    this.startFade(this.clickToPlay, shouldShow && this.video.paused && !this.isAudioOnly &&
-                                   this.video.currentTime == 0 && !this.hasError(), true);
-                    this.startFade(this.controlBar, shouldShow, true);
+                    this.setupNewLoadState();
 
                     // Use the handleEvent() callback for all media events.
                     // The "error" event listener must capture, so that it can trap error events
                     // from the <source> children, which don't bubble.
                     for each (let event in this.videoEvents)
                         this.video.addEventListener(event, this, (event == "error") ? true : false);
 
                     var self = this;
@@ -1425,16 +1434,17 @@
                       this.controlBar.classList.add("audio");
                     } else {
                       addListener(this.muteButton, "mouseover", this.onVolumeMouseInOut);
                       addListener(this.muteButton, "mouseout", this.onVolumeMouseInOut);
                       addListener(this.volumeStack, "mouseover", this.onVolumeMouseInOut);
                       addListener(this.volumeStack, "mouseout", this.onVolumeMouseInOut);
                     }
 
+                    addListener(this.videocontrols, "resizevideocontrols", this.adjustControlSize);
                     addListener(this.videocontrols, "transitionend", this.onTransitionEnd);
                     addListener(this.video.ownerDocument, "mozfullscreenchange", this.onFullscreenChange);
                     addListener(this.video, "keypress", this.keyHandler);
 
                     this.log("--- videocontrols initialized ---");
                 }
             };
             this.Utils.init(this);
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -228,17 +228,16 @@ add_tier_dir('platform', 'addon-sdk')
 if CONFIG['MOZ_MAPINFO']:
     add_tier_dir('platform', 'tools/codesighs')
 
 if CONFIG['ENABLE_MARIONETTE']:
     add_tier_dir('platform', 'testing/marionette')
 
 if CONFIG['ENABLE_TESTS']:
     add_tier_dir('platform', [
-        'testing/config',
         'testing/mochitest',
         'testing/xpcshell',
         'testing/tools/screenshot',
         'testing/peptest',
         'testing/profiles',
         'testing/mozbase',
         'testing/modules',
     ])
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3880,17 +3880,18 @@ XREMain::XRE_mainRun()
 }
 
 /*
  * XRE_main - A class based main entry point used by most platforms.
  */
 int
 XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
 {
-  GeckoProfilerInitRAII profilerGuard;
+  char aLocal;
+  GeckoProfilerInitRAII profilerGuard(&aLocal);
   PROFILER_LABEL("Startup", "XRE_Main");
 
   nsresult rv = NS_OK;
 
   gArgc = argc;
   gArgv = argv;
 
   NS_ENSURE_TRUE(aAppData, 2);
@@ -4075,17 +4076,18 @@ public:
     }
   }
   HRESULT mResult;
 };
 
 int
 XRE_mainMetro(int argc, char* argv[], const nsXREAppData* aAppData)
 {
-  GeckoProfilerInitRAII profilerGuard;
+  char aLocal;
+  GeckoProfilerInitRAII profilerGuard(&aLocal);
   PROFILER_LABEL("Startup", "XRE_Main");
 
   nsresult rv = NS_OK;
 
   xreMainPtr = new XREMain();
   if (!xreMainPtr) {
     return 1;
   }
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -277,17 +277,18 @@ SetTaskbarGroupId(const nsString& aId)
 nsresult
 XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      GeckoProcessType aProcess)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
-  profiler_init();
+  char aLocal;
+  profiler_init(&aLocal);
   PROFILER_LABEL("Startup", "XRE_InitChildProcess");
 
   sChildProcessType = aProcess;
 
   // Complete 'task_t' exchange for Mac OS X. This structure has the same size
   // regardless of architecture so we don't have any cross-arch issues here.
 #ifdef XP_MACOSX
   if (aArgc < 1)
--- a/tools/profiler/BreakpadSampler.cpp
+++ b/tools/profiler/BreakpadSampler.cpp
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // System
 #include <string>
 #include <stdio.h>
 #include <errno.h>
-#include <iostream>
+#include <ostream>
 #include <fstream>
 #include <sstream>
 #if defined(ANDROID)
 # include "android-signal-defs.h"
 #endif
 
 // Profiler
 #include "PlatformMacros.h"
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -74,17 +74,17 @@
 
 // Main thread specilization to avoid TLS lookup for performance critical use.
 #define PROFILER_MAIN_THREAD_LABEL(name_space, info) do {} while (0)
 #define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, format, ...) do {} while (0)
 
 // Initilize the profiler TLS, signal handlers on linux. If MOZ_PROFILER_STARTUP
 // is set the profiler will be started. This call must happen before any other
 // sampler calls. Particularly sampler_label/sampler_marker.
-static inline void profiler_init() {};
+static inline void profiler_init(void* stackTop) {};
 
 // Clean up the profiler module, stopping it if required. This function may
 // also save a shutdown profile if requested. No profiler calls should happen
 // after this point and all pseudo labels should have been popped.
 static inline void profiler_shutdown() {};
 
 // Start the profiler with the selected options. The samples will be
 // recorded in a circular buffer.
@@ -130,34 +130,34 @@ static inline void profiler_print_locati
 // Discard the profile, throw away the profile and notify 'profiler-locked'.
 // This function is to be used when entering private browsing to prevent
 // the profiler from collecting sensitive data.
 static inline void profiler_lock() {}
 
 // Re-enable the profiler and notify 'profiler-unlocked'.
 static inline void profiler_unlock() {}
 
-static inline void profiler_register_thread(const char* name) {}
+static inline void profiler_register_thread(const char* name, void* stackTop) {}
 static inline void profiler_unregister_thread() {}
 
 // Call by the JSRuntime's operation callback. This is used to enable
 // profiling on auxilerary threads.
 static inline void profiler_js_operation_callback() {}
 
 static inline double profiler_time() { return 0; }
 
 #else
 
 #include "GeckoProfilerImpl.h"
 
 #endif
 
 class GeckoProfilerInitRAII {
 public:
-  GeckoProfilerInitRAII() {
-    profiler_init();
+  GeckoProfilerInitRAII(void* stackTop) {
+    profiler_init(stackTop);
   }
   ~GeckoProfilerInitRAII() {
     profiler_shutdown();
   }
 };
 
 #endif // ifndef SAMPLER_H
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -37,33 +37,33 @@ const double* mozilla_sampler_get_respon
 void mozilla_sampler_save();
 
 char* mozilla_sampler_get_profile();
 
 JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
 
 const char** mozilla_sampler_get_features();
 
-void mozilla_sampler_init();
+void mozilla_sampler_init(void* stackTop);
 
 void mozilla_sampler_shutdown();
 
 void mozilla_sampler_print_location1();
 void mozilla_sampler_print_location2();
 
 // Lock the profiler. When locked the profiler is (1) stopped,
 // (2) profile data is cleared, (3) profiler-locked is fired.
 // This is used to lock down the profiler during private browsing
 void mozilla_sampler_lock();
 
 // Unlock the profiler, leaving it stopped and fires profiler-unlocked.
 void mozilla_sampler_unlock();
 
 // Register/unregister threads with the profiler
-bool mozilla_sampler_register_thread(const char* name);
+bool mozilla_sampler_register_thread(const char* name, void* stackTop);
 void mozilla_sampler_unregister_thread();
 
 double mozilla_sampler_time();
 
 /* Returns true if env var SPS_NEW is set to anything, else false. */
 extern bool sps_version2();
 
 #endif
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -47,19 +47,19 @@ extern bool stack_key_initialized;
 # elif defined(_MSC_VER)
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # else
 #  define SAMPLE_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
 # endif
 #endif
 
 static inline
-void profiler_init()
+void profiler_init(void* stackTop)
 {
-  mozilla_sampler_init();
+  mozilla_sampler_init(stackTop);
 }
 
 static inline
 void profiler_shutdown()
 {
   mozilla_sampler_shutdown();
 }
 
@@ -136,19 +136,19 @@ void profiler_lock()
 
 static inline
 void profiler_unlock()
 {
   return mozilla_sampler_unlock();
 }
 
 static inline
-void profiler_register_thread(const char* name)
+void profiler_register_thread(const char* name, void* stackTop)
 {
-  mozilla_sampler_register_thread(name);
+  mozilla_sampler_register_thread(name, stackTop);
 }
 
 static inline
 void profiler_unregister_thread()
 {
   mozilla_sampler_unregister_thread();
 }
 
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -1,14 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include <iostream>
+#include <ostream>
 #include "GeckoProfilerImpl.h"
 #include "platform.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 // JSON
 #include "JSObjectBuilder.h"
 #include "JSCustomObjectBuilder.h"
--- a/tools/profiler/SaveProfileTask.h
+++ b/tools/profiler/SaveProfileTask.h
@@ -11,17 +11,17 @@
 #include "nsIXULRuntime.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsXULAppAPI.h"
 #include "jsfriendapi.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIProfileSaveEvent.h"
 
-#include <iostream>
+#include <ostream>
 #include <fstream>
 
 #ifdef XP_WIN
  #include <windows.h>
  #define getpid GetCurrentProcessId
 #else
  #include <unistd.h>
 #endif
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -557,17 +557,17 @@ static void print_callback(const Profile
     case 'c':
       printf_stderr("  %s\n", tagStringData);
   }
 }
 
 void mozilla_sampler_print_location1()
 {
   if (!stack_key_initialized)
-    profiler_init();
+    profiler_init(NULL);
 
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(false);
     return;
   }
 
   ThreadProfile threadProfile("Temp", PROFILE_DEFAULT_ENTRY, stack,
--- a/tools/profiler/UnwinderThread2.cpp
+++ b/tools/profiler/UnwinderThread2.cpp
@@ -21,17 +21,17 @@
 # define VALGRIND_MAKE_MEM_UNDEFINED(_addr,_len) ((void)0)
 #endif
 
 #include "mozilla/arm.h"
 #include "mozilla/StandardInteger.h"
 #include "PlatformMacros.h"
 
 #include "platform.h"
-#include <iostream>
+#include <ostream>
 
 #include "ProfileEntry.h"
 #include "UnwinderThread2.h"
 
 #if !defined(SPS_OS_windows)
 # include <sys/time.h>
 # include <unistd.h>
 # include <pthread.h>
@@ -78,16 +78,20 @@ void uwt__stop()
 void uwt__deinit()
 {
 }
 
 void uwt__register_thread_for_profiling ( void* stackTop )
 {
 }
 
+void uwt__unregister_thread_for_profiling()
+{
+}
+
 // RUNS IN SIGHANDLER CONTEXT
 UnwinderThreadBuffer* uwt__acquire_empty_buffer()
 {
   return NULL;
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void
@@ -117,16 +121,19 @@ static void* unwind_thr_fn ( void* exit_
 static pthread_t unwind_thr;
 static int       unwind_thr_exit_now = 0; // RACED ON
 
 // Threads must be registered with this file before they can be
 // sampled.  So that we know the max safe stack address for each
 // registered thread.
 static void thread_register_for_profiling ( void* stackTop );
 
+// Unregister a thread.
+static void thread_unregister_for_profiling();
+
 // Frees some memory when the unwinder thread is shut down.
 static void do_breakpad_unwind_Buffer_free_singletons();
 
 // RUNS IN SIGHANDLER CONTEXT
 // Acquire an empty buffer and mark it as FILLING
 static UnwinderThreadBuffer* acquire_empty_buffer();
 
 // RUNS IN SIGHANDLER CONTEXT
@@ -171,16 +178,21 @@ void uwt__deinit()
   do_breakpad_unwind_Buffer_free_singletons();
 }
 
 void uwt__register_thread_for_profiling(void* stackTop)
 {
   thread_register_for_profiling(stackTop);
 }
 
+void uwt__unregister_thread_for_profiling()
+{
+  thread_unregister_for_profiling();
+}
+
 // RUNS IN SIGHANDLER CONTEXT
 UnwinderThreadBuffer* uwt__acquire_empty_buffer()
 {
   return acquire_empty_buffer();
 }
 
 // RUNS IN SIGHANDLER CONTEXT
 void
@@ -343,31 +355,39 @@ typedef
   StackLimit;
 
 /* Globals -- the buffer array */
 #define N_UNW_THR_BUFFERS 10
 /*SL*/ static UnwinderThreadBuffer** g_buffers     = NULL;
 /*SL*/ static uint64_t               g_seqNo       = 0;
 /*SL*/ static SpinLock               g_spinLock    = { 0 };
 
-/* Globals -- the thread array */
-#define N_SAMPLING_THREADS 10
-/*SL*/ static StackLimit g_stackLimits[N_SAMPLING_THREADS];
-/*SL*/ static int        g_stackLimitsUsed = 0;
+/* Globals -- the thread array.  The array is dynamically expanded on
+   demand.  The spinlock must be held when accessing g_stackLimits,
+   g_stackLimits[some index], g_stackLimitsUsed and g_stackLimitsSize.
+   However, the spinlock must not be held when calling malloc to
+   allocate or expand the array, as that would risk deadlock against a
+   sampling thread that holds the malloc lock and is trying to acquire
+   the spinlock. */
+/*SL*/ static StackLimit* g_stackLimits     = NULL;
+/*SL*/ static size_t      g_stackLimitsUsed = 0;
+/*SL*/ static size_t      g_stackLimitsSize = 0;
 
 /* Stats -- atomically incremented, no lock needed */
 static uintptr_t g_stats_totalSamples = 0; // total # sample attempts
 static uintptr_t g_stats_noBuffAvail  = 0; // # failed due to no buffer avail
+static uintptr_t g_stats_thrUnregd    = 0; // # failed due to unregistered thr
 
 /* We must be VERY CAREFUL what we do with the spinlock held.  The
    only thing it is safe to do with it held is modify (viz, read or
    write) g_buffers, g_buffers[], g_seqNo, g_buffers[]->state,
-   g_stackLimits[] and g_stackLimitsUsed.  No arbitrary computations,
-   no syscalls, no printfs, no file IO, and absolutely no dynamic
-   memory allocation (else we WILL eventually deadlock).
+   g_stackLimits, g_stackLimits[], g_stackLimitsUsed and
+   g_stackLimitsSize.  No arbitrary computations, no syscalls, no
+   printfs, no file IO, and absolutely no dynamic memory allocation
+   (else we WILL eventually deadlock).
 
    This applies both to the signal handler and to the unwinder thread.
 */
 
 //// END type UnwindThreadBuffer
 //////////////////////////////////////////////////////////
 
 // fwds
@@ -471,70 +491,201 @@ static void atomic_INC(uintptr_t* loc)
   while (1) {
     uintptr_t old = *loc;
     uintptr_t nyu = old + 1;
     bool ok = do_CASW( loc, old, nyu );
     if (ok) break;
   }
 }
 
-/* Register a thread for profiling.  It must not be allowed to receive
-   signals before this is done, else the signal handler will
-   MOZ_ASSERT. */
+// Registers a thread for profiling.  Detects and ignores duplicate
+// registration.
 static void thread_register_for_profiling(void* stackTop)
 {
-  int i;
-  /* Minimal sanity check on stackTop */
-  MOZ_ASSERT( (void*)&i < stackTop );
+  pthread_t me = pthread_self();
 
   spinLock_acquire(&g_spinLock);
 
-  pthread_t me = pthread_self();
-  for (i = 0; i < g_stackLimitsUsed; i++) {
-    /* check for duplicate registration */
-    MOZ_ASSERT(g_stackLimits[i].thrId != me);
+  // tmp copy of g_stackLimitsUsed, to avoid racing in message printing
+  int n_used;
+
+  // Ignore spurious calls which aren't really registering anything.
+  if (stackTop == NULL) {
+    n_used = g_stackLimitsUsed;
+    spinLock_release(&g_spinLock);
+    LOGF("BPUnw: [%d total] thread_register_for_profiling"
+         "(me=%p, stacktop=NULL) (IGNORED)", n_used, (void*)me);
+    return;
+  }
+
+  /* Minimal sanity check on stackTop */
+  MOZ_ASSERT((void*)&n_used/*any auto var will do*/ < stackTop);
+
+  bool is_dup = false;
+  for (size_t i = 0; i < g_stackLimitsUsed; i++) {
+    if (g_stackLimits[i].thrId == me) {
+      is_dup = true;
+      break;
+    }
+  }
+
+  if (is_dup) {
+    /* It's a duplicate registration.  Ignore it: drop the lock and
+       return. */
+    n_used = g_stackLimitsUsed;
+    spinLock_release(&g_spinLock);
+
+    LOGF("BPUnw: [%d total] thread_register_for_profiling"
+         "(me=%p, stacktop=%p) (DUPLICATE)", n_used, (void*)me, stackTop);
+    return;
   }
-  if (!(g_stackLimitsUsed < N_SAMPLING_THREADS))
-    MOZ_CRASH();  // Don't continue -- we'll get memory corruption.
+
+  /* Make sure the g_stackLimits array is large enough to accommodate
+     this new entry.  This is tricky.  If it isn't large enough, we
+     can malloc a larger version, but we have to do that without
+     holding the spinlock, else we risk deadlock.  The deadlock
+     scenario is:
+
+     Some other thread that is being sampled
+                                        This thread
+
+     call malloc                        call this function
+     acquire malloc lock                acquire the spinlock
+     (sampling signal)                  discover thread array not big enough,
+     call uwt__acquire_empty_buffer       call malloc to make it larger
+     acquire the spinlock               acquire malloc lock
+
+     This gives an inconsistent lock acquisition order on the malloc
+     lock and spinlock, hence risk of deadlock.
+
+     Allocating more space for the array without holding the spinlock
+     implies tolerating races against other thread(s) who are also
+     trying to expand the array.  How can we detect if we have been
+     out-raced?  Every successful expansion of g_stackLimits[] results
+     in an increase in g_stackLimitsSize.  Hence we can detect if we
+     got out-raced by remembering g_stackLimitsSize before we dropped
+     the spinlock and checking if it has changed after the spinlock is
+     reacquired. */
+
+  MOZ_ASSERT(g_stackLimitsUsed <= g_stackLimitsSize);
+
+  if (g_stackLimitsUsed == g_stackLimitsSize) {
+    /* g_stackLimits[] is full; resize it. */
+
+    size_t old_size = g_stackLimitsSize;
+    size_t new_size = old_size == 0 ? 4 : (2 * old_size);
+
+    spinLock_release(&g_spinLock);
+    StackLimit* new_arr  = (StackLimit*)malloc(new_size * sizeof(StackLimit));
+    if (!new_arr)
+      return;
+
+    spinLock_acquire(&g_spinLock);
+
+    if (old_size != g_stackLimitsSize) {
+      /* We've been outraced.  Instead of trying to deal in-line with
+         this extremely rare case, just start all over again by
+         tail-calling this routine. */
+      spinLock_release(&g_spinLock);
+      free(new_arr);
+      thread_register_for_profiling(stackTop);
+      return;
+    }
+
+    memcpy(new_arr, g_stackLimits, old_size * sizeof(StackLimit));
+    if (g_stackLimits)
+      free(g_stackLimits);
+
+    g_stackLimits = new_arr;
+
+    MOZ_ASSERT(g_stackLimitsSize < new_size);
+    g_stackLimitsSize = new_size;
+  }
+
+  MOZ_ASSERT(g_stackLimitsUsed < g_stackLimitsSize);
+