Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 09 Oct 2013 15:34:02 -0400
changeset 150272 aa986b6ce882b4d2fcd6e4d857cb619cffe480ea
parent 150271 541e79e84ce97fe041ab94a885d632c78f8f1d24 (current diff)
parent 150220 a141e39bf6da408eecb8edb66bc19c189dfe3a23 (diff)
child 150273 8ce7c4424b8afe9b81ab739d9b629962493c5cb0
child 150304 4275b265451268af7f64845a8a58e881bcbad3e4
child 150316 6e889bf97a591a8d4eed629563221b9a72486b9e
child 155836 e680128a639ee6e977ab8df4d2a5c996cf970cf8
push id25428
push userkwierso@gmail.com
push dateThu, 10 Oct 2013 03:41:12 +0000
treeherdermozilla-central@aa986b6ce882 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
aa986b6ce882 / 27.0a1 / 20131010030202 / files
nightly linux64
aa986b6ce882 / 27.0a1 / 20131010030202 / files
nightly mac
aa986b6ce882 / 27.0a1 / 20131010030202 / files
nightly win32
aa986b6ce882 / 27.0a1 / 20131010030202 / files
nightly win64
aa986b6ce882 / 27.0a1 / 20131010030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound. CLOSED TREE
browser/devtools/markupview/test/browser_inspector_markup_colorconversion.js
dom/system/gonk/NetworkManager.js
webapprt/test/chrome/Makefile.in
webapprt/test/chrome/moz.build
webapprt/test/content/Makefile.in
webapprt/test/content/moz.build
webapprt/test/moz.build
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "064b829074c2088d866b022c8d942c07744ff9b9", 
+    "revision": "916fa42b75d23393752647ef2b44da15e2e1dd52", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6429,16 +6429,24 @@ var gIdentityHandler = {
     this._identityIconLabel = document.getElementById("identity-icon-label");
     this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
     this._identityIcon = document.getElementById("page-proxy-favicon");
     this._permissionsContainer = document.getElementById("identity-popup-permissions");
     this._permissionList = document.getElementById("identity-popup-permission-list");
   },
 
   /**
+   * Handler for commands on the help button in the "identity-popup" panel.
+   */
+  handleHelpCommand : function(event) {
+    openHelpLink("secure-connection");
+    this._identityPopup.hidePopup();
+  },
+
+  /**
    * Handler for mouseclicks on the "More Information" button in the
    * "identity-popup" panel.
    */
   handleMoreInfoClick : function(event) {
     displaySecurityInfo();
     event.stopPropagation();
     this._identityPopup.hidePopup();
   },
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -337,17 +337,21 @@
             <separator class="thin"/>
             <label class="identity-popup-label header"
                    value="&identity.permissions;"/>
             <vbox id="identity-popup-permission-list" class="indent"/>
           </vbox>
         </vbox>
       </hbox>
       <!-- Footer button to open security page info -->
-      <hbox id="identity-popup-button-container" pack="end">
+      <hbox id="identity-popup-button-container" align="center">
+        <button id="identity-popup-help-icon"
+               oncommand="gIdentityHandler.handleHelpCommand(event);"
+               tooltiptext="&identity.help.tooltip;"/>
+        <spacer flex="1"/>
         <button id="identity-popup-more-info-button"
                 label="&identity.moreInfoLinkText;"
                 oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
       </hbox>
     </panel>
 
     <panel id="ctrlTab-panel" class="KUI-panel" hidden="true" norestorefocus="true" level="top">
       <hbox>
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -552,20 +552,17 @@ var BookmarkPropertiesPanel = {
                       value  : this._description,
                       expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
       let editItemTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj);
       childTransactions.push(editItemTxn);
     }
 
     if (this._loadInSidebar) {
       let annoObj = { name   : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
-                      type   : Ci.nsIAnnotationService.TYPE_INT32,
-                      flags  : 0,
-                      value  : this._loadInSidebar,
-                      expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
+                      value  : true };
       let setLoadTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj);
       childTransactions.push(setLoadTxn);
     }
 
     if (this._postData) {
       let postDataTxn = new PlacesEditBookmarkPostDataTransaction(-1, this._postData);
       childTransactions.push(postDataTxn);
     }
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -584,24 +584,20 @@ var gEditItemOverlay = {
     if (keyword != PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId)) {
       var txn = new PlacesEditBookmarkKeywordTransaction(this._itemId, keyword);
       PlacesUtils.transactionManager.doTransaction(txn);
     }
   },
 
   onLoadInSidebarCheckboxCommand:
   function EIO_onLoadInSidebarCheckboxCommand() {
-    var loadInSidebarChecked = this._element("loadInSidebarCheckbox").checked;
-    var annoObj = { name   : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
-                    type   : Ci.nsIAnnotationService.TYPE_INT32,
-                    flags  : 0,
-                    value  : loadInSidebarChecked,
-                    expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
-    var txn = new PlacesSetItemAnnotationTransaction(this._itemId,
-                                                     annoObj);
+    let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO };
+    if (this._element("loadInSidebarCheckbox").checked)
+      annoObj.value = true;
+    let txn = new PlacesSetItemAnnotationTransaction(this._itemId, annoObj);
     PlacesUtils.transactionManager.doTransaction(txn);
   },
 
   toggleFolderTreeVisibility: function EIO_toggleFolderTreeVisibility() {
     var expander = this._element("foldersExpander");
     var folderTreeRow = this._element("folderTreeRow");
     if (!folderTreeRow.collapsed) {
       expander.className = "expander-down";
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -35,40 +35,52 @@ const IDB = {
 
     request.onsuccess = function() {
       let db = IDB._db = request.result;
       let objectStore = db.transaction("projects").objectStore("projects");
       let projects = []
       objectStore.openCursor().onsuccess = function(event) {
         let cursor = event.target.result;
         if (cursor) {
-          projects.push(cursor.value);
+          if (cursor.value.location) {
+            // We need to make sure this object has a `.location` property.
+            // The UI depends on this property.
+            // This should not be needed as we make sure to register valid
+            // projects, but in the past (before bug 924568), we might have
+            // registered invalid objects.
+            projects.push(cursor.value);
+          }
           cursor.continue();
         } else {
           deferred.resolve(projects);
         }
       };
     };
 
     return deferred.promise;
   },
 
   add: function(project) {
     let deferred = promise.defer();
 
-    var transaction = IDB._db.transaction(["projects"], "readwrite");
-    var objectStore = transaction.objectStore("projects");
-    var request = objectStore.add(project);
-    request.onerror = function(event) {
-      deferred.reject("Unable to add project to the AppProjects indexedDB: " +
-                      this.error.name + " - " + this.error.message );
-    };
-    request.onsuccess = function() {
-      deferred.resolve();
-    };
+    if (!project.location) {
+      // We need to make sure this object has a `.location` property.
+      deferred.reject("Missing location property on project object.");
+    } else {
+      let transaction = IDB._db.transaction(["projects"], "readwrite");
+      let objectStore = transaction.objectStore("projects");
+      let request = objectStore.add(project);
+      request.onerror = function(event) {
+        deferred.reject("Unable to add project to the AppProjects indexedDB: " +
+                        this.error.name + " - " + this.error.message );
+      };
+      request.onsuccess = function() {
+        deferred.resolve();
+      };
+    }
 
     return deferred.promise;
   },
 
   update: function(project) {
     let deferred = promise.defer();
 
     var transaction = IDB._db.transaction(["projects"], "readwrite");
--- a/browser/devtools/app-manager/content/projects.js
+++ b/browser/devtools/app-manager/content/projects.js
@@ -77,16 +77,20 @@ let UI = {
     AppProjects.addPackaged(folder)
                .then(function (project) {
                  UI.validate(project);
                  UI.selectProject(project.location);
                });
   },
 
   addHosted: function() {
+    let form = document.querySelector("#new-hosted-project-wrapper")
+    if (!form.checkValidity())
+      return;
+
     let urlInput = document.querySelector("#url-input");
     let manifestURL = urlInput.value;
     AppProjects.addHosted(manifestURL)
                .then(function (project) {
                  UI.validate(project);
                  UI.selectProject(project.location);
                });
   },
@@ -203,35 +207,45 @@ let UI = {
     if (project.type == "packaged") {
       return "app://" + project.packagedAppOrigin + "/manifest.webapp";
     } else if (project.type == "hosted") {
       return project.location;
     }
   },
 
   install: function(project) {
+    this.connection.log("Installing the " + project.manifest.name + " app...");
+    let installPromise;
     if (project.type == "packaged") {
-      return installPackaged(this.connection.client, this.listTabsResponse.webappsActor, project.location, project.packagedAppOrigin)
+      installPromise = installPackaged(this.connection.client, this.listTabsResponse.webappsActor, project.location, project.packagedAppOrigin)
         .then(({ appId }) => {
           // If the packaged app specified a custom origin override,
           // we need to update the local project origin
           project.packagedAppOrigin = appId;
           // And ensure the indexed db on disk is also updated
           AppProjects.update(project);
         });
     } else {
       let manifestURLObject = Services.io.newURI(project.location, null, null);
       let origin = Services.io.newURI(manifestURLObject.prePath, null, null);
       let appId = origin.host;
       let metadata = {
         origin: origin.spec,
         manifestURL: project.location
       };
-      return installHosted(this.connection.client, this.listTabsResponse.webappsActor, appId, metadata, project.manifest);
+      installPromise = installHosted(this.connection.client, this.listTabsResponse.webappsActor, appId, metadata, project.manifest);
     }
+
+    installPromise.then(() => {
+      this.connection.log("Install completed.");
+    }, () => {
+      this.connection.log("Install failed.");
+    });
+
+    return installPromise;
   },
 
   start: function(project) {
     let deferred = promise.defer();
     let request = {
       to: this.listTabsResponse.webappsActor,
       type: "launch",
       manifestURL: this._getProjectManifestURL(project)
--- a/browser/devtools/app-manager/content/projects.xhtml
+++ b/browser/devtools/app-manager/content/projects.xhtml
@@ -20,17 +20,17 @@
     <aside id="sidebar">
       <div id="project-list" template='{"type":"attribute","path":"projects.length","name":"projects-count"}'>
         <div template-loop='{"arrayPath":"projects","childSelector":"#project-item-template"}'></div>
         <div id="no-project">&projects.noProjects;</div>
       </div>
       <div id="new-packaged-project" onclick="UI.addPackaged()" title="&projects.addPackagedTooltip;">&projects.addPackaged;</div>
       <div id="new-hosted-project">&projects.addHosted;
         <form onsubmit="UI.addHosted(); return false;" id="new-hosted-project-wrapper">
-          <input value="" id="url-input" type="url" pattern="https?://.+" placeholder="&projects.hostedManifestPlaceHolder2;" size="50" />
+          <input value="" id="url-input" type="url" required="true" pattern="https?://.+" placeholder="&projects.hostedManifestPlaceHolder2;" size="50" />
           <div onclick="UI.addHosted()" id="new-hosted-project-click" title="&projects.addHostedTooltip;"></div>
           <input type="submit" hidden="true"></input>
         </form>
       </div>
     </aside>
     <section id="lense"></section>
   </body>
 
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -13,17 +13,16 @@ const DEFAULT_MAX_CHILDREN = 100;
 const COLLAPSE_ATTRIBUTE_LENGTH = 120;
 const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
 const COLLAPSE_DATA_URL_LENGTH = 60;
 const CONTAINER_FLASHING_DURATION = 500;
 
 const {UndoStack} = require("devtools/shared/undo");
 const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-const {colorUtils} = require("devtools/css-color");
 const promise = require("sdk/core/promise");
 
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 loader.lazyGetter(this, "DOMParser", function() {
  return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
@@ -405,17 +404,17 @@ MarkupView.prototype = {
 
       let container = this._containers.get(target);
       if (!container) {
         // Container might not exist if this came from a load event for a node
         // we're not viewing.
         continue;
       }
       if (type === "attributes" || type === "characterData") {
-        container.update(false);
+        container.update();
       } else if (type === "childList") {
         container.childrenDirty = true;
         // Update the children to take care of changes in the DOM
         // Passing true as the last parameter asks for mutation flashing of the
         // new nodes
         this._updateChildren(container, {flash: true});
       }
     }
@@ -1146,19 +1145,19 @@ MarkupContainer.prototype = {
       this.highlighter.classList.remove("theme-selected");
     }
   },
 
   /**
    * Update the container's editor to the current state of the
    * viewed node.
    */
-  update: function(parseColors=true) {
+  update: function() {
     if (this.editor.update) {
-      this.editor.update(parseColors);
+      this.editor.update();
     }
   },
 
   /**
    * Try to put keyboard focus on the current editor.
    */
   focus: function() {
     let focusable = this.editor.elt.querySelector("[tabindex]");
@@ -1362,17 +1361,17 @@ function ElementEditor(aContainer, aNode
 
   this.update();
 }
 
 ElementEditor.prototype = {
   /**
    * Update the state of the editor from the node.
    */
-  update: function(parseColors=true) {
+  update: function() {
     let attrs = this.node.attributes;
     if (!attrs) {
       return;
     }
 
     // Hide all the attribute editors, they'll be re-shown if they're
     // still applicable.  Don't update attributes that are being
     // actively edited.
@@ -1381,19 +1380,16 @@ ElementEditor.prototype = {
       if (!attrEditors[i].inplaceEditor) {
         attrEditors[i].style.display = "none";
       }
     }
 
     // Get the attribute editor for each attribute that exists on
     // the node and show it.
     for (let attr of attrs) {
-      if (parseColors && typeof attr.value !== "undefined") {
-        attr.value = colorUtils.processCSSString(attr.value);
-      }
       let attribute = this._createAttribute(attr);
       if (!attribute.inplaceEditor) {
         attribute.style.removeProperty("display");
       }
     }
   },
 
   _startModifyingAttributes: function() {
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -1,15 +1,14 @@
 [DEFAULT]
 support-files = head.js
 
 [browser_bug896181_css_mixed_completion_new_attribute.js]
 # Bug 916763 - too many intermittent failures
 skip-if = true
-[browser_inspector_markup_colorconversion.js]
 [browser_inspector_markup_edit.html]
 [browser_inspector_markup_edit.js]
 [browser_inspector_markup_mutation.html]
 [browser_inspector_markup_mutation.js]
 [browser_inspector_markup_mutation_flashing.html]
 [browser_inspector_markup_mutation_flashing.js]
 [browser_inspector_markup_navigation.html]
 [browser_inspector_markup_navigation.js]
deleted file mode 100644
--- a/browser/devtools/markupview/test/browser_inspector_markup_colorconversion.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
-
-function test() {
-  let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
-  let inspector;
-  let doc;
-
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(createDocument, content);
-  }, true);
-
-  content.location = "data:text/html,browser_inspector_markupview_colorconversion.js";
-
-  function createDocument() {
-    doc.body.innerHTML = '' +
-    '<span style="color:red; border-radius:10px; ' +
-    'background-color:rgba(0, 255, 0, 1); display: inline-block;">' +
-    'Some styled text</span>';
-
-    doc.title = "Style Inspector key binding test";
-
-    setupTest();
-  }
-
-  function setupTest() {
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      inspector.once("inspector-updated", checkColors);
-    });
-  }
-
-  function checkColors() {
-    let node = content.document.querySelector("span");
-    assertAttributes(node, {
-      style: "color:#F00; border-radius:10px; background-color:#0F0; display: inline-block;"
-    }).then(() => {
-      finishUp();
-    });
-  }
-
-  // This version of assertAttributes is different from that in other markup
-  // view tests. This is because in most tests we are checking the node
-  // attributes but here we need the actual values displayed in the markup panel.
-  function assertAttributes(node, attributes) {
-    let deferred = promise.defer();
-    let attrsToCheck = Object.getOwnPropertyNames(attributes);
-    ok(node, "captain, we have the node");
-
-    let checkAttrs = function() {
-      let container = inspector.markup._selectedContainer;
-      let nodeAttrs = container.editor.attrs;
-
-      is(node.attributes.length, attrsToCheck.length,
-        "Node has the correct number of attributes");
-
-      for (let attr of attrsToCheck) {
-        ok(nodeAttrs[attr], "Node has a " + attr + "attribute");
-
-        let nodeAttributeText = nodeAttrs[attr].textContent;
-        [, nodeAttributeText] = nodeAttributeText.match(/^\s*[\w-]+\s*=\s*"(.*)"$/);
-        is(nodeAttributeText, attributes[attr],
-          "Node has the correct " + attr + " attribute value.");
-      }
-      deferred.resolve();
-    };
-
-    if (inspector.selection.node == node) {
-      checkAttrs();
-    } else {
-      inspector.once("inspector-updated", () => {
-        checkAttrs();
-      });
-      inspector.selection.setNode(node);
-    }
-    return deferred.promise;
-  }
-
-  function finishUp() {
-    doc = inspector = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  }
-}
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1743,18 +1743,18 @@ function TextPropertyEditor(aRuleEditor,
   this.ruleEditor = aRuleEditor;
   this.doc = this.ruleEditor.doc;
   this.popup = this.ruleEditor.ruleView.popup;
   this.prop = aProperty;
   this.prop.editor = this;
   this.browserWindow = this.doc.defaultView.top;
   this.removeOnRevert = this.prop.value === "";
 
-  let sheet = this.prop.rule.sheet;
-  let href = sheet ? (sheet.href || sheet.nodeHref) : null;
+  let domRule = this.prop.rule.domRule;
+  let href = domRule ? domRule.href : null;
   if (href) {
     this.sheetURI = IOService.newURI(href, null, null);
   }
 
   this._onEnableClicked = this._onEnableClicked.bind(this);
   this._onExpandClicked = this._onExpandClicked.bind(this);
   this._onStartEditing = this._onStartEditing.bind(this);
   this._onNameDone = this._onNameDone.bind(this);
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_677930_urls_clickable.html
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_677930_urls_clickable.html
@@ -9,13 +9,15 @@
   <body>
 
     <div class="relative">Background image with relative path (loaded from external css)</div>
 
     <div class="absolute">Background image with absolute path (loaded from external css)</div>
 
     <div class="base64">Background image with base64 url (loaded from external css)</div>
 
-    <div class="inline" style="background: url(test-image.png);">Background image with relative path (loaded from style attribute)</div>';
+    <div class="inline" style="background: url(test-image.png);">Background image with relative path (loaded from style attribute)</div>
+
+    <div class="inline-resolved" style="background-image: url(./test-image.png)">Background image with resolved relative path (loaded from style attribute)</div>
 
     <div class="noimage">No background image :(</div>
   </body>
 </html>
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_677930_urls_clickable.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_677930_urls_clickable.js
@@ -21,22 +21,24 @@ function selectNode(aInspector, aRuleVie
   let sidebar = inspector.sidebar;
   let contentDoc = aRuleView.doc;
 
   let relative = doc.querySelector(".relative");
   let absolute = doc.querySelector(".absolute");
   let inline = doc.querySelector(".inline");
   let base64 = doc.querySelector(".base64");
   let noimage = doc.querySelector(".noimage");
+  let inlineresolved = doc.querySelector(".inline-resolved");
 
   ok(relative, "captain, we have the relative div");
   ok(absolute, "captain, we have the absolute div");
   ok(inline, "captain, we have the inline div");
   ok(base64, "captain, we have the base64 div");
   ok(noimage, "captain, we have the noimage div");
+  ok(inlineresolved, "captain, we have the inlineresolved div");
 
   inspector.selection.setNode(relative);
   inspector.once("inspector-updated", () => {
     is(inspector.selection.node, relative, "selection matches the relative element");
     let relativeLink = contentDoc.querySelector(".ruleview-propertycontainer a");
     ok (relativeLink, "Link exists for relative node");
     ok (relativeLink.getAttribute("href"), TEST_IMAGE);
 
@@ -56,22 +58,30 @@ function selectNode(aInspector, aRuleVie
 
         inspector.selection.setNode(base64);
         inspector.once("inspector-updated", () => {
           is(inspector.selection.node, base64, "selection matches the base64 element");
           let base64Link = contentDoc.querySelector(".ruleview-propertycontainer a");
           ok (base64Link, "Link exists for base64 node");
           ok (base64Link.getAttribute("href"), BASE_64_URL);
 
-          inspector.selection.setNode(noimage);
+          inspector.selection.setNode(inlineresolved);
           inspector.once("inspector-updated", () => {
-            is(inspector.selection.node, noimage, "selection matches the inline element");
-            let noimageLink = contentDoc.querySelector(".ruleview-propertycontainer a");
-            ok (!noimageLink, "There is no link for the node with no background image");
-            finishUp();
+            is(inspector.selection.node, inlineresolved, "selection matches the style tag element");
+            let inlineResolvedLink = contentDoc.querySelector(".ruleview-propertycontainer a");
+            ok (inlineResolvedLink, "Link exists for style tag node");
+            ok (inlineResolvedLink.getAttribute("href"), TEST_IMAGE);
+
+            inspector.selection.setNode(noimage);
+            inspector.once("inspector-updated", () => {
+              is(inspector.selection.node, noimage, "selection matches the inline element");
+              let noimageLink = contentDoc.querySelector(".ruleview-propertycontainer a");
+              ok (!noimageLink, "There is no link for the node with no background image");
+              finishUp();
+            });
           });
         });
       });
     });
   });
 }
 
 function finishUp()
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -601,16 +601,21 @@ substitution variables.  If it is diffic
 with that structure, consider a translation which ignores the preceding domain and
 just addresses the organization to follow, e.g. "This site is run by " -->
 <!ENTITY identity.runBy "which is run by">
 
 <!ENTITY identity.moreInfoLinkText "More Information…">
 
 <!ENTITY identity.permissions "Permissions">
 
+<!-- LOCALIZATION NOTE (identity.help.tooltip) : This string should be the
+     localized title of this SUMO article
+     https://support.mozilla.org/kb/how-do-i-tell-if-my-connection-is-secure -->
+<!ENTITY identity.help.tooltip "How do I tell if my connection to a website is secure?">
+
 <!-- Name for the tabs toolbar as spoken by screen readers.
      The word "toolbar" is appended automatically and should not be contained below! -->
 <!ENTITY tabsToolbar.label "Browser tabs">
 
 <!-- LOCALIZATION NOTE (syncTabsMenu2.label): This appears in the history menu -->
 <!ENTITY syncTabsMenu2.label     "Tabs From Other Devices">
 
 <!ENTITY syncBrand.shortName.label    "Sync">
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -626,26 +626,16 @@
         <description>&sync.flyout.pairSuccess.description1;</description>
         <description>&sync.flyout.pairSuccess.description2;</description>
       </vbox>
 
     </flyoutpanel>
 #endif
 
     <flyoutpanel id="prefs-flyoutpanel" class="flyout-narrow" headertext="&optionsHeader.title;">
-      <settings id="prefs-startup" label="&optionsHeader.startup.title;"> <!-- note, this element has a custom margin-top -->
-        <setting id="prefs-homepage" title="&optionsHeader.homepage.title;" type="menulist" pref="browser.startup.sessionRestore" class="setting-expanded">
-          <menulist id="prefs-homepage-options">
-            <menupopup id="prefs-homepage-popup" position="after_end">
-              <menuitem id="prefs-homepage-default" label="&optionsHeader.homepage.startPage.button;" value="false"/>
-              <menuitem id="prefs-homepage-session" label="&optionsHeader.homepage.sessionRestore.button;" value="true"/>
-            </menupopup>
-          </menulist>
-        </setting>
-      </settings>
       <settings id="prefs-charencoding" label="&optionsHeader.char.title;">
         <setting pref="browser.menu.showCharacterEncoding" title="&optionsHeader.char.options.label;" type="bool"/>
       </settings>
       <settings id="prefs-privdata" label="&clearPrivateData.title;">
         <description>&clearPrivateData.label;</description>
 
         <checkbox id="prefs-privdata-history" itemName="history" label="&clearPrivateData.history;" checked="true" />
 
--- a/browser/metro/locales/en-US/chrome/preferences.dtd
+++ b/browser/metro/locales/en-US/chrome/preferences.dtd
@@ -2,20 +2,16 @@
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!-- ## Sync Flyout Panel ## -->
 <!-- see sync.dtd -->
 
 <!-- ## Options Flyout Panel ## -->
 <!ENTITY optionsHeader.title                                     "Options">
-<!ENTITY optionsHeader.startup.title                             "Startup">
-<!ENTITY optionsHeader.homepage.title                            "When &brandShortName; starts, show">
-<!ENTITY optionsHeader.homepage.startPage.button                 "start page">
-<!ENTITY optionsHeader.homepage.sessionRestore.button            "tabs from last time">
 <!ENTITY optionsHeader.char.title                                "Character Encoding">
 <!ENTITY optionsHeader.char.autodetect.label                     "Auto-detect">
 <!ENTITY optionsHeader.char.options.label                        "Show encoding options on the App Bar">
 
 <!ENTITY clearPrivateData.title                                  "Clear Private Data">
 <!ENTITY clearPrivateData.label                                  "Clear your browsing history, passwords, cookies, and form data on this device">
 <!ENTITY clearPrivateData.button                                 "Clear">
 <!ENTITY clearPrivateData.done                                   "Done!">
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1091,16 +1091,40 @@ toolbar[iconsize="small"] #webrtc-status
   -moz-margin-start: -18px;
 }
 
 #identity-popup-content-box.verifiedIdentity > #identity-popup-encryption > vbox > #identity-popup-encryption-icon ,
 #identity-popup-content-box.verifiedDomain > #identity-popup-encryption > vbox > #identity-popup-encryption-icon {
   list-style-image: url("chrome://browser/skin/Secure.png");
 }
 
+#identity-popup-help-icon {
+  -moz-appearance: none;
+  margin-left: 0px;
+  border: none;
+  background: none;
+  min-width: 0;
+  list-style-image: url("chrome://global/skin/icons/question-16.png");
+  cursor: pointer;
+}
+
+#identity-popup-help-icon > .button-box > .button-text {
+  display: none;
+}
+
+#identity-popup-help-icon > .button-box > .button-icon {
+  height: 16px;
+  width: 16px;
+}
+
+#identity-popup-help-icon:-moz-focusring {
+  outline: 1px dotted;
+  outline-offset: 1px;
+}
+
 #identity-popup > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
 #identity-popup-container {
   min-width: 280px;
   padding: 10px;
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3031,22 +3031,53 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 #identity-popup-content-box.verifiedIdentity > #identity-popup-encryption > vbox > #identity-popup-encryption-icon ,
 #identity-popup-content-box.verifiedDomain > #identity-popup-encryption > vbox > #identity-popup-encryption-icon {
   margin-top: 5px;
   list-style-image: url("chrome://browser/skin/Secure-Glyph.png");
 }
 
+#identity-popup-help-icon {
+  -moz-appearance: none;
+  border: none;
+  margin: 10px 0 0 2px;
+  background: none;
+  min-width: 0;
+  list-style-image: url("chrome://global/skin/icons/question-16.png");
+  cursor: pointer;
+}
+
+#identity-popup-help-icon > .button-box > .button-text {
+  display: none;
+}
+
+#identity-popup-help-icon > .button-box > .button-icon {
+  height: 16px;
+  width: 16px;
+}
+
+#identity-popup-help-icon:focus {
+  @hudButtonFocused@
+}
+
+#identity-popup-help-icon:hover:active {
+  @hudButtonPressed@
+}
+
 @media (min-resolution: 2dppx) {
   #identity-popup-content-box.verifiedIdentity > #identity-popup-encryption > vbox > #identity-popup-encryption-icon ,
   #identity-popup-content-box.verifiedDomain > #identity-popup-encryption > vbox > #identity-popup-encryption-icon {
     list-style-image: url("chrome://browser/skin/Secure-Glyph@2x.png");
     width: 24px;
   }
+
+  #identity-popup-help-icon {
+    list-style-image: url("chrome://global/skin/icons/question-32.png");
+  }
 }
 
 #identity-popup > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
 #identity-popup-container {
   padding: 16px;
--- a/browser/themes/shared/devtools/app-manager/device.css
+++ b/browser/themes/shared/devtools/app-manager/device.css
@@ -80,49 +80,67 @@ button {
   border-radius: 3px;
   border-width: 1px;
 }
 
 .app-buttons > button {
   display: none;
 }
 
+.app-buttons > button[disabled] {
+  background-color: transparent;
+  opacity: 0.4;
+  pointer-events: none;
+}
+
 .app[running="false"] > .app-buttons > .button-start,
 .app[running="true"] > .app-buttons > .button-stop,
 .app[running="true"] > .app-buttons > .button-debug {
   display: inline-block;
 }
 
 .button-debug {
   color: #3498DB;
 }
 
 .button-debug:hover {
   background-color: #3498DB;
   color: #FFF;
 }
 
+.button-debug[disabled] {
+  color: #3498DB;
+}
+
 .button-start {
   color: #18BC9C
 }
 
 .button-start:hover {
   background-color: #18BC9C;
   color: #FFF;
 }
 
+.button-start[disabled] {
+  color: #18BC9C
+}
+
 .button-stop {
   color: #E74C3C;
 }
 
 .button-stop:hover {
   background-color: #E74C3C;
   color: #FFF;
 }
 
+.button-stop[disabled] {
+  color: #E74C3C;
+}
+
 
 
 /*****************     PERMISSIONS     *****************/
 
 
 
 
 .permission-table {
--- a/browser/themes/shared/devtools/app-manager/projects.css
+++ b/browser/themes/shared/devtools/app-manager/projects.css
@@ -340,38 +340,52 @@ strong {
   border: 1px solid #CCC;
   border-left-width: 0;
   padding: 5px 15px;
   cursor: pointer;
   background: rgba(255,255,255,0.4);
   text-transform: uppercase;
 }
 
+.project-buttons > button[disabled] {
+  background-color: transparent;
+  opacity: 0.4;
+  pointer-events: none;
+}
+
 .project-buttons > button:first-child {
   border-left-width: 1px;
 }
 
 .project-button-debug {
   color: #3498DB;
 }
 
 .project-button-debug:hover {
   background-color: #3498DB;
   color: #FFF;
 }
 
+.project-button-debug[disabled] {
+  color: #3498DB;
+}
+
 .project-button-update {
   color: #777;
 }
 
 .project-button-update:hover {
   background-color: #777;
   color: #FFF;
 }
 
+.project-button-update[disabled] {
+  color: #777;
+}
+
 
 
 /********* ERRORS AND WARNINGS ***********/
 
 .project-warnings,
 .project-errors,
 .project-item-warnings,
 .project-item-errors {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2356,16 +2356,40 @@ toolbarbutton.bookmark-item[dragover="tr
   -moz-margin-start: -24px;
 }
 
 #identity-popup-content-box.verifiedIdentity > #identity-popup-encryption > vbox > #identity-popup-encryption-icon ,
 #identity-popup-content-box.verifiedDomain > #identity-popup-encryption > vbox > #identity-popup-encryption-icon {
   list-style-image: url("chrome://browser/skin/Secure24.png");
 }
 
+#identity-popup-help-icon {
+  -moz-appearance: none;
+  border: none;
+  margin: 7px 0 0 -3px;
+  background: none;
+  min-width: 0;
+  list-style-image: url("chrome://global/skin/icons/question-16.png");
+  cursor: pointer;
+}
+
+#identity-popup-help-icon > .button-box > .button-text {
+  display: none;
+}
+
+#identity-popup-help-icon > .button-box > .button-icon {
+  height: 16px;
+  width: 16px;
+}
+
+#identity-popup-help-icon:-moz-focusring {
+  outline: 1px dotted;
+  outline-offset: 1px;
+}
+
 #identity-popup-more-info-button {
   margin-top: 6px;
   margin-bottom: 0;
   -moz-margin-end: 0;
 }
 
 #identity-popup > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
--- a/build/docs/test_manifests.rst
+++ b/build/docs/test_manifests.rst
@@ -26,16 +26,19 @@ browser.ini
    For the *browser chrome* flavor of mochitests.
 
 a11y.ini
    For the *a11y* flavor of mochitests.
 
 xpcshell.ini
    For *xpcshell* tests.
 
+webapprt.ini
+   For the *chrome* flavor of webapp runtime mochitests.
+
 .. _manifest_destiny_manifests:
 
 Manifest Destiny Manifests
 ==========================
 
 Manifest destiny manifests are essentially ini files that conform to a basic
 set of assumptions.
 
--- a/config/makefiles/mochitest.mk
+++ b/config/makefiles/mochitest.mk
@@ -42,16 +42,11 @@ MOCHITEST_METRO_DEST := $(call mochitest
 INSTALL_TARGETS += MOCHITEST_METRO
 endif
 
 ifdef MOCHITEST_ROBOCOP_FILES
 MOCHITEST_ROBOCOP_DEST := $(call mochitestdir,tests/robocop,flat_hierarchy)
 INSTALL_TARGETS += MOCHITEST_ROBOCOP
 endif
 
-ifdef MOCHITEST_WEBAPPRT_CHROME_FILES
-MOCHITEST_WEBAPPRT_CHROME_DEST := $(call mochitestdir,webapprtChrome)
-INSTALL_TARGETS += MOCHITEST_WEBAPPRT_CHROME
-endif
-
 INCLUDED_TESTS_MOCHITEST_MK := 1
 
 endif #} INCLUDED_TESTS_MOCHITEST_MK
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -47,16 +47,17 @@ INCLUDED_RULES_MK = 1
   XPCSHELL_TESTS \
   XPIDL_MODULE \
   $(NULL)
 
 _DEPRECATED_VARIABLES := \
   XPIDL_FLAGS \
   MOCHITEST_FILES_PARTS \
   MOCHITEST_BROWSER_FILES_PARTS \
+  MOCHITEST_WEBAPPRT_CHROME_FILES \
   $(NULL)
 
 ifndef EXTERNALLY_MANAGED_MAKE_FILE
 # Using $(firstword) may not be perfect. But it should be good enough for most
 # scenarios.
 _current_makefile = $(CURDIR)/$(firstword $(MAKEFILE_LIST))
 
 $(foreach var,$(_MOZBUILD_EXTERNAL_VARIABLES),$(if $(filter file override,$(subst $(NULL) ,_,$(origin $(var)))),\
@@ -1723,17 +1724,16 @@ FREEZE_VARIABLES = \
   EXTRA_COMPONENTS \
   EXTRA_PP_COMPONENTS \
   MOCHITEST_FILES \
   MOCHITEST_CHROME_FILES \
   MOCHITEST_BROWSER_FILES \
   MOCHITEST_A11Y_FILES \
   MOCHITEST_METRO_FILES \
   MOCHITEST_ROBOCOP_FILES \
-  MOCHITEST_WEBAPPRT_CHROME_FILES \
   $(NULL)
 
 $(foreach var,$(FREEZE_VARIABLES),$(eval $(var)_FROZEN := '$($(var))'))
 
 CHECK_FROZEN_VARIABLES = $(foreach var,$(FREEZE_VARIABLES), \
   $(if $(subst $($(var)_FROZEN),,'$($(var))'),$(error Makefile variable '$(var)' changed value after including rules.mk. Was $($(var)_FROZEN), now $($(var)).)))
 
 libs export::
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -129,19 +129,16 @@ DOMInterfaces = {
 'AudioProcessingEvent' : {
     'resultNotAddRefed': [ 'inputBuffer', 'outputBuffer' ],
 },
 
 'BeforeUnloadEvent': {
     'nativeType': 'nsDOMBeforeUnloadEvent',
 },
 
-'AudioStreamTrack': {
-},
-
 'BarProp': {
     'headerFile': 'mozilla/dom/BarProps.h',
 },
 
 'BiquadFilterNode': {
     'resultNotAddRefed': [ 'frequency', 'detune', 'q', 'gain' ],
 },
 
@@ -168,20 +165,16 @@ DOMInterfaces = {
     'headerFile': 'BluetoothDevice.h'
 },
 
 'BluetoothManager': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
     'headerFile': 'BluetoothManager.h'
 },
 
-'CallEvent': {},
-
-'CallsList': {},
-
 'CameraControl': {
     'nativeType': 'mozilla::nsDOMCameraControl',
     'headerFile': 'DOMCameraControl.h',
     'binaryNames': {
         "release": "ReleaseHardware"
     }
 },
 
@@ -1186,22 +1179,16 @@ DOMInterfaces = {
 'SVGUnitTypes' : {
     'concrete': False,
 },
 
 'SVGZoomAndPan' : {
     'concrete': False,
 },
 
-'Telephony' : {},
-
-'TelephonyCall' : {},
-
-'TelephonyCallGroup' : {},
-
 'Text': {
     # Total hack to allow binding code to realize that nsTextNode can
     # in fact be cast to Text.
     'headerFile': 'nsTextNode.h',
 },
 
 'TextDecoder': {
     'nativeOwnership': 'owned',
@@ -1829,17 +1816,16 @@ addExternalIface('MozControllers', nativ
 addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
 addExternalIface('MozIccManager', headerFile='nsIDOMIccManager.h')
 addExternalIface('MozMobileConnection', headerFile='nsIDOMMobileConnection.h')
 addExternalIface('MozMobileMessageManager', headerFile='nsIDOMMobileMessageManager.h')
 addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
 addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
                  notflattened=True)
 addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
-addExternalIface('MozTelephony', nativeType='nsIDOMTelephony')
 addExternalIface('MozTreeBoxObject', nativeType='nsITreeBoxObject',
                  notflattened=True)
 addExternalIface('MozTreeColumn', nativeType='nsITreeColumn',
                  headerFile='nsITreeColumns.h')
 addExternalIface('MozVoicemailStatus')
 addExternalIface('MozWakeLock', headerFile='nsIDOMWakeLock.h')
 addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
 addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -1621,17 +1621,19 @@ BluetoothDBusService::StartInternal()
     BT_WARNING("Cannot start DBus thread!");
     return NS_ERROR_FAILURE;
   }
 
   if (mConnection) {
     return NS_OK;
   }
 
-  if (NS_FAILED(EstablishDBusConnection())) {
+  mConnection = new RawDBusConnection();
+
+  if (NS_FAILED(mConnection->EstablishDBusConnection())) {
     BT_WARNING("Cannot start Main Thread DBus connection!");
     StopDBus();
     return NS_ERROR_FAILURE;
   }
 
   gThreadConnection = new RawDBusConnection();
 
   if (NS_FAILED(gThreadConnection->EstablishDBusConnection())) {
@@ -1643,27 +1645,27 @@ BluetoothDBusService::StartInternal()
   DBusError err;
   dbus_error_init(&err);
 
   // Set which messages will be processed by this dbus connection.
   // Since we are maintaining a single thread for all the DBus bluez
   // signals we want, register all of them in this thread at startup.
   // The event handler will sort the destinations out as needed.
   for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
-    dbus_bus_add_match(mConnection,
+    dbus_bus_add_match(mConnection->GetConnection(),
                        sBluetoothDBusSignals[i],
                        &err);
     if (dbus_error_is_set(&err)) {
       LOG_AND_FREE_DBUS_ERROR(&err);
     }
   }
 
   // Add a filter for all incoming messages_base
-  if (!dbus_connection_add_filter(mConnection, EventFilter,
-                                  NULL, NULL)) {
+  if (!dbus_connection_add_filter(mConnection->GetConnection(),
+                                  EventFilter, NULL, NULL)) {
     BT_WARNING("Cannot create DBus Event Filter for DBus Thread!");
     return NS_ERROR_FAILURE;
   }
 
   if (!sPairingReqTable) {
     sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >;
   }
 
@@ -1707,25 +1709,26 @@ BluetoothDBusService::StopInternal()
   if (!mConnection) {
     StopDBus();
     return NS_OK;
   }
 
   DBusError err;
   dbus_error_init(&err);
   for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) {
-    dbus_bus_remove_match(mConnection,
+    dbus_bus_remove_match(mConnection->GetConnection(),
                           sBluetoothDBusSignals[i],
                           &err);
     if (dbus_error_is_set(&err)) {
       LOG_AND_FREE_DBUS_ERROR(&err);
     }
   }
 
-  dbus_connection_remove_filter(mConnection, EventFilter, nullptr);
+  dbus_connection_remove_filter(mConnection->GetConnection(),
+                                EventFilter, nullptr);
 
   if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(),
                                               KEY_LOCAL_AGENT)) {
     BT_WARNING("%s: Can't unregister object path %s for agent!",
         __FUNCTION__, KEY_LOCAL_AGENT);
   }
 
   if (!dbus_connection_unregister_object_path(gThreadConnection->GetConnection(),
@@ -1880,20 +1883,21 @@ BluetoothDBusService::GetDefaultAdapterP
     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
     return NS_OK;
   }
 
   nsRefPtr<DefaultAdapterPathReplyHandler> handler =
     new DefaultAdapterPathReplyHandler(aRunnable);
 
-  bool success = SendWithReply(DefaultAdapterPathReplyHandler::Callback,
-                               handler.get(), 1000,
-                               "/", DBUS_MANAGER_IFACE, "DefaultAdapter",
-                               DBUS_TYPE_INVALID);
+  bool success = mConnection->SendWithReply(
+    DefaultAdapterPathReplyHandler::Callback,
+    handler.get(), 1000,
+    "/", DBUS_MANAGER_IFACE, "DefaultAdapter",
+    DBUS_TYPE_INVALID);
 
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   handler.forget();
 
   return NS_OK;
 }
 
@@ -1923,21 +1927,22 @@ BluetoothDBusService::SendDiscoveryMessa
   if (!IsReady()) {
     NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
     DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
     return NS_OK;
   }
 
   nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
 
-  bool success = SendWithReply(OnSendDiscoveryMessageReply,
-                               static_cast<void*>(aRunnable), -1,
-                               NS_ConvertUTF16toUTF8(sAdapterPath).get(),
-                               DBUS_ADAPTER_IFACE, aMessageName,
-                               DBUS_TYPE_INVALID);
+  bool success = mConnection->SendWithReply(
+    OnSendDiscoveryMessageReply,
+    static_cast<void*>(aRunnable), -1,
+    NS_ConvertUTF16toUTF8(sAdapterPath).get(),
+    DBUS_ADAPTER_IFACE, aMessageName,
+    DBUS_TYPE_INVALID);
 
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   runnable.forget();
 
   return NS_OK;
 }
 
@@ -1977,21 +1982,21 @@ BluetoothDBusService::SendAsyncDBusMessa
     *serviceClass = BluetoothServiceClass::A2DP;
   } else if (!strcmp(aInterface, DBUS_INPUT_IFACE)) {
     *serviceClass = BluetoothServiceClass::HID;
   } else {
     MOZ_ASSERT(false);
     return NS_ERROR_FAILURE;
   }
 
-  bool ret = SendWithReply(aCallback,
-                           static_cast<void*>(serviceClass.forget()), -1,
-                           NS_ConvertUTF16toUTF8(aObjectPath).get(),
-                           aInterface, NS_ConvertUTF16toUTF8(aMessage).get(),
-                           DBUS_TYPE_INVALID);
+  bool ret = mConnection->SendWithReply(
+    aCallback, static_cast<void*>(serviceClass.forget()), -1,
+    NS_ConvertUTF16toUTF8(aObjectPath).get(),
+    aInterface, NS_ConvertUTF16toUTF8(aMessage).get(),
+    DBUS_TYPE_INVALID);
 
   NS_ENSURE_TRUE(ret, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress,
@@ -2289,17 +2294,20 @@ BluetoothDBusService::SetProperty(Blueto
     BT_WARNING("Could not append argument to method call!");
     dbus_message_unref(msg);
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
   // msg is unref'd as part of SendWithReply
-  if (!SendWithReply(GetVoidCallback, (void*)aRunnable, 1000, msg)) {
+  bool success = mConnection->SendWithReply(GetVoidCallback,
+                                            (void*)aRunnable,
+                                            1000, msg);
+  if (!success) {
     BT_WARNING("SendWithReply failed");
     return NS_ERROR_FAILURE;
   }
   runnable.forget();
   return NS_OK;
 }
 
 bool
@@ -2336,24 +2344,25 @@ BluetoothDBusService::CreatePairedDevice
    * Please see Bug 818696 for more information.
    */
   sIsPairing++;
 
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
   // Then send CreatePairedDevice, it will register a temp device agent then
   // unregister it after pairing process is over
-  bool ret = SendWithReply(GetObjectPathCallback, (void*)runnable, aTimeout,
-                           NS_ConvertUTF16toUTF8(sAdapterPath).get(),
-                           DBUS_ADAPTER_IFACE,
-                           "CreatePairedDevice",
-                           DBUS_TYPE_STRING, &deviceAddress,
-                           DBUS_TYPE_OBJECT_PATH, &deviceAgentPath,
-                           DBUS_TYPE_STRING, &capabilities,
-                           DBUS_TYPE_INVALID);
+  bool ret = mConnection->SendWithReply(
+    GetObjectPathCallback, (void*)runnable, aTimeout,
+    NS_ConvertUTF16toUTF8(sAdapterPath).get(),
+    DBUS_ADAPTER_IFACE,
+    "CreatePairedDevice",
+    DBUS_TYPE_STRING, &deviceAddress,
+    DBUS_TYPE_OBJECT_PATH, &deviceAgentPath,
+    DBUS_TYPE_STRING, &capabilities,
+    DBUS_TYPE_INVALID);
   if (!ret) {
     BT_WARNING("Could not start async function!");
     return NS_ERROR_FAILURE;
   }
 
   runnable.forget();
   return NS_OK;
 }
@@ -2387,22 +2396,22 @@ BluetoothDBusService::RemoveDeviceIntern
 
   nsCString deviceObjectPath =
     NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath,
                                                    aDeviceAddress));
   const char* cstrDeviceObjectPath = deviceObjectPath.get();
 
   nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
 
-  bool success = SendWithReply(OnRemoveDeviceReply,
-                               static_cast<void*>(runnable.get()), -1,
-                               NS_ConvertUTF16toUTF8(sAdapterPath).get(),
-                               DBUS_ADAPTER_IFACE, "RemoveDevice",
-                               DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath,
-                               DBUS_TYPE_INVALID);
+  bool success = mConnection->SendWithReply(
+    OnRemoveDeviceReply, static_cast<void*>(runnable.get()), -1,
+    NS_ConvertUTF16toUTF8(sAdapterPath).get(),
+    DBUS_ADAPTER_IFACE, "RemoveDevice",
+    DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath,
+    DBUS_TYPE_INVALID);
 
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   runnable.forget();
 
   return NS_OK;
 }
 
@@ -2438,17 +2447,17 @@ BluetoothDBusService::SetPinCodeInternal
 
   if (!dbus_message_append_args(reply,
                                 DBUS_TYPE_STRING, &pinCode,
                                 DBUS_TYPE_INVALID)) {
     BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
     errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
     result = false;
   } else {
-    result = Send(reply);
+    result = mConnection->Send(reply);
   }
 
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sPairingReqTable->Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
   return result;
@@ -2484,17 +2493,17 @@ BluetoothDBusService::SetPasskeyInternal
 
   if (!dbus_message_append_args(reply,
                                 DBUS_TYPE_UINT32, &passkey,
                                 DBUS_TYPE_INVALID)) {
     BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
     errorStr.AssignLiteral("Couldn't append arguments to dbus message.");
     result = false;
   } else {
-    result = Send(reply);
+    result = mConnection->Send(reply);
   }
 
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sPairingReqTable->Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
   return result;
@@ -2528,17 +2537,17 @@ BluetoothDBusService::SetPairingConfirma
   if (!reply) {
     BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__);
     dbus_message_unref(msg);
     errorStr.AssignLiteral("Memory can't be allocated for the message.");
     DispatchBluetoothReply(aRunnable, v, errorStr);
     return false;
   }
 
-  bool result = Send(reply);
+  bool result = mConnection->Send(reply);
   if (!result) {
     errorStr.AssignLiteral("Can't send message!");
   }
   dbus_message_unref(msg);
   dbus_message_unref(reply);
 
   sPairingReqTable->Remove(aDeviceAddress);
   DispatchBluetoothReply(aRunnable, v, errorStr);
@@ -2741,23 +2750,23 @@ BluetoothDBusService::GetServiceChannel(
   // GetServiceAttributeValue only exists in android's bluez dbus binding
   // implementation
   nsCString serviceUUID = NS_ConvertUTF16toUTF8(aServiceUUID);
   const char* cstrServiceUUID = serviceUUID.get();
 
   nsRefPtr<OnGetServiceChannelReplyHandler> handler =
     new OnGetServiceChannelReplyHandler(objectPath, aServiceUUID, aManager);
 
-  bool success = SendWithReply(OnGetServiceChannelReplyHandler::Callback,
-                               handler, -1,
-                               NS_ConvertUTF16toUTF8(objectPath).get(),
-                               DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
-                               DBUS_TYPE_STRING, &cstrServiceUUID,
-                               DBUS_TYPE_UINT16, &sProtocolDescriptorList,
-                               DBUS_TYPE_INVALID);
+  bool success = mConnection->SendWithReply(
+    OnGetServiceChannelReplyHandler::Callback, handler, -1,
+    NS_ConvertUTF16toUTF8(objectPath).get(),
+    DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
+    DBUS_TYPE_STRING, &cstrServiceUUID,
+    DBUS_TYPE_UINT16, &sProtocolDescriptorList,
+    DBUS_TYPE_INVALID);
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   handler.forget();
 #else
   // FIXME/Bug 793977 qdot: Just set something for desktop, until we have a
   // parser for the GetServiceAttributes xml block
   //
   // Even though we are on the main thread already, we need to dispatch a
@@ -2794,23 +2803,23 @@ BluetoothDBusService::UpdateSdpRecords(c
 
   nsString objectPath(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress));
 
   // I choose to use raw pointer here because this is going to be passed as an
   // argument into SendWithReply() at once.
   OnUpdateSdpRecordsRunnable* callbackRunnable =
     new OnUpdateSdpRecordsRunnable(objectPath, aManager);
 
-  return SendWithReply(DiscoverServicesCallback,
-                       (void*)callbackRunnable, -1,
-                       NS_ConvertUTF16toUTF8(objectPath).get(),
-                       DBUS_DEVICE_IFACE,
-                       "DiscoverServices",
-                       DBUS_TYPE_STRING, &EmptyCString(),
-                       DBUS_TYPE_INVALID);
+  return mConnection->SendWithReply(DiscoverServicesCallback,
+                                    (void*)callbackRunnable, -1,
+                                    NS_ConvertUTF16toUTF8(objectPath).get(),
+                                    DBUS_DEVICE_IFACE,
+                                    "DiscoverServices",
+                                    DBUS_TYPE_STRING, &EmptyCString(),
+                                    DBUS_TYPE_INVALID);
 }
 
 nsresult
 BluetoothDBusService::GetScoSocket(const nsAString& aAddress,
                                    bool aAuth,
                                    bool aEncrypt,
                                    mozilla::ipc::UnixSocketConsumer* aConsumer)
 {
@@ -3009,26 +3018,27 @@ BluetoothDBusService::SendMetaData(const
   if (aMediaNumber != a2dp->GetMediaNumber() ||
       !aTitle.Equals(prevTitle) ||
       !aAlbum.Equals(prevAlbum)) {
     UpdateNotification(ControlEventId::EVENT_TRACK_CHANGED, aMediaNumber);
   }
 
   nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
 
-  bool ret = SendWithReply(GetVoidCallback, (void*)runnable.get(), -1,
-                           NS_ConvertUTF16toUTF8(objectPath).get(),
-                           DBUS_CTL_IFACE, "UpdateMetaData",
-                           DBUS_TYPE_STRING, &title,
-                           DBUS_TYPE_STRING, &artist,
-                           DBUS_TYPE_STRING, &album,
-                           DBUS_TYPE_STRING, &mediaNumber,
-                           DBUS_TYPE_STRING, &totalMediaCount,
-                           DBUS_TYPE_STRING, &duration,
-                           DBUS_TYPE_INVALID);
+  bool ret = mConnection->SendWithReply(
+    GetVoidCallback, (void*)runnable.get(), -1,
+    NS_ConvertUTF16toUTF8(objectPath).get(),
+    DBUS_CTL_IFACE, "UpdateMetaData",
+    DBUS_TYPE_STRING, &title,
+    DBUS_TYPE_STRING, &artist,
+    DBUS_TYPE_STRING, &album,
+    DBUS_TYPE_STRING, &mediaNumber,
+    DBUS_TYPE_STRING, &totalMediaCount,
+    DBUS_TYPE_STRING, &duration,
+    DBUS_TYPE_INVALID);
   NS_ENSURE_TRUE_VOID(ret);
 
   runnable.forget();
 
   a2dp->UpdateMetaData(aTitle, aArtist, aAlbum,
                        aMediaNumber, aTotalMediaCount, aDuration);
 }
 
@@ -3106,23 +3116,24 @@ BluetoothDBusService::SendPlayStatus(int
 
   nsAutoString address;
   a2dp->GetAddress(address);
   nsString objectPath =
     GetObjectPathFromAddress(sAdapterPath, address);
 
   nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
 
-  bool ret = SendWithReply(GetVoidCallback, (void*)runnable.get(), -1,
-                           NS_ConvertUTF16toUTF8(objectPath).get(),
-                           DBUS_CTL_IFACE, "UpdatePlayStatus",
-                           DBUS_TYPE_UINT32, &aDuration,
-                           DBUS_TYPE_UINT32, &aPosition,
-                           DBUS_TYPE_UINT32, &tempPlayStatus,
-                           DBUS_TYPE_INVALID);
+  bool ret = mConnection->SendWithReply(
+    GetVoidCallback, (void*)runnable.get(), -1,
+    NS_ConvertUTF16toUTF8(objectPath).get(),
+    DBUS_CTL_IFACE, "UpdatePlayStatus",
+    DBUS_TYPE_UINT32, &aDuration,
+    DBUS_TYPE_UINT32, &aPosition,
+    DBUS_TYPE_UINT32, &tempPlayStatus,
+    DBUS_TYPE_INVALID);
   NS_ENSURE_TRUE_VOID(ret);
 
   runnable.forget();
 
   a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus);
 }
 
 static void
@@ -3153,23 +3164,24 @@ BluetoothDBusService::UpdatePlayStatus(u
 
   nsAutoString address;
   a2dp->GetAddress(address);
   nsString objectPath =
     GetObjectPathFromAddress(sAdapterPath, address);
 
   uint32_t tempPlayStatus = aPlayStatus;
 
-  bool ret = SendWithReply(ControlCallback, nullptr, -1,
-                           NS_ConvertUTF16toUTF8(objectPath).get(),
-                           DBUS_CTL_IFACE, "UpdatePlayStatus",
-                           DBUS_TYPE_UINT32, &aDuration,
-                           DBUS_TYPE_UINT32, &aPosition,
-                           DBUS_TYPE_UINT32, &tempPlayStatus,
-                           DBUS_TYPE_INVALID);
+  bool ret = mConnection->SendWithReply(
+    ControlCallback, nullptr, -1,
+    NS_ConvertUTF16toUTF8(objectPath).get(),
+    DBUS_CTL_IFACE, "UpdatePlayStatus",
+    DBUS_TYPE_UINT32, &aDuration,
+    DBUS_TYPE_UINT32, &aPosition,
+    DBUS_TYPE_UINT32, &tempPlayStatus,
+    DBUS_TYPE_INVALID);
   NS_ENSURE_TRUE_VOID(ret);
 }
 
 void
 BluetoothDBusService::UpdateNotification(ControlEventId aEventId,
                                          uint64_t aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3181,17 +3193,17 @@ BluetoothDBusService::UpdateNotification
   MOZ_ASSERT(a2dp->IsAvrcpConnected());
 
   nsAutoString address;
   a2dp->GetAddress(address);
   nsString objectPath =
     GetObjectPathFromAddress(sAdapterPath, address);
   uint16_t eventId = aEventId;
 
-  bool ret = SendWithReply(ControlCallback, nullptr, -1,
-                           NS_ConvertUTF16toUTF8(objectPath).get(),
-                           DBUS_CTL_IFACE,
-                           "UpdateNotification",
-                           DBUS_TYPE_UINT16, &eventId,
-                           DBUS_TYPE_UINT64, &aData,
-                           DBUS_TYPE_INVALID);
+  bool ret = mConnection->SendWithReply(
+    ControlCallback, nullptr, -1,
+    NS_ConvertUTF16toUTF8(objectPath).get(),
+    DBUS_CTL_IFACE, "UpdateNotification",
+    DBUS_TYPE_UINT16, &eventId,
+    DBUS_TYPE_UINT64, &aData,
+    DBUS_TYPE_INVALID);
   NS_ENSURE_TRUE_VOID(ret);
 }
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -17,17 +17,16 @@ class DBusMessage;
 BEGIN_BLUETOOTH_NAMESPACE
 
 /**
  * BluetoothDBusService is the implementation of BluetoothService for DBus on
  * linux/android/B2G. Function comments are in BluetoothService.h
  */
 
 class BluetoothDBusService : public BluetoothService
-                           , private mozilla::ipc::RawDBusConnection
 {
 public:
   bool IsReady();
 
   virtual nsresult StartInternal() MOZ_OVERRIDE;
 
   virtual nsresult StopInternal() MOZ_OVERRIDE;
 
@@ -196,13 +195,15 @@ private:
                                   BluetoothReplyRunnable* aRunnable);
 
   void UpdateNotification(ControlEventId aEventId, uint64_t aData);
 
   nsresult SendAsyncDBusMessage(const nsAString& aObjectPath,
                                 const char* aInterface,
                                 const nsAString& aMessage,
                                 mozilla::ipc::DBusReplyCallback aCallback);
+
+  nsRefPtr<mozilla::ipc::RawDBusConnection> mConnection;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -571,16 +571,20 @@ NetworkManager.prototype = {
     }
 
     if (this._manageOfflineStatus) {
       Services.io.offline = !this.active;
     }
   },
 
   resetRoutingTable: function resetRoutingTable(network) {
+    if (!network.ip || !network.netmask) {
+      debug("Either ip or netmask is null. Cannot reset routing table.");
+      return;
+    }
     let options = {
       cmd: "removeNetworkRoute",
       ifname: network.name,
       ip : network.ip,
       netmask: network.netmask,
     };
     this.worker.postMessage(options);
   },
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -600,19 +600,23 @@ public:
             return nullptr;
         }
     }
 
     bool SwapBuffers()
     {
         if (mSurface && !mPlatformContext) {
 #ifdef MOZ_WIDGET_GONK
-            if (!mIsOffscreen)
-                return GetGonkDisplay()->SwapBuffers(EGL_DISPLAY(), mSurface);
-            else
+            if (!mIsOffscreen) {
+                if (mHwc) {
+                    return mHwc->Render(EGL_DISPLAY(), mSurface);
+                } else {
+                    return GetGonkDisplay()->SwapBuffers(EGL_DISPLAY(), mSurface);
+                }
+            } else
 #endif
                 return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), mSurface);
         } else {
             return false;
         }
     }
     // GLContext interface - returns Tiled Texture Image in our case
     virtual already_AddRefed<TextureImage>
--- a/gfx/gl/SharedSurfaceGralloc.cpp
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -38,17 +38,17 @@ SurfaceFactory_Gralloc::SurfaceFactory_G
     : SurfaceFactory_GL(prodGL, SharedSurfaceType::Gralloc, caps)
 {
     if (caps.surfaceAllocator) {
         allocator = caps.surfaceAllocator;
     }
 
     MOZ_ASSERT(allocator);
 
-    mAllocator = allocator;
+    mAllocator = allocator->asWeakPtr();
 }
 
 SharedSurface_Gralloc*
 SharedSurface_Gralloc::Create(GLContext* prodGL,
                               const GLFormats& formats,
                               const gfxIntSize& size,
                               bool hasAlpha,
                               ISurfaceAllocator* allocator)
@@ -127,17 +127,20 @@ SharedSurface_Gralloc::~SharedSurface_Gr
 {
 
     DEBUG_PRINT("[SharedSurface_Gralloc %p] destroyed\n", this);
 
     mGL->MakeCurrent();
     mGL->fDeleteTextures(1, (GLuint*)&mProdTex);
 
     SurfaceDescriptor desc(mDesc);
-    mAllocator->DestroySharedSurface(&desc);
+
+    if (mAllocator) {
+        mAllocator->DestroySharedSurface(&desc);
+    }
 }
 
 void
 SharedSurface_Gralloc::Fence()
 {
     // We should be able to rely on genlock write locks/read locks.
     // But they're broken on some configs, and even a glFinish doesn't
     // work.  glReadPixels seems to, though.
--- a/gfx/gl/SharedSurfaceGralloc.h
+++ b/gfx/gl/SharedSurfaceGralloc.h
@@ -3,16 +3,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/. */
 
 #ifndef SHARED_SURFACE_GRALLOC_H_
 #define SHARED_SURFACE_GRALLOC_H_
 
 #include "SharedSurfaceGL.h"
 #include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 class ISurfaceAllocator;
 class SurfaceDescriptorGralloc;
 }
 
 namespace gl {
@@ -87,25 +88,28 @@ public:
         return mDesc;
     }
 };
 
 class SurfaceFactory_Gralloc
     : public SurfaceFactory_GL
 {
 protected:
-    layers::ISurfaceAllocator* mAllocator;
+    WeakPtr<layers::ISurfaceAllocator> mAllocator;
 
 public:
     SurfaceFactory_Gralloc(GLContext* prodGL,
                            const SurfaceCaps& caps,
                            layers::ISurfaceAllocator* allocator = nullptr);
 
     virtual SharedSurface* CreateShared(const gfxIntSize& size) {
         bool hasAlpha = mReadCaps.alpha;
+        if (!mAllocator) {
+            return nullptr;
+        }
         return SharedSurface_Gralloc::Create(mGL, mFormats, size, hasAlpha, mAllocator);
     }
 };
 
 } /* namespace gl */
 } /* namespace mozilla */
 
 #endif /* SHARED_SURFACE_GRALLOC_H_ */
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -6,16 +6,17 @@
 #ifndef GFX_LAYERS_ISURFACEDEALLOCATOR
 #define GFX_LAYERS_ISURFACEDEALLOCATOR
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t
 #include "gfxTypes.h"
 #include "gfxPoint.h"                   // for gfxIntSize
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
+#include "mozilla/WeakPtr.h"
 
 /*
  * FIXME [bjacob] *** PURE CRAZYNESS WARNING ***
  *
  * This #define is actually needed here, because subclasses of ISurfaceAllocator,
  * namely ShadowLayerForwarder, will or will not override AllocGrallocBuffer
  * depending on whether MOZ_HAVE_SURFACEDESCRIPTORGRALLOC is defined.
  */
@@ -63,17 +64,17 @@ bool ReleaseOwnedSurfaceDescriptor(const
  * An interface used to create and destroy surfaces that are shared with the
  * Compositor process (using shmem, or gralloc, or other platform specific memory)
  *
  * Most of the methods here correspond to methods that are implemented by IPDL
  * actors without a common polymorphic interface.
  * These methods should be only called in the ipdl implementor's thread, unless
  * specified otherwise in the implementing class.
  */
-class ISurfaceAllocator
+class ISurfaceAllocator : public SupportsWeakPtr<ISurfaceAllocator>
 {
 public:
 ISurfaceAllocator() {}
 
   /**
    * Allocate shared memory that can be accessed by only one process at a time.
    * Ownership of this memory is passed when the memory is sent in an IPDL
    * message.
--- a/js/src/config/makefiles/mochitest.mk
+++ b/js/src/config/makefiles/mochitest.mk
@@ -42,16 +42,11 @@ MOCHITEST_METRO_DEST := $(call mochitest
 INSTALL_TARGETS += MOCHITEST_METRO
 endif
 
 ifdef MOCHITEST_ROBOCOP_FILES
 MOCHITEST_ROBOCOP_DEST := $(call mochitestdir,tests/robocop,flat_hierarchy)
 INSTALL_TARGETS += MOCHITEST_ROBOCOP
 endif
 
-ifdef MOCHITEST_WEBAPPRT_CHROME_FILES
-MOCHITEST_WEBAPPRT_CHROME_DEST := $(call mochitestdir,webapprtChrome)
-INSTALL_TARGETS += MOCHITEST_WEBAPPRT_CHROME
-endif
-
 INCLUDED_TESTS_MOCHITEST_MK := 1
 
 endif #} INCLUDED_TESTS_MOCHITEST_MK
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -47,16 +47,17 @@ INCLUDED_RULES_MK = 1
   XPCSHELL_TESTS \
   XPIDL_MODULE \
   $(NULL)
 
 _DEPRECATED_VARIABLES := \
   XPIDL_FLAGS \
   MOCHITEST_FILES_PARTS \
   MOCHITEST_BROWSER_FILES_PARTS \
+  MOCHITEST_WEBAPPRT_CHROME_FILES \
   $(NULL)
 
 ifndef EXTERNALLY_MANAGED_MAKE_FILE
 # Using $(firstword) may not be perfect. But it should be good enough for most
 # scenarios.
 _current_makefile = $(CURDIR)/$(firstword $(MAKEFILE_LIST))
 
 $(foreach var,$(_MOZBUILD_EXTERNAL_VARIABLES),$(if $(filter file override,$(subst $(NULL) ,_,$(origin $(var)))),\
@@ -1723,17 +1724,16 @@ FREEZE_VARIABLES = \
   EXTRA_COMPONENTS \
   EXTRA_PP_COMPONENTS \
   MOCHITEST_FILES \
   MOCHITEST_CHROME_FILES \
   MOCHITEST_BROWSER_FILES \
   MOCHITEST_A11Y_FILES \
   MOCHITEST_METRO_FILES \
   MOCHITEST_ROBOCOP_FILES \
-  MOCHITEST_WEBAPPRT_CHROME_FILES \
   $(NULL)
 
 $(foreach var,$(FREEZE_VARIABLES),$(eval $(var)_FROZEN := '$($(var))'))
 
 CHECK_FROZEN_VARIABLES = $(foreach var,$(FREEZE_VARIABLES), \
   $(if $(subst $($(var)_FROZEN),,'$($(var))'),$(error Makefile variable '$(var)' changed value after including rules.mk. Was $($(var)_FROZEN), now $($(var)).)))
 
 libs export::
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1269,17 +1269,17 @@ abstract public class BrowserApp extends
      * @return true if we successfully switched to a tab, false otherwise.
      */
     private boolean maybeSwitchToTab(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
         if (!flags.contains(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB)) {
             return false;
         }
 
         final Tabs tabs = Tabs.getInstance();
-        final int tabId = tabs.getTabIdForUrl(url);
+        final int tabId = tabs.getTabIdForUrl(url, tabs.getSelectedTab().isPrivate());
         if (tabId < 0) {
             return false;
         }
 
         // If this tab is already selected, just hide the home pager.
         if (tabs.isSelectedTab(tabs.getTab(tabId))) {
             hideHomePager();
         } else {
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -582,23 +582,29 @@ public class Tabs implements GeckoEventL
     }
 
     private void registerEventListener(String event) {
         GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
     }
 
     /**
      * Looks for an open tab with the given URL.
+     * @param url       the URL of the tab we're looking for
+     * @param isPrivate if true, only look for tabs that are private. if false,
+     *                  only look for tabs that are non-private.
      *
      * @return id of an open tab with the given URL; -1 if the tab doesn't exist.
      */
-    public int getTabIdForUrl(String url) {
+    public int getTabIdForUrl(String url, boolean isPrivate) {
         for (Tab tab : mOrder) {
-            if (TextUtils.equals(tab.getURL(), url) ||
-                TextUtils.equals(ReaderModeUtils.getUrlFromAboutReader(tab.getURL()), url)) {
+            String tabUrl = tab.getURL();
+            if (ReaderModeUtils.isAboutReader(tabUrl)) {
+                tabUrl = ReaderModeUtils.getUrlFromAboutReader(tabUrl);
+            }
+            if (TextUtils.equals(tabUrl, url) && isPrivate == tab.isPrivate()) {
                 return tab.getId();
             }
         }
 
         return -1;
     }
 
     /**
--- a/mobile/android/base/home/TwoLinePageRow.java
+++ b/mobile/android/base/home/TwoLinePageRow.java
@@ -146,19 +146,22 @@ public class TwoLinePageRow extends Line
         if (mLoadFaviconTask != null) {
             mLoadFaviconTask.cancel(true);
             mLoadFaviconTask = null;
         }
     }
 
     /**
      * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL.
+     * Only looks for tabs that are either private or non-private, depending on the current 
+     * selected tab.
      */
     private void updateDisplayedUrl() {
-        int tabId = Tabs.getInstance().getTabIdForUrl(mPageUrl);
+        boolean isPrivate = Tabs.getInstance().getSelectedTab().isPrivate();
+        int tabId = Tabs.getInstance().getTabIdForUrl(mPageUrl, isPrivate);
         if (!mShowIcons || tabId < 0) {
             setUrl(mPageUrl);
             setUrlIcon(NO_ICON);
         } else {
             setUrl(R.string.switch_to_tab);
             setUrlIcon(R.drawable.ic_url_bar_tab);
         }
     }
--- a/netwerk/base/src/BackgroundFileSaver.cpp
+++ b/netwerk/base/src/BackgroundFileSaver.cpp
@@ -77,18 +77,20 @@ BackgroundFileSaver::BackgroundFileSaver
 , mPipeInputStream(nullptr)
 , mObserver(nullptr)
 , mLock("BackgroundFileSaver.mLock")
 , mWorkerThreadAttentionRequested(false)
 , mFinishRequested(false)
 , mComplete(false)
 , mStatus(NS_OK)
 , mAppend(false)
-, mAssignedTarget(nullptr)
-, mAssignedTargetKeepPartial(false)
+, mInitialTarget(nullptr)
+, mInitialTargetKeepPartial(false)
+, mRenamedTarget(nullptr)
+, mRenamedTargetKeepPartial(false)
 , mAsyncCopyContext(nullptr)
 , mSha256Enabled(false)
 , mActualTarget(nullptr)
 , mActualTargetKeepPartial(false)
 , mDigestContext(nullptr)
 {
 }
 
@@ -176,18 +178,23 @@ BackgroundFileSaver::EnableAppend()
 
 // Called on the control thread.
 NS_IMETHODIMP
 BackgroundFileSaver::SetTarget(nsIFile *aTarget, bool aKeepPartial)
 {
   NS_ENSURE_ARG(aTarget);
   {
     MutexAutoLock lock(mLock);
-    aTarget->Clone(getter_AddRefs(mAssignedTarget));
-    mAssignedTargetKeepPartial = aKeepPartial;
+    if (!mInitialTarget) {
+      aTarget->Clone(getter_AddRefs(mInitialTarget));
+      mInitialTargetKeepPartial = aKeepPartial;
+    } else {
+      aTarget->Clone(getter_AddRefs(mRenamedTarget));
+      mRenamedTargetKeepPartial = aKeepPartial;
+    }
   }
 
   // After the worker thread wakes up because attention is requested, it will
   // rename or create the target file as requested, and start copying data.
   return GetWorkerThreadAttention(true);
 }
 
 // Called on the control thread.
@@ -368,87 +375,100 @@ BackgroundFileSaver::ProcessStateChange(
   nsresult rv;
 
   // We might have been notified because the operation is complete, verify.
   if (CheckCompletion()) {
     return NS_OK;
   }
 
   // Get a copy of the current shared state for the worker thread.
-  nsCOMPtr<nsIFile> target;
-  bool targetKeepPartial;
-  bool sha256Enabled = false;
-  bool append = false;
+  nsCOMPtr<nsIFile> initialTarget;
+  bool initialTargetKeepPartial;
+  nsCOMPtr<nsIFile> renamedTarget;
+  bool renamedTargetKeepPartial;
+  bool sha256Enabled;
+  bool append;
   {
     MutexAutoLock lock(mLock);
 
-    target = mAssignedTarget;
-    targetKeepPartial = mAssignedTargetKeepPartial;
+    initialTarget = mInitialTarget;
+    initialTargetKeepPartial = mInitialTargetKeepPartial;
+    renamedTarget = mRenamedTarget;
+    renamedTargetKeepPartial = mRenamedTargetKeepPartial;
     sha256Enabled = mSha256Enabled;
     append = mAppend;
 
     // From now on, another attention event needs to be posted if state changes.
     mWorkerThreadAttentionRequested = false;
   }
 
-  // The target can only be null if it has never been assigned.  In this case,
-  // there is nothing to do since we never created any output file.
-  if (!target) {
+  // The initial target can only be null if it has never been assigned.  In this
+  // case, there is nothing to do since we never created any output file.
+  if (!initialTarget) {
     return NS_OK;
   }
 
+  // Determine if we are processing the attention request for the first time.
   bool isContinuation = !!mActualTarget;
-
-  // We will append to the initial target file only if it was requested by the
-  // caller, but we'll always append on subsequent accesses to the target file.
-  int32_t creationIoFlags;
-  if (isContinuation) {
-    creationIoFlags = PR_APPEND;
-  } else {
-    creationIoFlags = (append ? PR_APPEND : PR_TRUNCATE) | PR_CREATE_FILE;
+  if (!isContinuation) {
+    // Assign the target file for the first time.
+    mActualTarget = initialTarget;
+    mActualTargetKeepPartial = initialTargetKeepPartial;
   }
 
   // Verify whether we have actually been instructed to use a different file.
+  // This may happen the first time this function is executed, if SetTarget was
+  // called two times before the worker thread processed the attention request.
   bool equalToCurrent = false;
-  if (isContinuation) {
-    rv = mActualTarget->Equals(target, &equalToCurrent);
+  if (renamedTarget) {
+    rv = mActualTarget->Equals(renamedTarget, &equalToCurrent);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!equalToCurrent)
     {
-      // We are moving the previous target file to a different location.
-      nsCOMPtr<nsIFile> targetParentDir;
-      rv = target->GetParent(getter_AddRefs(targetParentDir));
-      NS_ENSURE_SUCCESS(rv, rv);
+      // If we were asked to rename the file but the initial file did not exist,
+      // we simply create the file in the renamed location.  We avoid this check
+      // if we have already started writing the output file ourselves.
+      bool exists = true;
+      if (!isContinuation) {
+        rv = mActualTarget->Exists(&exists);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      if (exists) {
+        // We are moving the previous target file to a different location.
+        nsCOMPtr<nsIFile> renamedTargetParentDir;
+        rv = renamedTarget->GetParent(getter_AddRefs(renamedTargetParentDir));
+        NS_ENSURE_SUCCESS(rv, rv);
 
-      nsAutoString targetName;
-      rv = target->GetLeafName(targetName);
-      NS_ENSURE_SUCCESS(rv, rv);
+        nsAutoString renamedTargetName;
+        rv = renamedTarget->GetLeafName(renamedTargetName);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-      // We must delete any existing target file before moving the current one.
-      bool exists = false;
-      rv = target->Exists(&exists);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (exists) {
-        rv = target->Remove(false);
+        // We must delete any existing target file before moving the current
+        // one.
+        rv = renamedTarget->Exists(&exists);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (exists) {
+          rv = renamedTarget->Remove(false);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        // Move the file.  If this fails, we still reference the original file
+        // in mActualTarget, so that it is deleted if requested.  If this
+        // succeeds, the nsIFile instance referenced by mActualTarget mutates
+        // and starts pointing to the new file, but we'll discard the reference.
+        rv = mActualTarget->MoveTo(renamedTargetParentDir, renamedTargetName);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
-      // Move the file.  If this fails, we still reference the original file
-      // in mActualTarget, so that it is deleted if requested.  If this
-      // succeeds, the nsIFile instance referenced by mActualTarget mutates and
-      // starts pointing to the new file, but we'll discard the reference.
-      rv = mActualTarget->MoveTo(targetParentDir, targetName);
-      NS_ENSURE_SUCCESS(rv, rv);
+      // Now we can update the actual target file name.
+      mActualTarget = renamedTarget;
+      mActualTargetKeepPartial = renamedTargetKeepPartial;
     }
   }
 
-  // Now we can update the actual target file name.
-  mActualTarget = target;
-  mActualTargetKeepPartial = targetKeepPartial;
-
   // Notify if the target file name actually changed.
   if (!equalToCurrent) {
     // We must clone the nsIFile instance because mActualTarget is not
     // immutable, it may change if the target is renamed later.
     nsCOMPtr<nsIFile> actualTargetToNotify;
     rv = mActualTarget->Clone(getter_AddRefs(actualTargetToNotify));
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -523,16 +543,25 @@ BackgroundFileSaver::ProcessStateChange(
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       rv = inputStream->Close();
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
+  // We will append to the initial target file only if it was requested by the
+  // caller, but we'll always append on subsequent accesses to the target file.
+  int32_t creationIoFlags;
+  if (isContinuation) {
+    creationIoFlags = PR_APPEND;
+  } else {
+    creationIoFlags = (append ? PR_APPEND : PR_TRUNCATE) | PR_CREATE_FILE;
+  }
+
   // Create the target file, or append to it if we already started writing it.
   nsCOMPtr<nsIOutputStream> outputStream;
   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
                                    mActualTarget,
                                    PR_WRONLY | creationIoFlags, 0600);
   NS_ENSURE_SUCCESS(rv, rv);
 
   outputStream = NS_BufferOutputStream(outputStream, BUFFERED_IO_SIZE);
@@ -597,26 +626,34 @@ BackgroundFileSaver::CheckCompletion()
       return true;
     }
 
     // If an error occurred, we don't need to do the checks in this code block,
     // and the operation can be completed immediately with a failure code.
     if (NS_SUCCEEDED(mStatus)) {
       failed = false;
 
-      // On success, if there is a pending rename operation, we must process it
-      // before finishing.  Otherwise, we can finish now if requested.
-      if ((mAssignedTarget && mAssignedTarget != mActualTarget) ||
-          !mFinishRequested) {
+      // We did not incur in an error, so we must determine if we can stop now.
+      // If the Finish method has not been called, we can just continue now.
+      if (!mFinishRequested) {
         return false;
       }
 
-      // If completion was requested, but we still have data to write to the
-      // output file, allow the copy operation to resume.  The Available getter
-      // may return an error if one of the pipe's streams has been already closed.
+      // We can only stop when all the operations requested by the control
+      // thread have been processed.  First, we check whether we have processed
+      // the first SetTarget call, if any.  Then, we check whether we have
+      // processed any rename requested by subsequent SetTarget calls.
+      if ((mInitialTarget && !mActualTarget) ||
+          (mRenamedTarget && mRenamedTarget != mActualTarget)) {
+        return false;
+      }
+
+      // If we still have data to write to the output file, allow the copy
+      // operation to resume.  The Available getter may return an error if one
+      // of the pipe's streams has been already closed.
       uint64_t available;
       rv = mPipeInputStream->Available(&available);
       if (NS_SUCCEEDED(rv) && available != 0) {
         return false;
       }
     }
 
     mComplete = true;
--- a/netwerk/base/src/BackgroundFileSaver.h
+++ b/netwerk/base/src/BackgroundFileSaver.h
@@ -156,27 +156,47 @@ private:
 
   /**
    * True if we should append data to the initial target file, instead of
    * overwriting it.
    */
   bool mAppend;
 
   /**
-   * Set by the control thread to the target file name that will be used by the
-   * worker thread, as soon as it is possible to update mActualTarget and open
-   * the file.  This is null if no target was ever assigned to this object.
+   * This is set by the first SetTarget call on the control thread, and contains
+   * the target file name that will be used by the worker thread, as soon as it
+   * is possible to update mActualTarget and open the file.  This is null if no
+   * target was ever assigned to this object.
    */
-  nsCOMPtr<nsIFile> mAssignedTarget;
+  nsCOMPtr<nsIFile> mInitialTarget;
+
+  /**
+   * This is set by the first SetTarget call on the control thread, and
+   * indicates whether mInitialTarget should be kept as partially completed,
+   * rather than deleted, if the operation fails or is canceled.
+   */
+  bool mInitialTargetKeepPartial;
 
   /**
-   * Indicates whether mAssignedTarget should be kept as partially completed,
+   * This is set by subsequent SetTarget calls on the control thread, and
+   * contains the new target file name to which the worker thread will move the
+   * target file, as soon as it can be done.  This is null if SetTarget was
+   * called only once, or no target was ever assigned to this object.
+   *
+   * The target file can be renamed multiple times, though only the most recent
+   * rename is guaranteed to be processed by the worker thread.
+   */
+  nsCOMPtr<nsIFile> mRenamedTarget;
+
+  /**
+   * This is set by subsequent SetTarget calls on the control thread, and
+   * indicates whether mRenamedTarget should be kept as partially completed,
    * rather than deleted, if the operation fails or is canceled.
    */
-  bool mAssignedTargetKeepPartial;
+  bool mRenamedTargetKeepPartial;
 
   /**
    * While NS_AsyncCopy is in progress, allows canceling it.  Null otherwise.
    * This is read by both threads but only written by the worker thread.
    */
   nsCOMPtr<nsISupports> mAsyncCopyContext;
 
   /**
--- a/netwerk/test/unit/test_backgroundfilesaver.js
+++ b/netwerk/test/unit/test_backgroundfilesaver.js
@@ -424,16 +424,39 @@ add_task(function test_setTarget_after_c
     do_check_eq(EXPECTED_HASHES[TEST_DATA_SHORT.length],
                 toHex(saver.sha256Hash));
   }
 
   // Clean up.
   destFile.remove(false);
 });
 
+add_task(function test_setTarget_fast()
+{
+  // This test checks a fast rename of the target file.
+  let destFile1 = getTempFile(TEST_FILE_NAME_1);
+  let destFile2 = getTempFile(TEST_FILE_NAME_2);
+  let saver = new BackgroundFileSaverOutputStream();
+  let completionPromise = promiseSaverComplete(saver);
+
+  // Set the initial name after the stream is closed, then rename immediately.
+  yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
+  saver.setTarget(destFile1, false);
+  saver.setTarget(destFile2, false);
+
+  // Wait for all the operations to complete.
+  saver.finish(Cr.NS_OK);
+  yield completionPromise;
+
+  // Verify results and clean up.
+  do_check_false(destFile1.exists());
+  yield promiseVerifyContents(destFile2, TEST_DATA_SHORT);
+  destFile2.remove(false);
+});
+
 add_task(function test_setTarget_multiple()
 {
   // This test checks multiple renames of the target file.
   let destFile = getTempFile(TEST_FILE_NAME_1);
   let saver = new BackgroundFileSaverOutputStream();
   let completionPromise = promiseSaverComplete(saver);
 
   // Rename both before and after the stream is closed.
@@ -477,16 +500,53 @@ add_task(function test_enableAppend()
                                    : TEST_DATA_LONG + TEST_DATA_LONG);
     yield promiseVerifyContents(destFile, expectedContents);
   }
 
   // Clean up.
   destFile.remove(false);
 });
 
+add_task(function test_enableAppend_setTarget_fast()
+{
+  // This test checks a fast rename of the target file in append mode.
+  let destFile1 = getTempFile(TEST_FILE_NAME_1);
+  let destFile2 = getTempFile(TEST_FILE_NAME_2);
+
+  // Test the case where the file does not already exists first, then the case
+  // where the file already exists.
+  for (let i = 0; i < 2; i++) {
+    let saver = new BackgroundFileSaverOutputStream();
+    saver.enableAppend();
+    let completionPromise = promiseSaverComplete(saver);
+
+    yield promiseCopyToSaver(TEST_DATA_SHORT, saver, true);
+
+    // The first time, we start appending to the first file and rename to the
+    // second file.  The second time, we start appending to the second file,
+    // that was created the first time, and rename back to the first file.
+    let firstFile = (i == 0) ? destFile1 : destFile2;
+    let secondFile = (i == 0) ? destFile2 : destFile1;
+    saver.setTarget(firstFile, false);
+    saver.setTarget(secondFile, false);
+
+    saver.finish(Cr.NS_OK);
+    yield completionPromise;
+
+    // Verify results.
+    do_check_false(firstFile.exists());
+    let expectedContents = (i == 0 ? TEST_DATA_SHORT
+                                   : TEST_DATA_SHORT + TEST_DATA_SHORT);
+    yield promiseVerifyContents(secondFile, expectedContents);
+  }
+
+  // Clean up.
+  destFile1.remove(false);
+});
+
 add_task(function test_enableAppend_hash()
 {
   // This test checks append mode, also verifying that the computed hash
   // includes the contents of the existing data.
   let destFile = getTempFile(TEST_FILE_NAME_1);
 
   // Test the case where the file does not already exists first, then the case
   // where the file already exists.
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -226,16 +226,17 @@ class TreeMetadataEmitter(LoggingMixin):
         # harness can yet deal with test filtering. Once all harnesses can do
         # this, this feature can be dropped.
         test_manifests = dict(
             A11Y=('a11y', 'testing/mochitest/a11y', True),
             BROWSER_CHROME=('browser-chrome', 'testing/mochitest/browser', True),
             METRO_CHROME=('metro-chrome', 'testing/mochitest/metro', True),
             MOCHITEST=('mochitest', 'testing/mochitest/tests', True),
             MOCHITEST_CHROME=('chrome', 'testing/mochitest/chrome', True),
+            MOCHITEST_WEBAPPRT_CHROME=('webapprt-chrome', 'testing/mochitest/webapprtChrome', True),
             WEBRTC_SIGNALLING_TEST=('steeplechase', 'steeplechase', True),
             XPCSHELL_TESTS=('xpcshell', 'xpcshell', False),
         )
 
         for prefix, info in test_manifests.items():
             for path in sandbox.get('%s_MANIFESTS' % prefix, []):
                 for obj in self._process_test_manifest(sandbox, info, path):
                     yield obj
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -476,22 +476,26 @@ VARIABLES = {
     'BROWSER_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining browser chrome tests.
         """, None),
 
     'METRO_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining metro browser chrome tests.
         """, None),
 
+    'MOCHITEST_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
+        """List of manifest files defining mochitest chrome tests.
+        """, None),
+
     'MOCHITEST_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining mochitest tests.
         """, None),
 
-    'MOCHITEST_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
-        """List of manifest files defining mochitest chrome tests.
+    'MOCHITEST_WEBAPPRT_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
+        """List of manifest files defining webapprt mochitest chrome tests.
         """, None),
 
     'WEBRTC_SIGNALLING_TEST_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining WebRTC signalling tests.
         """, None),
 
     'XPCSHELL_TESTS_MANIFESTS': (StrictOrderingOnAppendList, list, [],
         """List of manifest files defining xpcshell tests.
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -102,18 +102,16 @@ private:
 
 public:
   nsAppExitEvent(nsAppStartup *service) : mService(service) {}
 
   NS_IMETHOD Run() {
     // Tell the appshell to exit
     mService->mAppShell->Exit();
 
-    // We're done "shutting down".
-    mService->mShuttingDown = false;
     mService->mRunning = false;
     return NS_OK;
   }
 };
 
 /**
  * Computes an approximation of the absolute time represented by @a stamp
  * which is comparable to those obtained via PR_Now(). If the current absolute
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4109,19 +4109,19 @@
   },
   "FX_THUMBNAILS_BG_CAPTURE_SERVICE_TIME_MS": {
     "kind": "exponential",
     "high": 30000,
     "n_buckets": 20,
     "extended_statistics_ok": true,
     "description": "BACKGROUND THUMBNAILS: Time the capture took once it started and successfully completed (ms)"
   },
-  "FX_THUMBNAILS_BG_CAPTURE_DONE_REASON": {
+  "FX_THUMBNAILS_BG_CAPTURE_DONE_REASON_2": {
     "kind": "enumerated",
-    "n_values": 4,
+    "n_values": 10,
     "description": "BACKGROUND THUMBNAILS: Reason the capture completed (see TEL_CAPTURE_DONE_* constants in BackgroundPageThumbs.jsm)"
   },
   "FX_THUMBNAILS_BG_CAPTURE_PAGE_LOAD_TIME_MS": {
     "kind": "exponential",
     "high": 60000,
     "n_buckets": 20,
     "extended_statistics_ok": true,
     "description": "BACKGROUND THUMBNAILS: Time the capture's page load took (ms)"
--- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
+++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
@@ -14,20 +14,21 @@ const EXPORTED_SYMBOLS = [
 ];
 
 const DEFAULT_CAPTURE_TIMEOUT = 30000; // ms
 const DESTROY_BROWSER_TIMEOUT = 60000; // ms
 const FRAME_SCRIPT_URL = "chrome://global/content/backgroundPageThumbsContent.js";
 
 const TELEMETRY_HISTOGRAM_ID_PREFIX = "FX_THUMBNAILS_BG_";
 
-// possible FX_THUMBNAILS_BG_CAPTURE_DONE_REASON telemetry values
+// possible FX_THUMBNAILS_BG_CAPTURE_DONE_REASON_2 telemetry values
 const TEL_CAPTURE_DONE_OK = 0;
 const TEL_CAPTURE_DONE_TIMEOUT = 1;
 // 2 and 3 were used when we had special handling for private-browsing.
+const TEL_CAPTURE_DONE_CRASHED = 4;
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
@@ -196,17 +197,17 @@ const BackgroundPageThumbs = {
       this._destroyBrowser();
       let curCapture = this._captureQueue.length ? this._captureQueue[0] : null;
       // we could retry the pending capture, but it's possible the crash
       // was due directly to it, so trying again might just crash again.
       // We could keep a flag to indicate if it previously crashed, but
       // "resetting" the capture requires more work - so for now, we just
       // discard it.
       if (curCapture && curCapture.pending) {
-        curCapture._done(null);
+        curCapture._done(null, TEL_CAPTURE_DONE_CRASHED);
         // _done automatically continues queue processing.
       }
       // else: we must have been idle and not currently doing a capture (eg,
       // maybe a GC or similar crashed) - so there's no need to attempt a
       // queue restart - the next capture request will set everything up.
     });
 
     browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
@@ -326,54 +327,57 @@ Capture.prototype = {
       delete this._timeoutTimer;
     }
     if (this._msgMan) {
       this._msgMan.removeMessageListener("BackgroundPageThumbs:didCapture",
                                          this);
       delete this._msgMan;
     }
     delete this.captureCallback;
-    Services.ww.unregisterNotification(this);
+    delete this.doneCallbacks;
+    delete this.options;
   },
 
   // Called when the didCapture message is received.
   receiveMessage: function (msg) {
-    tel("CAPTURE_DONE_REASON", TEL_CAPTURE_DONE_OK);
     tel("CAPTURE_SERVICE_TIME_MS", new Date() - this.startDate);
 
     // A different timed-out capture may have finally successfully completed, so
     // discard messages that aren't meant for this capture.
     if (msg.json.id == this.id)
-      this._done(msg.json);
+      this._done(msg.json, TEL_CAPTURE_DONE_OK);
   },
 
   // Called when the timeout timer fires.
   notify: function () {
-    tel("CAPTURE_DONE_REASON", TEL_CAPTURE_DONE_TIMEOUT);
-    this._done(null);
+    this._done(null, TEL_CAPTURE_DONE_TIMEOUT);
   },
 
-  _done: function (data) {
+  _done: function (data, reason) {
     // Note that _done will be called only once, by either receiveMessage or
-    // notify, since it calls destroy, which cancels the timeout timer and
+    // notify, since it calls destroy here, which cancels the timeout timer and
     // removes the didCapture message listener.
+    let { captureCallback, doneCallbacks, options } = this;
+    this.destroy();
 
+    if (typeof(reason) != "number")
+      throw new Error("A done reason must be given.");
+    tel("CAPTURE_DONE_REASON_2", reason);
     if (data && data.telemetry) {
       // Telemetry is currently disabled in the content process (bug 680508).
       for (let id in data.telemetry) {
         tel(id, data.telemetry[id]);
       }
     }
 
     let done = () => {
-      this.captureCallback(this);
-      this.destroy();
-      for (let callback of this.doneCallbacks) {
+      captureCallback(this);
+      for (let callback of doneCallbacks) {
         try {
-          callback.call(this.options, this.url);
+          callback.call(options, this.url);
         }
         catch (err) {
           Cu.reportError(err);
         }
       }
     };
 
     if (!data) {
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -64,19 +64,21 @@
       <li><a href="about:license#angle">ANGLE License</a></li>
       <li><a href="about:license#apache">Apache License 2.0</a></li>
       <li><a href="about:license#apple">Apple License</a></li>
       <li><a href="about:license#apple-mozilla">Apple/Mozilla NPRuntime License</a></li>
       <li><a href="about:license#apple-torch">Apple/Torch Mobile License</a></li>
       <li><a href="about:license#bspatch">bspatch License</a></li>
       <li><a href="about:license#cairo">Cairo Component Licenses</a></li>
       <li><a href="about:license#chromium">Chromium License</a></li>
+      <li><a href="about:license#codemirror">CodeMirror License</a></li>
       <li><a href="about:license#dtoa">dtoa License</a></li>
       <li><a href="about:license#hunspell-nl">Dutch Spellchecking Dictionary License</a></li>
       <li><a href="about:license#edl">Eclipse Distribution License</a></li>
+      <li><a href="about:license#escodegen">Escodegen License</a></li>
       <li><a href="about:license#hunspell-ee">Estonian Spellchecking Dictionary License</a></li>
       <li><a href="about:license#expat">Expat License</a></li>
       <li><a href="about:license#firebug">Firebug License</a></li>
       <li><a href="about:license#gfx-font-list">gfxFontList License</a></li>
       <li><a href="about:license#google-bsd">Google BSD License</a></li>
       <li><a href="about:license#gears">Google Gears License</a></li>
       <li><a href="about:license#gears-istumbler">Google Gears/iStumbler License</a></li>
       <li><a href="about:license#vp8">Google VP8 License</a></li>
@@ -120,18 +122,16 @@
       <li><a href="about:license#unicode">Unicode License</a></li>
       <li><a href="about:license#ucal">University of California License</a></li>
       <li><a href="about:license#uszeged">University of Szeged License</a></li>
       <li><a href="about:license#hunspell-en-US">US English Spellchecking Dictionary Licenses</a></li>
       <li><a href="about:license#v8">V8 License</a></li>
       <li><a href="about:license#vtune">VTune License</a></li>
       <li><a href="about:license#webrtc">WebRTC License</a></li>
       <li><a href="about:license#xiph">Xiph.org Foundation License</a></li>
-      <li><a href="about:license#codemirror">CodeMirror License</a></li>
-      <li><a href="about:license#escodegen">Escodegen License</a></li>
     </ul>
 
 <br>
 
     <ul>
       <li><a href="about:license#other-notices">Other Required Notices</a>
       <li><a href="about:license#optional-notices">Optional Notices</a>
 #ifdef XP_WIN
@@ -1671,16 +1671,59 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTI
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
 
     <hr>
 
+    <h1><a id="codemirror"></a>CodeMirror License</h1>
+
+    <p>This license applies to all files in
+      <span class="path">browser/devtools/sourceeditor/codemirror</span> and
+      to specified files in the <span class="path">browser/devtools/sourceeditor/test/</span>:
+    </p>
+    <ul>
+      <li><span class="path">cm_comment_test.js</span></li>
+      <li><span class="path">cm_driver.js</span></li>
+      <li><span class="path">cm_mode_javascript_test.js</span></li>
+      <li><span class="path">cm_mode_test.css</span></li>
+      <li><span class="path">cm_mode_test.js</span></li>
+      <li><span class="path">cm_test.js</span></li>
+    </ul>
+<pre>
+Copyright (C) 2013 by Marijn Haverbeke <marijnh@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Please note that some subdirectories of the CodeMirror distribution
+include their own LICENSE files, and are released under different
+licences.
+</pre>
+
+
+    <hr>
+
     <h1><a id="dtoa"></a>dtoa License</h1>
 
     <p>This license applies to the file
     <span class="path">nsprpub/pr/src/misc/dtoa.c</span>.</p>
 
 <pre>
 The author of this software is David M. Gay.
 
@@ -1775,16 +1818,48 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIA
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
 
+    <hr>
+
+    <h1><a id="escodegen"></a>Escodegen License</h1>
+
+    <p>This license applies to all files in
+      <span class="path">toolkit/devtools/escodegen</span>.
+    </p>
+<pre>
+Copyright (C) 2012 Yusuke Suzuki (twitter: @Constellation) and other contributors.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
+</pre>
+
 
     <hr>
 
     <h1><a id="hunspell-ee"></a>Estonian Spellchecking Dictionary License</h1>
 
     <p>This license applies to precursor works to certain files in the directory
       <span class="path">l10n/ee/extensions/spellcheck/hunspell/</span>. The
       shipped versions are under the GNU Lesser General Public License. (This
@@ -4053,91 +4128,16 @@ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
-    <hr>
-
-    <h1><a id="codemirror"></a>CodeMirror License</h1>
-
-    <p>This license applies to all files in
-      <span class="path">browser/devtools/sourceeditor/codemirror</span> and
-      to specified files in the <span class="path">browser/devtools/sourceeditor/test/</span>:
-    </p>
-    <ul>
-      <li><span class="path">cm_comment_test.js</span></li>
-      <li><span class="path">cm_driver.js</span></li>
-      <li><span class="path">cm_mode_javascript_test.js</span></li>
-      <li><span class="path">cm_mode_test.css</span></li>
-      <li><span class="path">cm_mode_test.js</span></li>
-      <li><span class="path">cm_test.js</span></li>
-    </ul>
-<pre>
-Copyright (C) 2013 by Marijn Haverbeke <marijnh@gmail.com>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-Please note that some subdirectories of the CodeMirror distribution
-include their own LICENSE files, and are released under different
-licences.
-</pre>
-
-
-    <hr>
-
-    <h1><a id="escodegen"></a>Escodegen License</h1>
-
-    <p>This license applies to all files in
-      <span class="path">toolkit/devtools/escodegen</span>.
-    </p>
-<pre>
-Copyright (C) 2012 Yusuke Suzuki (twitter: @Constellation) and other contributors.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-  * Redistributions of source code must retain the above copyright notice, this
-    list of conditions and the following disclaimer.
-
-  * Redistributions in binary form must reproduce the above copyright notice,
-    this list of conditions and the following disclaimer in the documentation
-    and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
-OF SUCH DAMAGE.
-</pre>
-
 
     <hr>
 
     <h1><a id="other-notices"></a>Other Required Notices</h1>
 
     <ul>
       <li>This software is based in part on the work of the Independent
           JPEG Group.</li>
--- a/toolkit/devtools/server/actors/styles.js
+++ b/toolkit/devtools/server/actors/styles.js
@@ -559,25 +559,33 @@ var StyleSheetActor = protocol.ActorClas
 
   get conn() this.pageStyle.conn,
 
   form: function(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
+    let href;
+    if (this.rawSheet.ownerNode) {
+      if (this.rawSheet.ownerNode instanceof Ci.nsIDOMHTMLDocument)
+        href = this.rawSheet.ownerNode.location.href;
+      if (this.rawSheet.ownerNode.ownerDocument)
+        href = this.rawSheet.ownerNode.ownerDocument.location.href;
+    }
+
     return {
       actor: this.actorID,
 
       // href stores the uri of the sheet
       href: this.rawSheet.href,
 
       // nodeHref stores the URI of the document that
       // included the sheet.
-      nodeHref: this.rawSheet.ownerNode ? this.rawSheet.ownerNode.ownerDocument.location.href : undefined,
+      nodeHref: href,
 
       system: !CssLogic.isContentStylesheet(this.rawSheet),
       disabled: this.rawSheet.disabled ? true : undefined
     }
   }
 });
 
 /**
@@ -715,17 +723,21 @@ var StyleRuleActor = protocol.ActorClass
 
     // Use a fresh element for each call to this function to prevent side effects
     // that pop up based on property values that were already set on the element.
 
     let document;
     if (this.rawNode) {
       document = this.rawNode.ownerDocument;
     } else {
-      document = this.rawRule.parentStyleSheet.ownerNode.ownerDocument;
+      if (this.rawRule.parentStyleSheet.ownerNode instanceof Ci.nsIDOMHTMLDocument) {
+        document = this.rawRule.parentStyleSheet.ownerNode;
+      } else {
+        document = this.rawRule.parentStyleSheet.ownerNode.ownerDocument;
+      }
     }
 
     let tempElement = document.createElement("div");
 
     for (let mod of modifications) {
       if (mod.type === "set") {
         tempElement.style.setProperty(mod.name, mod.value, mod.priority || "");
         this.rawStyle.setProperty(mod.name,
--- a/toolkit/devtools/server/tests/mochitest/chrome.ini
+++ b/toolkit/devtools/server/tests/mochitest/chrome.ini
@@ -22,10 +22,11 @@ support-files =
 [test_inspector-remove.html]
 [test_inspector-retain.html]
 [test_inspector-traversal.html]
 [test_makeGlobalObjectReference.html]
 [test_styles-applied.html]
 [test_styles-computed.html]
 [test_styles-matched.html]
 [test_styles-modify.html]
+[test_styles-svg.html]
 [test_unsafeDereference.html]
-[test_evalInGlobal-outerized_this.html]
\ No newline at end of file
+[test_evalInGlobal-outerized_this.html]
--- a/toolkit/devtools/server/tests/mochitest/inspector-styles-data.html
+++ b/toolkit/devtools/server/tests/mochitest/inspector-styles-data.html
@@ -11,16 +11,19 @@
   .uninheritable-rule {
     background-color: #f06;
   }
   @media screen {
     #mediaqueried {
       background-color: #f06;
     }
   }
+  #svgcontent rect {
+    fill: rgb(1,2,3);
+  }
 </style>
 <link type="text/css" rel="stylesheet" href="inspector-styles-data.css"></link>
 <body>
   <h1>Style Actor Tests</h1>
   <!-- Inheritance checks -->
   <div id="inheritable-rule-uninheritable-style" class="inheritable-rule" style="background-color: purple">
     <div id="inheritable-rule-inheritable-style" class="inheritable-rule" style="color: blue">
       <div id="uninheritable-rule-uninheritable-style" class="uninheritable-rule" style="background-color: green">
@@ -46,10 +49,14 @@
       Here is the test node.
     </div>
   </div>
 
   <div id="mediaqueried">
     Screen mediaqueried.
   </div>
 
+  <div id="svgcontent">
+    <svg><rect></rect></svg>
+  </div>
+
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_styles-svg.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=921191
+Bug 921191 - allow inspection/editing of SVG elements' CSS properties
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug </title>
+
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
+  <script type="application/javascript;version=1.8">
+Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
+
+const promise = devtools.require("sdk/core/promise");
+const inspector = devtools.require("devtools/server/actors/inspector");
+
+window.onload = function() {
+  SimpleTest.waitForExplicitFinish();
+  runNextTest();
+}
+
+var gWalker = null;
+var gStyles = null;
+var gClient = null;
+
+addTest(function setup() {
+  let url = document.getElementById("inspectorContent").href;
+  attachURL(url, function(err, client, tab, doc) {
+    gInspectee = doc;
+    let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
+    let inspector = InspectorFront(client, tab);
+    promiseDone(inspector.getWalker().then(walker => {
+      ok(walker, "getWalker() should return an actor.");
+      gClient = client;
+      gWalker = walker;
+      return inspector.getPageStyle();
+    }).then(styles => {
+      gStyles = styles;
+    }).then(runNextTest));
+  });
+});
+
+addTest(function inheritedUserStyles() {
+  let node = node;
+  promiseDone(gWalker.querySelector(gWalker.rootNode, "#svgcontent rect").then(node => {
+    return gStyles.getApplied(node, { inherited: true, filter: "user" });
+  }).then(applied => {
+    is(applied.length, 3, "Should have 3 rules");
+    is(applied[1].rule.cssText, "fill: rgb(1, 2, 3);", "cssText is right");
+  }).then(runNextTest));
+});
+
+addTest(function cleanup() {
+  delete gStyles;
+  delete gWalker;
+  delete gClient;
+  runNextTest();
+});
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=921191">Mozilla Bug 921191</a>
+<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/webapprt/ContentPermission.js
+++ b/webapprt/ContentPermission.js
@@ -57,17 +57,17 @@ ContentPermission.prototype = {
     } else if (result == Ci.nsIPermissionManager.DENY_ACTION ||
                (result == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
                 UNKNOWN_FAIL.indexOf(request.type) >= 0)) {
       request.cancel();
       return;
     }
 
     // Display a prompt at the top level
-    let {name} = WebappRT.config.app.manifest;
+    let {name} = WebappRT.localeManifest;
     let requestingWindow = request.window.top;
     let chromeWin = this._getChromeWindow(requestingWindow);
     let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");
 
     // Construct a prompt with share/don't and remember checkbox
     let remember = {value: false};
     let choice = Services.prompt.confirmEx(
       chromeWin,
--- a/webapprt/Startup.jsm
+++ b/webapprt/Startup.jsm
@@ -15,37 +15,72 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/Services.jsm");
 // Initialize DOMApplicationRegistry by importing Webapps.jsm.
 Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
 Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
 
 // Initialize window-independent handling of webapps- notifications.
 Cu.import("resource://webapprt/modules/WebappsHandler.jsm");
 Cu.import("resource://webapprt/modules/WebappRT.jsm");
 
+const PROFILE_DIR = OS.Constants.Path.profileDir;
+
 function isFirstRunOrUpdate() {
   let savedBuildID = null;
   try {
     savedBuildID = Services.prefs.getCharPref("webapprt.buildID");
   } catch (e) {}
 
   let ourBuildID = Services.appinfo.platformBuildID;
 
   if (ourBuildID != savedBuildID) {
     Services.prefs.setCharPref("webapprt.buildID", ourBuildID);
     return true;
   }
 
   return false;
 }
 
+function writeFile(aPath, aData) {
+  return Task.spawn(function() {
+    let data = TextEncoder().encode(aData);
+    yield OS.File.writeAtomic(aPath, data, { tmpPath: aPath + ".tmp" });
+  });
+}
+
+function createBrandingFiles() {
+  return Task.spawn(function() {
+    let manifest = WebappRT.localeManifest;
+    let name = WebappRT.localeManifest.name;
+    let developer = " ";
+    if (WebappRT.localeManifest.developer) {
+      developer = WebappRT.localeManifest.developer.name;
+    }
+
+    let brandDTDContent = '<!ENTITY brandShortName "' + name + '">\n\
+  <!ENTITY brandFullName "' + name + '">\n\
+  <!ENTITY vendorShortName "' + developer + '">\n\
+  <!ENTITY trademarkInfo.part1 " ">';
+
+    yield writeFile(OS.Path.join(PROFILE_DIR, "brand.dtd"), brandDTDContent);
+
+    let brandPropertiesContent = 'brandShortName=' + name + '\n\
+  brandFullName=' + name + '\n\
+  vendorShortName=' + developer;
+
+    yield writeFile(OS.Path.join(PROFILE_DIR, "brand.properties"),
+                    brandPropertiesContent);
+  });
+}
+
 // Observes all the events needed to actually launch an application.
 // It waits for XUL window and webapps registry loading.
 this.startup = function(window) {
   return Task.spawn(function () {
     // Observe registry loading.
     let deferredRegistry = Promise.defer();
     function observeRegistryLoading() {
       Services.obs.removeObserver(observeRegistryLoading, "webapps-registry-start");
@@ -74,19 +109,31 @@ this.startup = function(window) {
     if (manifestURL) {
       appID = DOMApplicationRegistry.getAppLocalIdByManifestURL(manifestURL);
 
       // On firstrun, set permissions to their default values.
       // When the webapp runtime is updated, update the permissions.
       // TODO: Update the permissions when the application is updated.
       if (isFirstRunOrUpdate(Services.prefs)) {
         PermissionsInstaller.installPermissions(WebappRT.config.app, true);
+        yield createBrandingFiles();
       }
     }
 
+    // Branding substitution
+    let aliasFile = Components.classes["@mozilla.org/file/local;1"]
+                              .createInstance(Ci.nsIFile);
+    aliasFile.initWithPath(PROFILE_DIR);
+
+    let aliasURI = Services.io.newFileURI(aliasFile);
+
+    Services.io.getProtocolHandler("resource")
+               .QueryInterface(Ci.nsIResProtocolHandler)
+               .setSubstitution("webappbranding", aliasURI);
+
     // Wait for XUL window loading
     yield deferredWindowLoad.promise;
 
     // Get the <browser> element in the webapp.xul window.
     let appBrowser = window.document.getElementById("content");
 
     // Set the principal to the correct appID and launch the application.
     appBrowser.docShell.setIsApp(appID);
--- a/webapprt/WebappRT.jsm
+++ b/webapprt/WebappRT.jsm
@@ -41,13 +41,17 @@ this.WebappRT = {
   // will have a reference to its global object, so our reference to it
   // will leak that object (per bug 780674).  The setter enables us to clone
   // the new value so we don't actually retain a reference to it.
   set config(newVal) {
     this._config = JSON.parse(JSON.stringify(newVal));
   },
 
   get launchURI() {
-    let manifest = new ManifestHelper(this.config.app.manifest,
-                                      this.config.app.origin);
+    let manifest = this.localeManifest;
     return manifest.fullLaunchPath();
-  }
+  },
+
+  get localeManifest() {
+    return new ManifestHelper(this.config.app.manifest,
+                              this.config.app.origin);
+  },
 };
--- a/webapprt/locales/jar.mn
+++ b/webapprt/locales/jar.mn
@@ -2,8 +2,10 @@
 # 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/.
 
 @AB_CD@.jar:
 % locale webapprt @AB_CD@ %locale/webapprt/
     locale/webapprt/webapp.dtd                     (%webapprt/webapp.dtd)
     locale/webapprt/webapp.properties              (%webapprt/webapp.properties)
+
+% locale branding @AB_CD@ resource://webappbranding/
--- a/webapprt/moz.build
+++ b/webapprt/moz.build
@@ -7,24 +7,27 @@
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['win']
 elif CONFIG['OS_ARCH'] == 'Darwin':
     DIRS += ['mac']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk2':
     DIRS += ['gtk2']
 
 DIRS += ['locales']
-TEST_DIRS += ['test']
 
 EXTRA_COMPONENTS += [
     'CommandLineHandler.js',
     'ContentPermission.js',
     'DirectoryProvider.js',
     'PaymentUIGlue.js',
     'components.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'RemoteDebugger.jsm',
     'Startup.jsm',
     'WebappRT.jsm',
     'WebappsHandler.jsm',
 ]
+
+MOCHITEST_WEBAPPRT_CHROME_MANIFESTS += ['test/chrome/webapprt.ini']
+MOCHITEST_MANIFESTS += ['test/content/mochitest.ini']
+
deleted file mode 100644
--- a/webapprt/test/chrome/Makefile.in
+++ /dev/null
@@ -1,40 +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/.
-
-MOCHITEST_WEBAPPRT_CHROME_FILES = \
-  head.js \
-  browser_sample.js \
-    sample.webapp \
-    sample.webapp^headers^ \
-    sample.html \
-  browser_window-title.js \
-    window-title.webapp \
-    window-title.webapp^headers^ \
-    window-title.html \
-  browser_webperm.js \
-    webperm.webapp \
-    webperm.webapp^headers^ \
-    webperm.html \
-  browser_noperm.js \
-    noperm.webapp \
-    noperm.webapp^headers^ \
-    noperm.html \
-  browser_geolocation-prompt-perm.js \
-  browser_geolocation-prompt-noperm.js \
-    geolocation-prompt-perm.webapp \
-    geolocation-prompt-perm.webapp^headers^ \
-    geolocation-prompt-noperm.webapp \
-    geolocation-prompt-noperm.webapp^headers^ \
-    geolocation-prompt-perm.html \
-    geolocation-prompt-noperm.html \
-  browser_debugger.js \
-    debugger.webapp \
-    debugger.webapp^headers^ \
-    debugger.html \
-  browser_mozpay.js \
-    mozpay.webapp \
-    mozpay.webapp^headers^ \
-    mozpay.html \
-    mozpay-success.html \
-  $(NULL)
deleted file mode 100644
--- a/webapprt/test/chrome/moz.build
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
new file mode 100644
--- /dev/null
+++ b/webapprt/test/chrome/webapprt.ini
@@ -0,0 +1,38 @@
+[DEFAULT]
+support-files =
+  head.js
+  sample.webapp
+  sample.webapp^headers^
+  sample.html
+  window-title.webapp
+  window-title.webapp^headers^
+  window-title.html
+  webperm.webapp
+  webperm.webapp^headers^
+  webperm.html
+  noperm.webapp
+  noperm.webapp^headers^
+  noperm.html
+  geolocation-prompt-perm.webapp
+  geolocation-prompt-perm.webapp^headers^
+  geolocation-prompt-noperm.webapp
+  geolocation-prompt-noperm.webapp^headers^
+  geolocation-prompt-perm.html
+  geolocation-prompt-noperm.html
+  debugger.webapp
+  debugger.webapp^headers^
+  debugger.html
+  mozpay.webapp
+  mozpay.webapp^headers^
+  mozpay.html
+  mozpay-success.html
+
+
+[browser_sample.js]
+[browser_window-title.js]
+[browser_webperm.js]
+[browser_noperm.js]
+[browser_geolocation-prompt-perm.js]
+[browser_geolocation-prompt-noperm.js]
+[browser_debugger.js]
+[browser_mozpay.js]
deleted file mode 100644
--- a/webapprt/test/content/Makefile.in
+++ /dev/null
@@ -1,10 +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/.
-
-MOCHITEST_FILES = \
-  test.webapp \
-  test.webapp^headers^ \
-  webapprt_sample.html \
-  webapprt_indexeddb.html \
-  $(NULL)
new file mode 100644
--- /dev/null
+++ b/webapprt/test/content/mochitest.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+  test.webapp
+  test.webapp^headers^
+
+[webapprt_sample.html]
+[webapprt_indexeddb.html]
deleted file mode 100644
--- a/webapprt/test/content/moz.build
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
deleted file mode 100644
--- a/webapprt/test/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-DIRS += ['chrome', 'content']
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -440,55 +440,124 @@ HwcComposer2D::PrepareLayerList(Layer* a
 
 
 #if ANDROID_VERSION >= 18
 bool
 HwcComposer2D::TryHwComposition()
 {
     FramebufferSurface* fbsurface = (FramebufferSurface*)(GetGonkDisplay()->GetFBSurface());
 
+    if (!(fbsurface && fbsurface->lastHandle)) {
+        LOGD("H/W Composition failed. FBSurface not initialized.");
+        return false;
+    }
+
+    // Add FB layer
+    int idx = mList->numHwLayers++;
+    if (idx >= mMaxLayerCount) {
+        if (!ReallocLayerList() || idx >= mMaxLayerCount) {
+            LOGE("TryHwComposition failed! Could not add FB layer");
+            return false;
+        }
+    }
+
+    Prepare(fbsurface->lastHandle, -1);
+
+    for (int j = 0; j < idx; j++) {
+        if (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER) {
+            LOGD("GPU or Partial HWC Composition");
+            return false;
+        }
+    }
+
+    // Full HWC Composition
+    Commit();
+
+    // No composition on FB layer, so closing releaseFenceFd
+    close(mList->hwLayers[idx].releaseFenceFd);
+    mList->hwLayers[idx].releaseFenceFd = -1;
+    mList->numHwLayers = 0;
+    return true;
+}
+
+bool
+HwcComposer2D::Render(EGLDisplay dpy, EGLSurface sur)
+{
+    if (!mList) {
+        // After boot, HWC list hasn't been created yet
+        return GetGonkDisplay()->SwapBuffers(dpy, sur);
+    }
+
+    GetGonkDisplay()->UpdateFBSurface(dpy, sur);
+
+    FramebufferSurface* fbsurface = (FramebufferSurface*)(GetGonkDisplay()->GetFBSurface());
     if (!fbsurface) {
         LOGE("H/W Composition failed. FBSurface not initialized.");
         return false;
     }
 
-    hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = {NULL};
+    if (mList->numHwLayers != 0) {
+        // No mHwc prepare, if already prepared in current draw cycle
+        mList->hwLayers[mList->numHwLayers - 1].handle = fbsurface->lastHandle;
+        mList->hwLayers[mList->numHwLayers - 1].acquireFenceFd = fbsurface->lastFenceFD;
+    } else {
+        mList->numHwLayers = 2;
+        mList->hwLayers[0].hints = 0;
+        mList->hwLayers[0].compositionType = HWC_BACKGROUND;
+        mList->hwLayers[0].flags = HWC_SKIP_LAYER;
+        mList->hwLayers[0].backgroundColor = {0};
+        mList->hwLayers[0].displayFrame = {0, 0, mScreenRect.width, mScreenRect.height};
+        Prepare(fbsurface->lastHandle, fbsurface->lastFenceFD);
+    }
+
+    // GPU or partial HWC Composition
+    Commit();
+
+    GetGonkDisplay()->SetFBReleaseFd(mList->hwLayers[mList->numHwLayers - 1].releaseFenceFd);
+    mList->numHwLayers = 0;
+    return true;
+}
+
+void
+HwcComposer2D::Prepare(buffer_handle_t fbHandle, int fence)
+{
+    int idx = mList->numHwLayers - 1;
     const hwc_rect_t r = {0, 0, mScreenRect.width, mScreenRect.height};
-    int idx = mList->numHwLayers;
+    hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = { nullptr };
 
     displays[HWC_DISPLAY_PRIMARY] = mList;
     mList->flags = HWC_GEOMETRY_CHANGED;
+    mList->outbufAcquireFenceFd = -1;
+    mList->outbuf = nullptr;
     mList->retireFenceFd = -1;
 
     mList->hwLayers[idx].hints = 0;
     mList->hwLayers[idx].flags = 0;
     mList->hwLayers[idx].transform = 0;
-    mList->hwLayers[idx].handle = fbsurface->lastHandle;
+    mList->hwLayers[idx].handle = fbHandle;
     mList->hwLayers[idx].blending = HWC_BLENDING_PREMULT;
     mList->hwLayers[idx].compositionType = HWC_FRAMEBUFFER_TARGET;
     mList->hwLayers[idx].sourceCrop = r;
     mList->hwLayers[idx].displayFrame = r;
     mList->hwLayers[idx].visibleRegionScreen.numRects = 1;
     mList->hwLayers[idx].visibleRegionScreen.rects = &mList->hwLayers[idx].sourceCrop;
-    mList->hwLayers[idx].acquireFenceFd = -1;
+    mList->hwLayers[idx].acquireFenceFd = fence;
     mList->hwLayers[idx].releaseFenceFd = -1;
     mList->hwLayers[idx].planeAlpha = 0xFF;
-    mList->numHwLayers++;
 
     mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
+}
 
-    for (int j = 0; j < idx; j++) {
-        if (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER) {
-            LOGD("GPU or Partial MDP Composition");
-            return false;
-        }
-    }
+bool
+HwcComposer2D::Commit()
+{
+    hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = { nullptr };
+    displays[HWC_DISPLAY_PRIMARY] = mList;
 
-    // Full MDP Composition
-    mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
+    int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
 
     for (int i = 0; i <= MAX_HWC_LAYERS; i++) {
         if (mPrevRelFd[i] <= 0) {
             break;
         }
         if (!i) {
             // Wait for previous retire Fence to signal.
             // Denotes contents on display have been replaced.
@@ -499,35 +568,38 @@ HwcComposer2D::TryHwComposition()
                 LOGE("Wait timed-out for retireFenceFd %d", mPrevRelFd[i]);
             }
         }
         close(mPrevRelFd[i]);
         mPrevRelFd[i] = -1;
     }
 
     mPrevRelFd[0] = mList->retireFenceFd;
-    for (uint32_t j = 0; j < idx; j++) {
+    for (uint32_t j = 0; j < (mList->numHwLayers - 1); j++) {
         if (mList->hwLayers[j].compositionType == HWC_OVERLAY) {
             mPrevRelFd[j + 1] = mList->hwLayers[j].releaseFenceFd;
             mList->hwLayers[j].releaseFenceFd = -1;
         }
     }
 
-    close(mList->hwLayers[idx].releaseFenceFd);
-    mList->hwLayers[idx].releaseFenceFd = -1;
     mList->retireFenceFd = -1;
-    mList->numHwLayers = 0;
-    return true;
+    return !err;
 }
 #else
 bool
 HwcComposer2D::TryHwComposition()
 {
     return !mHwc->set(mHwc, mDpy, mSur, mList);
 }
+
+bool
+HwcComposer2D::Render(EGLDisplay dpy, EGLSurface sur)
+{
+    return GetGonkDisplay()->SwapBuffers(dpy, sur);
+}
 #endif
 
 bool
 HwcComposer2D::TryRender(Layer* aRoot,
                          const gfxMatrix& aGLWorldTransform)
 {
     if (!aGLWorldTransform.PreservesAxisAlignedRectangles()) {
         LOGD("Render aborted. World transform has non-square angle rotation");
@@ -544,22 +616,24 @@ HwcComposer2D::TryRender(Layer* aRoot,
     mVisibleRegions.clear();
 
     if (!PrepareLayerList(aRoot,
                           mScreenRect,
                           gfxMatrix(),
                           aGLWorldTransform))
     {
         LOGD("Render aborted. Nothing was drawn to the screen");
+        if (mList) {
+           mList->numHwLayers = 0;
+        }
         return false;
     }
 
     if (!TryHwComposition()) {
-      // Full MDP Composition
-      LOGE("H/W Composition failed");
-      return false;
+        LOGD("H/W Composition failed");
+        return false;
     }
 
     LOGD("Frame rendered");
     return true;
 }
 
 } // namespace mozilla
--- a/widget/gonk/HwcComposer2D.h
+++ b/widget/gonk/HwcComposer2D.h
@@ -57,17 +57,21 @@ public:
 
     static HwcComposer2D* GetInstance();
 
     // Returns TRUE if the container has been succesfully rendered
     // Returns FALSE if the container cannot be fully rendered
     // by this composer so nothing was rendered at all
     bool TryRender(layers::Layer* aRoot, const gfxMatrix& aGLWorldTransform) MOZ_OVERRIDE;
 
+    bool Render(EGLDisplay dpy, EGLSurface sur);
+
 private:
+    void Prepare(buffer_handle_t fbHandle, int fence);
+    bool Commit();
     bool TryHwComposition();
     bool ReallocLayerList();
     bool PrepareLayerList(layers::Layer* aContainer, const nsIntRect& aClip,
           const gfxMatrix& aParentTransform, const gfxMatrix& aGLWorldTransform);
 
     HwcDevice*              mHwc;
     HwcList*                mList;
     hwc_display_t           mDpy;
--- a/widget/gonk/libdisplay/GonkDisplay.h
+++ b/widget/gonk/libdisplay/GonkDisplay.h
@@ -38,16 +38,20 @@ public:
     virtual void* GetFBSurface() = 0;
 
     virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur) = 0;
 
     virtual ANativeWindowBuffer* DequeueBuffer() = 0;
 
     virtual bool QueueBuffer(ANativeWindowBuffer* buf) = 0;
 
+    virtual void UpdateFBSurface(EGLDisplay dpy, EGLSurface sur) = 0;
+
+    virtual void SetFBReleaseFd(int fd) = 0;
+
     float xdpi;
     uint32_t surfaceformat;
 };
 
 __attribute__ ((weak))
 GonkDisplay* GetGonkDisplay();
 
 }
--- a/widget/gonk/libdisplay/GonkDisplayICS.cpp
+++ b/widget/gonk/libdisplay/GonkDisplayICS.cpp
@@ -191,16 +191,27 @@ GonkDisplayICS::DequeueBuffer()
 
 bool
 GonkDisplayICS::QueueBuffer(ANativeWindowBuffer *buf)
 {
     ANativeWindow *window = static_cast<ANativeWindow *>(mFBSurface.get());
     return !window->queueBuffer(window, buf);
 }
 
+void
+GonkDisplayICS::UpdateFBSurface(EGLDisplay dpy, EGLSurface sur)
+{
+    eglSwapBuffers(dpy, sur);
+}
+
+void
+GonkDisplayICS::SetFBReleaseFd(int fd)
+{
+}
+
 __attribute__ ((visibility ("default")))
 GonkDisplay*
 GetGonkDisplay()
 {
     if (!sGonkDisplay)
         sGonkDisplay = new GonkDisplayICS();
     return sGonkDisplay;
 }
--- a/widget/gonk/libdisplay/GonkDisplayICS.h
+++ b/widget/gonk/libdisplay/GonkDisplayICS.h
@@ -41,16 +41,20 @@ public:
     virtual void* GetFBSurface();
 
     virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur);
 
     virtual ANativeWindowBuffer* DequeueBuffer();
 
     virtual bool QueueBuffer(ANativeWindowBuffer* handle);
 
+    virtual void UpdateFBSurface(EGLDisplay dpy, EGLSurface sur);
+
+    virtual void SetFBReleaseFd(int fd);
+
 private:
     hw_module_t const*        mModule;
     hwc_composer_device_t*    mHwc;
     android::sp<android::FramebufferNativeWindow> mFBSurface;
 };
 
 }
 
--- a/widget/gonk/libdisplay/GonkDisplayJB.cpp
+++ b/widget/gonk/libdisplay/GonkDisplayJB.cpp
@@ -267,16 +267,30 @@ GonkDisplayJB::DequeueBuffer()
 
 bool
 GonkDisplayJB::QueueBuffer(ANativeWindowBuffer* buf)
 {
     bool success = Post(buf->handle, -1);
     return success;
 }
 
+void
+GonkDisplayJB::UpdateFBSurface(EGLDisplay dpy, EGLSurface sur)
+{
+    StopBootAnimation();
+    mBootAnimBuffer = nullptr;
+    eglSwapBuffers(dpy, sur);
+}
+
+void
+GonkDisplayJB::SetFBReleaseFd(int fd)
+{
+    mFBSurface->setReleaseFenceFd(fd);
+}
+
 __attribute__ ((visibility ("default")))
 GonkDisplay*
 GetGonkDisplay()
 {
     if (!sGonkDisplay)
         sGonkDisplay = new GonkDisplayJB();
     return sGonkDisplay;
 }
--- a/widget/gonk/libdisplay/GonkDisplayJB.h
+++ b/widget/gonk/libdisplay/GonkDisplayJB.h
@@ -40,16 +40,20 @@ public:
     virtual void* GetFBSurface();
 
     virtual bool SwapBuffers(EGLDisplay dpy, EGLSurface sur);
 
     virtual ANativeWindowBuffer* DequeueBuffer();
 
     virtual bool QueueBuffer(ANativeWindowBuffer* buf);
 
+    virtual void UpdateFBSurface(EGLDisplay dpy, EGLSurface sur);
+
+    virtual void SetFBReleaseFd(int fd);
+
     bool Post(buffer_handle_t buf, int fence);
 
 private:
     hw_module_t const*        mModule;
     hw_module_t const*        mFBModule;
     hwc_composer_device_1_t*  mHwc;
     framebuffer_device_t*     mFBDevice;
     power_module_t*           mPowerModule;