Merge m-c to b2g-inbound
authorWes Kocher <wkocher@mozilla.com>
Wed, 04 Jun 2014 18:49:51 -0700
changeset 205986 7385e56b5d5516ae2e0c1d862eef744093979cf2
parent 205985 2e13643959568b6f79b99526ce1ae291b51c4571 (current diff)
parent 205810 8828261990767364e2b6c54ecd214924fe12f44b (diff)
child 205987 5d28e6b95af51db91f3e4e33e98559cd4bda334b
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -420,8 +420,9 @@ skip-if = (os == "win" && !debug) || e10
 [browser_wyciwyg_urlbarCopying.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content (content.document.getElementById)
 [browser_zbug569342.js]
 skip-if = e10s # Bug 516755 - SessionStore disabled for e10s
 [browser_registerProtocolHandler_notification.js]
 skip-if = e10s # Bug 940206 - nsIWebContentHandlerRegistrar::registerProtocolHandler doesn't work in e10s
 [browser_no_mcb_on_http_site.js]
 skip-if = e10s # Bug 516755 - SessionStore disabled for e10s
+[browser_bug1003461-switchtab-override.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_bug1003461-switchtab-override.js
@@ -0,0 +1,63 @@
+/* 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/. */
+
+add_task(function* test_switchtab_override() {
+  let testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
+
+  info("Opening first tab");
+  let tab = gBrowser.addTab(testURL);
+  let deferred = Promise.defer();
+  whenTabLoaded(tab, deferred.resolve);
+  yield deferred.promise;
+
+  info("Opening and selecting second tab");
+  let secondTab = gBrowser.selectedTab = gBrowser.addTab();
+  registerCleanupFunction(() => {
+    try {
+      gBrowser.removeTab(tab);
+      gBrowser.removeTab(secondTab);
+    } catch(ex) { /* tabs may have already been closed in case of failure */ }
+  });
+
+  info("Wait for autocomplete")
+  deferred = Promise.defer();
+  let onSearchComplete = gURLBar.onSearchComplete;
+  registerCleanupFunction(() => {
+    gURLBar.onSearchComplete = onSearchComplete;
+  });
+  gURLBar.onSearchComplete = function () {
+    ok(gURLBar.popupOpen, "The autocomplete popup is correctly open");
+    onSearchComplete.apply(gURLBar);
+    deferred.resolve();
+  }
+  
+  gURLBar.focus();
+  gURLBar.value = "dummy_pag";
+  EventUtils.synthesizeKey("e" , {});
+  yield deferred.promise;
+
+  info("Select first autocomplete popup entry");
+  EventUtils.synthesizeKey("VK_DOWN" , {});
+  ok(/moz-action:switchtab/.test(gURLBar.value), "switch to tab entry found");
+
+  info("Override switch-to-tab");
+  let deferred = Promise.defer();
+  // In case of failure this would switch tab.
+  let onTabSelect = event => {
+    deferred.reject(new Error("Should have overridden switch to tab"));
+  };
+  gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect, false);
+  registerCleanupFunction(() => {
+    gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect, false);
+  });
+  // Otherwise it would load the page.
+  whenTabLoaded(secondTab, deferred.resolve);
+
+  EventUtils.synthesizeKey("VK_SHIFT" , { type: "keydown" });
+  EventUtils.synthesizeKey("VK_RETURN" , { });
+  EventUtils.synthesizeKey("VK_SHIFT" , { type: "keyup" });
+  yield deferred.promise;
+
+  yield promiseClearHistory();
+});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -288,16 +288,49 @@ function promiseHistoryClearedState(aURI
       callbackDone();
     });
   });
 
   return deferred.promise;
 }
 
 /**
+ * Allows waiting for an observer notification once.
+ *
+ * @param topic
+ *        Notification topic to observe.
+ *
+ * @return {Promise}
+ * @resolves The array [subject, data] from the observed notification.
+ * @rejects Never.
+ */
+function promiseTopicObserved(topic)
+{
+  let deferred = Promise.defer();
+  Services.obs.addObserver(function PTO_observe(subject, topic, data) {
+    Services.obs.removeObserver(PTO_observe, topic);
+    deferred.resolve([subject, data]);
+  }, topic, false);
+  return deferred.promise;
+}
+
+/**
+ * Clears history asynchronously.
+ *
+ * @return {Promise}
+ * @resolves When history has been cleared.
+ * @rejects Never.
+ */
+function promiseClearHistory() {
+  let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+  PlacesUtils.bhistory.removeAllPages();
+  return promise;
+}
+
+/**
  * Waits for the next top-level document load in the current browser.  The URI
  * of the document is compared against aExpectedURL.  The load is then stopped
  * before it actually starts.
  *
  * @param aExpectedURL
  *        The URL of the document that is expected to load.
  * @return promise
  */
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -138,17 +138,19 @@
         It should return the value that the setter should use.
       -->
       <method name="onBeforeValueSet">
         <parameter name="aValue"/>
         <body><![CDATA[
           this._value = aValue;
           var returnValue = aValue;
           var action = this._parseActionUrl(aValue);
-          if (action) {
+          // Don't put back the action if we are invoked while override actions
+          // is active.
+          if (action && this._numNoActionsKeys <= 0) {
             returnValue = action.param;
             this.setAttribute("actiontype", action.type);
           } else {
             this.removeAttribute("actiontype");
           }
           return returnValue;
         ]]></body>
       </method>
--- a/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
+++ b/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
@@ -19,20 +19,20 @@ add_task(function*() {
 
   let widgetOverflowPanel = document.getElementById("widget-overflow");
   let panelShownPromise = promisePanelElementShown(window, widgetOverflowPanel);
   let identityBox = document.getElementById("identity-box");
   let overflowChevron = document.getElementById("nav-bar-overflow-button");
   ChromeUtils.synthesizeDrop(identityBox, overflowChevron, [], null);
   yield panelShownPromise;
 
-  ok(true, "Overflow panel is shown.");
+  info("Overflow panel is shown.");
 
   let panelHiddenPromise = promisePanelElementHidden(window, widgetOverflowPanel);
-  EventUtils.synthesizeKey("VK_ESCAPE", {});
+  widgetOverflowPanel.hidePopup();
   yield panelHiddenPromise;
 });
 
 add_task(function*() {
   window.resizeTo(originalWindowWidth, window.outerHeight);
   let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
   yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
   ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
--- a/browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
+++ b/browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
@@ -6,27 +6,47 @@
 
 // Bug 968447 - The Bookmarks Toolbar Items doesn't appear as a
 // normal menu panel button in new windows.
 add_task(function() {
   const buttonId = "bookmarks-toolbar-placeholder";
   yield startCustomizing();
   CustomizableUI.addWidgetToArea("personal-bookmarks", CustomizableUI.AREA_PANEL);
   yield endCustomizing();
+
   yield PanelUI.show();
+
   let bookmarksToolbarPlaceholder = document.getElementById(buttonId);
   ok(bookmarksToolbarPlaceholder.classList.contains("toolbarbutton-1"),
      "Button should have toolbarbutton-1 class");
   is(bookmarksToolbarPlaceholder.getAttribute("wrap"), "true",
      "Button should have the 'wrap' attribute");
-  yield PanelUI.hide();
 
-  let newWin = yield openAndLoadWindow();
+  info("Waiting for panel to close");
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+
+  info("Waiting for window to open");
+  let newWin = yield openAndLoadWindow({}, true);
+
+  info("Waiting for panel in new window to open");
   yield newWin.PanelUI.show();
   let newWinBookmarksToolbarPlaceholder = newWin.document.getElementById(buttonId);
   ok(newWinBookmarksToolbarPlaceholder.classList.contains("toolbarbutton-1"),
      "Button in new window should have toolbarbutton-1 class");
   is(newWinBookmarksToolbarPlaceholder.getAttribute("wrap"), "true",
      "Button in new window should have 'wrap' attribute");
-  yield newWin.PanelUI.hide();
+
+  info("Waiting for panel in new window to close");
+  panelHiddenPromise = promisePanelHidden(newWin);
+  newWin.PanelUI.hide();
+  yield panelHiddenPromise;
+
+  info("Waiting for new window to close");
   yield promiseWindowClosed(newWin);
+});
+
+add_task(function asyncCleanUp() {
+  yield endCustomizing();
   CustomizableUI.reset();
 });
+
--- a/browser/components/sessionstore/test/browser_upgrade_backup.js
+++ b/browser/components/sessionstore/test/browser_upgrade_backup.js
@@ -11,35 +11,39 @@ function test() {
   Task.spawn(function task() {
     try {
       // Wait until initialization is complete
       yield SessionStore.promiseInitialized;
 
       const PREF_UPGRADE = "browser.sessionstore.upgradeBackup.latestBuildID";
       let buildID = Services.appinfo.platformBuildID;
 
+      // Write state once before starting the test to
+      // ensure sessionstore.js writes won't happen in between.
+      yield forceSaveState();
+
       // Force backup to take place with a file decided by us
       Services.prefs.setCharPref(PREF_UPGRADE, "");
       let contents = "browser_upgrade_backup.js";
       let pathStore = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
       yield OS.File.writeAtomic(pathStore, contents, { tmpPath: pathStore + ".tmp" });
       yield SessionStore._internal._performUpgradeBackup();
       is(Services.prefs.getCharPref(PREF_UPGRADE), buildID, "upgrade backup should be set (again)");
 
       let pathBackup = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak-" + Services.appinfo.platformBuildID);
       is((yield OS.File.exists(pathBackup)), true, "upgrade backup file has been created");
 
       let data = yield OS.File.read(pathBackup);
-      is(contents, (new TextDecoder()).decode(data), "upgrade backup contains the expected contents");
+      is(new TextDecoder().decode(data), contents, "upgrade backup contains the expected contents");
 
       // Ensure that we don't re-backup by accident
       yield OS.File.writeAtomic(pathStore, "something else entirely", { tmpPath: pathStore + ".tmp" });
       yield SessionStore._internal._performUpgradeBackup();
       data = yield OS.File.read(pathBackup);
-      is(contents, (new TextDecoder()).decode(data), "upgrade backup hasn't changed");
+      is(new TextDecoder().decode(data), contents, "upgrade backup hasn't changed");
 
     } catch (ex) {
       ok(false, "Uncaught error: " + ex + " at " + ex.stack);
     } finally {
       finish();
     }
   });
 }
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -80,16 +80,19 @@ function MarkupView(aInspector, aFrame, 
   this.undo = new UndoStack();
   this.undo.installController(aControllerWindow);
 
   this._containers = new Map();
 
   this._boundMutationObserver = this._mutationObserver.bind(this);
   this.walker.on("mutations", this._boundMutationObserver);
 
+  this._boundOnDisplayChange = this._onDisplayChange.bind(this);
+  this.walker.on("display-change", this._boundOnDisplayChange);
+
   this._boundOnNewSelection = this._onNewSelection.bind(this);
   this._inspector.selection.on("new-node-front", this._boundOnNewSelection);
   this._onNewSelection();
 
   this._boundKeyDown = this._onKeyDown.bind(this);
   this._frame.contentWindow.addEventListener("keydown", this._boundKeyDown, false);
 
   this._boundFocus = this._onFocus.bind(this);
@@ -610,16 +613,29 @@ MarkupView.prototype = {
           }
         });
 
       }
     });
   },
 
   /**
+   * React to display-change events from the walker
+   * @param {Array} nodes An array of nodeFronts
+   */
+  _onDisplayChange: function(nodes) {
+    for (let node of nodes) {
+      let container = this._containers.get(node);
+      if (container) {
+        container.isDisplayed = node.isDisplayed;
+      }
+    }
+  },
+
+  /**
    * Given a list of mutations returned by the mutation observer, flash the
    * corresponding containers to attract attention.
    */
   _flashMutatedNodes: function(aMutations) {
     let addedOrEditedContainers = new Set();
     let removedContainers = new Set();
 
     for (let {type, target, added, removed} of aMutations) {
@@ -1105,16 +1121,19 @@ MarkupView.prototype = {
     this._boundKeyDown = null;
 
     this._inspector.selection.off("new-node-front", this._boundOnNewSelection);
     this._boundOnNewSelection = null;
 
     this.walker.off("mutations", this._boundMutationObserver)
     this._boundMutationObserver = null;
 
+    this.walker.off("display-change", this._boundOnDisplayChange);
+    this._boundOnDisplayChange = null;
+
     this._elt.removeEventListener("mousemove", this._onMouseMove, false);
     this._elt.removeEventListener("mouseleave", this._onMouseLeave, false);
     this._elt = null;
 
     for (let [key, container] of this._containers) {
       container.destroy();
     }
     this._containers = null;
@@ -1263,16 +1282,19 @@ function MarkupContainer(aMarkupView, aN
   // Appending the editor element and attaching event listeners
   this.tagLine.appendChild(this.editor.elt);
 
   this._onMouseDown = this._onMouseDown.bind(this);
   this.elt.addEventListener("mousedown", this._onMouseDown, false);
 
   // Prepare the image preview tooltip data if any
   this._prepareImagePreview();
+
+  // Marking the node as shown or hidden
+  this.isDisplayed = this.node.isDisplayed;
 }
 
 MarkupContainer.prototype = {
   toString: function() {
     return "[MarkupContainer for " + this.node + "]";
   },
 
   isPreviewable: function() {
@@ -1337,16 +1359,26 @@ MarkupContainer.prototype = {
 
     return this.tooltipData.data.then(({data, size}) => {
       tooltip.setImageContent(data, size);
     }, () => {
       tooltip.setBrokenImageContent();
     });
   },
 
+  /**
+   * Show the element has displayed or not
+   */
+  set isDisplayed(isDisplayed) {
+    this.elt.classList.remove("not-displayed");
+    if (!isDisplayed) {
+      this.elt.classList.add("not-displayed");
+    }
+  },
+
   copyImageDataUri: function() {
     // We need to send again a request to gettooltipData even if one was sent for
     // the tooltip, because we want the full-size image
     this.node.getImageData().then(data => {
       data.data.string().then(str => {
         clipboardHelper.copyString(str, this.markup.doc);
       });
     });
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
 subsuite = devtools
 support-files =
   doc_markup_edit.html
   doc_markup_flashing.html
   doc_markup_mutation.html
   doc_markup_navigation.html
+  doc_markup_not_displayed.html
   doc_markup_pagesize_01.html
   doc_markup_pagesize_02.html
   doc_markup_search.html
   doc_markup_toggle.html
   doc_markup_tooltip.png
   head.js
   helper_attributes_test_runner.js
   helper_outerhtml_test_runner.js
@@ -21,16 +22,18 @@ support-files =
 [browser_markupview_highlight_hover_02.js]
 [browser_markupview_html_edit_01.js]
 [browser_markupview_html_edit_02.js]
 [browser_markupview_html_edit_03.js]
 [browser_markupview_image_tooltip.js]
 [browser_markupview_mutation_01.js]
 [browser_markupview_mutation_02.js]
 [browser_markupview_navigation.js]
+[browser_markupview_node_not_displayed_01.js]
+[browser_markupview_node_not_displayed_02.js]
 [browser_markupview_pagesize_01.js]
 [browser_markupview_pagesize_02.js]
 [browser_markupview_search_01.js]
 [browser_markupview_tag_edit_01.js]
 [browser_markupview_tag_edit_02.js]
 [browser_markupview_tag_edit_03.js]
 [browser_markupview_tag_edit_04.js]
 [browser_markupview_tag_edit_05.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_node_not_displayed_01.js
@@ -0,0 +1,33 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that nodes that are not displayed appear differently in the markup-view
+// when these nodes are imported in the view.
+
+// Note that nodes inside a display:none parent are obviously not displayed too
+// but the markup-view uses css inheritance to mark those as hidden instead of
+// having to visit each and every child of a hidden node. So there's no sense
+// testing children nodes.
+
+const TEST_URL = TEST_URL_ROOT + "doc_markup_not_displayed.html";
+const TEST_DATA = [
+  {selector: "#normal-div", isDisplayed: true},
+  {selector: "head", isDisplayed: false},
+  {selector: "#display-none", isDisplayed: false},
+  {selector: "#hidden-true", isDisplayed: false},
+  {selector: "#visibility-hidden", isDisplayed: true}
+];
+
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  for (let {selector, isDisplayed} of TEST_DATA) {
+    info("Getting node " + selector);
+    let container = getContainerForRawNode(selector, inspector);
+    is(!container.elt.classList.contains("not-displayed"), isDisplayed,
+      "The container for " + selector + " is marked as displayed " + isDisplayed);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_node_not_displayed_02.js
@@ -0,0 +1,132 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that nodes are marked as displayed and not-displayed dynamically, when
+// their display changes
+
+const TEST_URL = TEST_URL_ROOT + "doc_markup_not_displayed.html";
+const TEST_DATA = [
+  {
+    desc: "Hiding a node by creating a new stylesheet",
+    selector: "#normal-div",
+    before: true,
+    changeStyle: (doc, node) => {
+      let div = doc.createElement("div");
+      div.id = "new-style";
+      div.innerHTML = "<style>#normal-div {display:none;}</style>";
+      doc.body.appendChild(div);
+    },
+    after: false
+  },
+  {
+    desc: "Showing a node by deleting an existing stylesheet",
+    selector: "#normal-div",
+    before: false,
+    changeStyle: (doc, node) => {
+      doc.getElementById("new-style").remove();
+    },
+    after: true
+  },
+  {
+    desc: "Hiding a node by changing its style property",
+    selector: "#display-none",
+    before: false,
+    changeStyle: (doc, node) => {
+      node.style.display = "block";
+    },
+    after: true
+  },
+  {
+    desc: "Showing a node by removing its hidden attribute",
+    selector: "#hidden-true",
+    before: false,
+    changeStyle: (doc, node) => {
+      node.removeAttribute("hidden");
+    },
+    after: true
+  },
+  {
+    desc: "Hiding a node by adding a hidden attribute",
+    selector: "#hidden-true",
+    before: true,
+    changeStyle: (doc, node) => {
+      node.setAttribute("hidden", "true");
+    },
+    after: false
+  },
+  {
+    desc: "Showing a node by changin a stylesheet's rule",
+    selector: "#hidden-via-stylesheet",
+    before: false,
+    changeStyle: (doc, node) => {
+      doc.styleSheets[0].cssRules[0].style.setProperty("display", "inline");
+    },
+    after: true
+  },
+  {
+    desc: "Hiding a node by adding a new rule to a stylesheet",
+    selector: "#hidden-via-stylesheet",
+    before: true,
+    changeStyle: (doc, node) => {
+      doc.styleSheets[0].insertRule(
+        "#hidden-via-stylesheet {display: none;}", 1);
+    },
+    after: false
+  },
+  {
+    desc: "Hiding a node by adding a class that matches an existing rule",
+    selector: "#normal-div",
+    before: true,
+    changeStyle: (doc, node) => {
+      doc.styleSheets[0].insertRule(
+        ".a-new-class {display: none;}", 2);
+      node.classList.add("a-new-class");
+    },
+    after: false
+  }
+];
+
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  for (let data of TEST_DATA) {
+    info("Running test case: " + data.desc);
+    yield runTestData(inspector, data);
+  }
+});
+
+function runTestData(inspector, {selector, before, changeStyle, after}) {
+  let def = promise.defer();
+
+  info("Getting the " + selector + " test node");
+  let container = getContainerForRawNode(selector, inspector);
+  is(!container.elt.classList.contains("not-displayed"), before,
+    "The container is marked as " + (before ? "shown" : "hidden"));
+
+  info("Listening for the display-change event");
+  inspector.markup.walker.once("display-change", nodes => {
+    info("Verifying that the list of changed nodes include our container");
+
+    ok(nodes.length, "The display-change event was received with a nodes");
+    let foundContainer = false;
+    for (let node of nodes) {
+      if (inspector.markup.getContainer(node) === container) {
+        foundContainer = true;
+        break;
+      }
+    }
+    ok(foundContainer, "Container is part of the list of changed nodes");
+
+    is(!container.elt.classList.contains("not-displayed"), after,
+      "The container is marked as " + (after ? "shown" : "hidden"));
+    def.resolve();
+  });
+
+  info("Making style changes");
+  changeStyle(content.document, getNode(selector));
+
+  return def.promise;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/doc_markup_not_displayed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    #hidden-via-stylesheet {
+      display: none;
+    }
+  </style>
+</head>
+<body>
+  <div id="normal-div"></div>
+  <div id="display-none" style="display:none;"></div>
+  <div id="hidden-true" hidden="true"></div>
+  <div id="visibility-hidden" style="visibility:hidden;"></div>
+  <div id="hidden-via-stylesheet"></div>
+</body>
+</html>
\ No newline at end of file
--- a/browser/themes/shared/devtools/markup-view.css
+++ b/browser/themes/shared/devtools/markup-view.css
@@ -25,16 +25,22 @@
 .theme-selected ~ .editor .theme-fg-color3,
 .theme-selected ~ .editor .theme-fg-color4,
 .theme-selected ~ .editor .theme-fg-color5,
 .theme-selected ~ .editor .theme-fg-color6,
 .theme-selected ~ .editor .theme-fg-color7 {
   color: #f5f7fa; /* Light foreground text */
 }
 
+/* In case a node isn't displayed in the page, we fade the syntax highlighting */
+.not-displayed .open,
+.not-displayed .close {
+  opacity: .7;
+}
+
 .tag-line {
   padding-left: 2px;
 }
 
 /* Preview */
 
 #previewbar {
   position: fixed;
--- a/build/annotationProcessors/AnnotationInfo.java
+++ b/build/annotationProcessors/AnnotationInfo.java
@@ -7,17 +7,19 @@ package org.mozilla.gecko.annotationProc
 /**
  * Object holding annotation data. Used by GeneratableElementIterator.
  */
 public class AnnotationInfo {
     public final String wrapperName;
     public final boolean isStatic;
     public final boolean isMultithreaded;
     public final boolean noThrow;
+    public final boolean narrowChars;
 
     public AnnotationInfo(String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded,
-                          boolean aNoThrow) {
+                          boolean aNoThrow, boolean aNarrowChars) {
         wrapperName = aWrapperName;
         isStatic = aIsStatic;
         isMultithreaded = aIsMultithreaded;
         noThrow = aNoThrow;
+	narrowChars = aNarrowChars;
     }
 }
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -98,47 +98,48 @@ public class CodeGenerator {
 
         boolean isFieldStatic = Utils.isMemberStatic(theMethod);
         boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic;
 
         Class<?>[] parameterTypes = theMethod.getParameterTypes();
         Class<?> returnType = theMethod.getReturnType();
 
         // Get the C++ method signature for this method.
-        String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName);
-        String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic);
+        String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName, aMethodTuple.mAnnotationInfo.narrowChars);
+        String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic, aMethodTuple.mAnnotationInfo.narrowChars);
 
         // Add the header signature to the header file.
         writeSignatureToHeader(headerSignature);
 
         // Use the implementation signature to generate the method body...
         writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap,
             aMethodTuple.mAnnotationInfo.isStatic,
             aMethodTuple.mAnnotationInfo.isMultithreaded,
-            aMethodTuple.mAnnotationInfo.noThrow);
+            aMethodTuple.mAnnotationInfo.noThrow,
+            aMethodTuple.mAnnotationInfo.narrowChars);
     }
 
-    private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) {
+    private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter, boolean aNarrowChars) {
         StringBuilder argumentContent = null;
 
         if (isSetter) {
             Class<?>[] setterArguments = new Class<?>[]{aFieldType};
             // Marshall the argument..
             argumentContent = getArgumentMarshalling(setterArguments);
         }
 
         boolean isObjectReturningMethod = Utils.isObjectType(aFieldType);
         wrapperMethodBodies.append("    ");
         if (isSetter) {
             wrapperMethodBodies.append("env->Set");
         } else {
             wrapperMethodBodies.append("return ");
 
             if (isObjectReturningMethod) {
-                wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">(");
+                wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType, aNarrowChars)).append(">(");
             }
 
             wrapperMethodBodies.append("env->Get");
         }
 
         if (aIsFieldStatic) {
             wrapperMethodBodies.append("Static");
         }
@@ -179,51 +180,51 @@ public class CodeGenerator {
 
         generateMemberCommon(theField, CFieldName, mClassToWrap);
 
         boolean isFieldStatic = Utils.isMemberStatic(theField);
         boolean isFieldFinal = Utils.isMemberFinal(theField);
         boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic;
 
         String getterName = "get" + CFieldName;
-        String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName);
-        String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic);
+        String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars);
+        String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic, aFieldTuple.mAnnotationInfo.narrowChars);
 
         writeSignatureToHeader(getterHeaderSignature);
 
         writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true);
 
-        generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false);
+        generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false, aFieldTuple.mAnnotationInfo.narrowChars);
 
         // If field not final, also generate a setter function.
         if (!isFieldFinal) {
             String setterName = "set" + CFieldName;
 
             Class<?>[] setterArguments = new Class<?>[]{fieldType};
 
-            String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName);
-            String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic);
+            String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars);
+            String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic, aFieldTuple.mAnnotationInfo.narrowChars);
 
             writeSignatureToHeader(setterHeaderSignature);
 
             writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true);
 
-            generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true);
+            generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true, aFieldTuple.mAnnotationInfo.narrowChars);
         }
     }
 
     public void generateConstructor(AnnotatableEntity aCtorTuple) {
         // Unpack the tuple and extract some useful fields from the Method..
         Constructor theCtor = aCtorTuple.getConstructor();
         String CMethodName = mCClassName;
 
         generateMemberCommon(theCtor, mCClassName, mClassToWrap);
 
-        String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName);
-        String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false);
+        String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName, aCtorTuple.mAnnotationInfo.narrowChars);
+        String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false, aCtorTuple.mAnnotationInfo.narrowChars);
 
         // Slice off the "void " from the start of the constructor declaration.
         headerSignature = headerSignature.substring(5);
         implementationSignature = implementationSignature.substring(5);
 
         // Add the header signatures to the header file.
         writeSignatureToHeader(headerSignature);
 
@@ -421,17 +422,17 @@ public class CodeGenerator {
      * @param methodSignature The previously-generated C++ method signature for the method to be
      *                        generated.
      * @param aCMethodName    The C++ method name for the method to be generated.
      * @param aMethod         The Java method to be wrapped by the C++ method being generated.
      * @param aClass          The Java class to which the method belongs.
      */
     private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod,
             Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded,
-            boolean aNoThrow) {
+				 boolean aNoThrow, boolean aNarrowChars) {
         Class<?>[] argumentTypes = aMethod.getParameterTypes();
         Class<?> returnType = aMethod.getReturnType();
 
         writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded);
 
         boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType);
 
         writeFramePushBoilerplate(aMethod, isObjectReturningMethod, aNoThrow);
@@ -450,17 +451,17 @@ public class CodeGenerator {
         }
 
         // Allocate a temporary variable to hold the return type from Java.
         wrapperMethodBodies.append("    ");
         if (!returnType.getCanonicalName().equals("void")) {
             if (isObjectReturningMethod) {
                 wrapperMethodBodies.append("jobject");
             } else {
-                wrapperMethodBodies.append(Utils.getCReturnType(returnType));
+                wrapperMethodBodies.append(Utils.getCReturnType(returnType, aNarrowChars));
             }
             wrapperMethodBodies.append(" temp = ");
         }
 
         boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod);
 
         // The call into Java
         wrapperMethodBodies.append("env->")
@@ -489,18 +490,18 @@ public class CodeGenerator {
         if (!aNoThrow) {
             wrapperMethodBodies.append("    AndroidBridge::HandleUncaughtException(env);\n");
         }
 
         // If we're returning an object, pop the callee's stack frame extracting our ref as the return
         // value.
         if (isObjectReturningMethod) {
             wrapperMethodBodies.append("    ")
-                               .append(Utils.getCReturnType(returnType))
-                               .append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
+		.append(Utils.getCReturnType(returnType, aNarrowChars))
+		.append(" ret = static_cast<").append(Utils.getCReturnType(returnType, aNarrowChars)).append(">(env->PopLocalFrame(temp));\n" +
                                        "    return ret;\n");
         } else if (!returnType.getCanonicalName().equals("void")) {
             // If we're a primitive-returning function, just return the directly-obtained primative
             // from the call to Java.
             wrapperMethodBodies.append("    env->PopLocalFrame(nullptr);\n" +
                                        "    return temp;\n");
         } else {
             // If we don't return anything, just pop the stack frame and move on with life.
--- a/build/annotationProcessors/utils/GeneratableElementIterator.java
+++ b/build/annotationProcessors/utils/GeneratableElementIterator.java
@@ -71,16 +71,17 @@ public class GeneratableElementIterator 
                 // WrappedJNIMethod has parameters. Use Reflection to obtain them.
                 Class<? extends Annotation> annotationType = annotation.annotationType();
                 final String annotationTypeName = annotationType.getName();
                 if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) {
                     String stubName = null;
                     boolean isStaticStub = false;
                     boolean isMultithreadedStub = false;
                     boolean noThrow = false;
+		    boolean narrowChars = false;
                     try {
                         // Determine the explicitly-given name of the stub to generate, if any.
                         final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
                         stubNameMethod.setAccessible(true);
                         stubName = (String) stubNameMethod.invoke(annotation);
 
                         // Detemine if the generated stub should be static.
                         final Method staticStubMethod = annotationType.getDeclaredMethod("generateStatic");
@@ -92,16 +93,21 @@ public class GeneratableElementIterator 
                         multithreadedStubMethod.setAccessible(true);
                         isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation);
 
                         // Determine if ignoring exceptions
                         final Method noThrowMethod = annotationType.getDeclaredMethod("noThrow");
                         noThrowMethod.setAccessible(true);
                         noThrow = (Boolean) noThrowMethod.invoke(annotation);
 
+                        // Determine if strings should be wide or narrow
+                        final Method narrowCharsMethod = annotationType.getDeclaredMethod("narrowChars");
+                        narrowCharsMethod.setAccessible(true);
+                        narrowChars = (Boolean) narrowCharsMethod.invoke(annotation);
+
                     } catch (NoSuchMethodException e) {
                         System.err.println("Unable to find expected field on WrapElementForJNI annotation. Did the signature change?");
                         e.printStackTrace(System.err);
                         System.exit(3);
                     } catch (IllegalAccessException e) {
                         System.err.println("IllegalAccessException reading fields on WrapElementForJNI annotation. Seems the semantics of Reflection have changed...");
                         e.printStackTrace(System.err);
                         System.exit(4);
@@ -113,27 +119,27 @@ public class GeneratableElementIterator 
 
                     // If the method name was not explicitly given in the annotation generate one...
                     if (stubName.isEmpty()) {
                         String aMethodName = candidateElement.getName();
                         stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1);
                     }
 
                     AnnotationInfo annotationInfo = new AnnotationInfo(
-                        stubName, isStaticStub, isMultithreadedStub, noThrow);
+		        stubName, isStaticStub, isMultithreadedStub, noThrow, narrowChars);
                     mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
                     return;
                 }
             }
 
             // If no annotation found, we might be expected to generate anyway using default arguments,
             // thanks to the "Generate everything" annotation.
             if (mIterateEveryEntry) {
                 AnnotationInfo annotationInfo = new AnnotationInfo(
-                    candidateElement.getName(), false, false, false);
+		    candidateElement.getName(), false, false, false, false);
                 mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
                 return;
             }
         }
         mNextReturnValue = null;
     }
 
     @Override
--- a/build/annotationProcessors/utils/Utils.java
+++ b/build/annotationProcessors/utils/Utils.java
@@ -132,17 +132,17 @@ public class Utils {
 
     /**
      * Get the C type corresponding to the provided type parameter. Used for generating argument
      * types for the wrapper method.
      *
      * @param type Class to determine the corresponding JNI type for.
      * @return true if the type an object type, false otherwise.
      */
-    public static String getCParameterType(Class<?> type) {
+    public static String getCParameterType(Class<?> type, boolean aNarrowChars) {
         String name = type.getCanonicalName();
         if (sBasicCTypes.containsKey(name)) {
             return sBasicCTypes.get(name);
         }
         // Are we dealing with an array type?
         int len = name.length();
         if (name.endsWith("[]")) {
             // Determine if it is a 2D array - these map to jobjectArrays
@@ -156,16 +156,19 @@ public class Utils {
                 }
                 return "jobjectArray";
             }
         }
         // Not an array type, check the remaining possibilities before we fall back to jobject
 
         // Check for CharSequences (Strings and things that are string-like)
         if (isCharSequence(type)) {
+	    if (aNarrowChars) {
+		return "const nsACString&";
+	    }
             return "const nsAString&";
         }
 
         if (name.equals("java.lang.Class")) {
             // You're doing reflection on Java objects from inside C, returning Class objects
             // to C, generating the corresponding code using this Java program. Really?!
             return "jclass";
         }
@@ -176,22 +179,22 @@ public class Utils {
     }
 
     /**
      * For a given Java type, get the corresponding C++ type if we're returning it from a function.
      *
      * @param type The Java return type.
      * @return A string representation of the C++ return type.
      */
-    public static String getCReturnType(Class<?> type) {
+     public static String getCReturnType(Class<?> type, boolean aNarrowChars) {
         if (type.getCanonicalName().equals("java.lang.Void")) {
             return "void";
         }
-        String cParameterType = getCParameterType(type);
-        if (cParameterType.equals("const nsAString&")) {
+        String cParameterType = getCParameterType(type, aNarrowChars);
+        if (cParameterType.equals("const nsAString&") || cParameterType.equals("const nsACString&")) {
             return "jstring";
         } else {
             return cParameterType;
         }
     }
 
     /**
      * Gets the type-specific part of the  JNI function to use to get or set a field of a given type.
@@ -376,29 +379,29 @@ public class Utils {
      * generating header files and method bodies.
      *
      * @param aArgumentTypes Argument types of the Java method being wrapped.
      * @param aReturnType Return type of the Java method being wrapped.
      * @param aCMethodName Name of the method to generate in the C++ class.
      * @param aCClassName Name of the C++ class into which the method is declared.
      * @return The C++ method implementation signature for the method described.
      */
-    public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName) {
+    public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aNarrowChars) {
         StringBuilder retBuffer = new StringBuilder();
 
-        retBuffer.append(getCReturnType(aReturnType));
+        retBuffer.append(getCReturnType(aReturnType, aNarrowChars));
         retBuffer.append(' ');
         retBuffer.append(aCClassName);
         retBuffer.append("::");
         retBuffer.append(aCMethodName);
         retBuffer.append('(');
 
         // Write argument types...
         for (int aT = 0; aT < aArgumentTypes.length; aT++) {
-            retBuffer.append(getCParameterType(aArgumentTypes[aT]));
+            retBuffer.append(getCParameterType(aArgumentTypes[aT], aNarrowChars));
             retBuffer.append(" a");
             // We, imaginatively, call our arguments a1, a2, a3...
             // The only way to preserve the names from Java would be to parse the
             // Java source, which would be computationally hard.
             retBuffer.append(aT);
             if (aT != aArgumentTypes.length - 1) {
                 retBuffer.append(", ");
             }
@@ -415,33 +418,33 @@ public class Utils {
      * @param aArgumentAnnotations The annotations on the Java method arguments. Used to specify
      *                             default values etc.
      * @param aReturnType Return type of the Java method being wrapped.
      * @param aCMethodName Name of the method to generate in the C++ class.
      * @param aCClassName Name of the C++ class into which the method is declared.e
      * @param aIsStaticStub true if the generated C++ method should be static, false otherwise.
      * @return The generated C++ header method signature for the method described.
      */
-    public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub) {
+    public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub, boolean aNarrowChars) {
         StringBuilder retBuffer = new StringBuilder();
 
         // Add the static keyword, if applicable.
         if (aIsStaticStub) {
             retBuffer.append("static ");
         }
 
         // Write return type..
-        retBuffer.append(getCReturnType(aReturnType));
+        retBuffer.append(getCReturnType(aReturnType, aNarrowChars));
         retBuffer.append(' ');
         retBuffer.append(aCMethodName);
         retBuffer.append('(');
 
         // Write argument types...
         for (int aT = 0; aT < aArgumentTypes.length; aT++) {
-            retBuffer.append(getCParameterType(aArgumentTypes[aT]));
+            retBuffer.append(getCParameterType(aArgumentTypes[aT], aNarrowChars));
             retBuffer.append(" a");
             // We, imaginatively, call our arguments a1, a2, a3...
             // The only way to preserve the names from Java would be to parse the
             // Java source, which would be computationally hard.
             retBuffer.append(aT);
 
             // Append the default value, if there is one..
             retBuffer.append(getDefaultValueString(aArgumentTypes[aT], aArgumentAnnotations[aT]));
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -2531,17 +2531,19 @@ this.DOMApplicationRegistry = {
     }
 
     // We notify about the successful installation via mgmt.oninstall and the
     // corresponding DOMRequest.onsuccess event as soon as the app is properly
     // saved in the registry.
     yield this._saveApps();
 
     this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
-    if (aData.isPackage && aData.autoInstall) {
+
+    // The presence of a requestID means that we have a page to update.
+    if (aData.isPackage && aData.apkInstall && !aData.requestID) {
       // Skip directly to onInstallSuccessAck, since there isn't
       // a WebappsRegistry to receive Webapps:Install:Return:OK and respond
       // Webapps:Install:Return:Ack when an app is being auto-installed.
       this.onInstallSuccessAck(app.manifestURL);
     } else {
       // Broadcast Webapps:Install:Return:OK so the WebappsRegistry can notify
       // the installing page about the successful install, after which it'll
       // respond Webapps:Install:Return:Ack, which calls onInstallSuccessAck.
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -35,36 +35,16 @@ function getIntPref(prefName, def) {
   try {
     return Services.prefs.getIntPref(prefName);
   }
   catch(err) {
     return def;
   }
 }
 
-function exposeAll(obj) {
-  // Filter for Objects and Arrays.
-  if (typeof obj !== "object" || !obj)
-    return;
-
-  // Recursively expose our children.
-  Object.keys(obj).forEach(function(key) {
-    exposeAll(obj[key]);
-  });
-
-  // If we're not an Array, generate an __exposedProps__ object for ourselves.
-  if (obj instanceof Array)
-    return;
-  var exposed = {};
-  Object.keys(obj).forEach(function(key) {
-    exposed[key] = 'rw';
-  });
-  obj.__exposedProps__ = exposed;
-}
-
 function defineAndExpose(obj, name, value) {
   obj[name] = value;
   if (!('__exposedProps__' in obj))
     obj.__exposedProps__ = {};
   obj.__exposedProps__[name] = 'r';
 }
 
 function visibilityChangeHandler(e) {
@@ -494,17 +474,17 @@ BrowserElementParent.prototype = {
       sendUnblockMsg();
     }
   },
 
   _createEvent: function(evtName, detail, cancelable) {
     // This will have to change if we ever want to send a CustomEvent with null
     // detail.  For now, it's OK.
     if (detail !== undefined && detail !== null) {
-      exposeAll(detail);
+      detail = Cu.cloneInto(detail, this._window);
       return new this._window.CustomEvent('mozbrowser' + evtName,
                                           { bubbles: true,
                                             cancelable: cancelable,
                                             detail: detail });
     }
 
     return new this._window.Event('mozbrowser' + evtName,
                                   { bubbles: true,
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -42,31 +42,32 @@ class ProfileEntry
 
     // Stack pointer for non-JS entries, the script pointer otherwise.
     void * volatile spOrScript;
 
     // Line number for non-JS entries, the bytecode offset otherwise.
     int32_t volatile lineOrPc;
 
     // General purpose storage describing this frame.
-    uint32_t volatile flags;
+    uint32_t volatile flags_;
 
   public:
-    ProfileEntry(void) : flags(0) {}
-
     // These traits are bit masks. Make sure they're powers of 2.
     enum Flags {
         // Indicate whether a profile entry represents a CPP frame. If not set,
         // a JS frame is assumed by default. You're not allowed to publicly
         // change the frame type. Instead, call `setJsFrame` or `setCppFrame`.
         IS_CPP_ENTRY = 0x01,
 
         // Indicate that copying the frame label is not necessary when taking a
         // sample of the pseudostack.
-        FRAME_LABEL_COPY = 0x02
+        FRAME_LABEL_COPY = 0x02,
+
+        // Mask for removing all flags except the category information.
+        CATEGORY_MASK = ~IS_CPP_ENTRY & ~FRAME_LABEL_COPY
     };
 
     MOZ_BEGIN_NESTED_ENUM_CLASS(Category, uint32_t)
         OTHER    = 0x04,
         CSS      = 0x08,
         JS       = 0x10,
         GC       = 0x20,
         CC       = 0x40,
@@ -88,36 +89,43 @@ class ProfileEntry
     bool isJs() const volatile { return !isCpp(); }
 
     bool isCopyLabel() const volatile { return hasFlag(FRAME_LABEL_COPY); };
 
     void setLabel(const char *aString) volatile { string = aString; }
     const char *label() const volatile { return string; }
 
     void setJsFrame(JSScript *aScript, jsbytecode *aPc) volatile {
-        flags &= ~IS_CPP_ENTRY;
+        flags_ = 0;
         spOrScript = aScript;
         setPC(aPc);
     }
     void setCppFrame(void *aSp, uint32_t aLine) volatile {
-        flags |= IS_CPP_ENTRY;
+        flags_ = IS_CPP_ENTRY;
         spOrScript = aSp;
         lineOrPc = static_cast<int32_t>(aLine);
     }
 
     void setFlag(uint32_t flag) volatile {
         MOZ_ASSERT(flag != IS_CPP_ENTRY);
-        flags |= flag;
+        flags_ |= flag;
     }
     void unsetFlag(uint32_t flag) volatile {
         MOZ_ASSERT(flag != IS_CPP_ENTRY);
-        flags &= ~flag;
+        flags_ &= ~flag;
     }
     bool hasFlag(uint32_t flag) const volatile {
-        return bool(flags & uint32_t(flag));
+        return bool(flags_ & flag);
+    }
+
+    uint32_t flags() const volatile {
+        return flags_;
+    }
+    uint32_t category() const volatile {
+        return flags_ & CATEGORY_MASK;
     }
 
     void *stackAddress() const volatile {
         MOZ_ASSERT(!isJs());
         return spOrScript;
     }
     JSScript *script() const volatile {
         MOZ_ASSERT(isJs());
@@ -135,17 +143,17 @@ class ProfileEntry
     // The offset of a pc into a script's code can actually be 0, so to
     // signify a nullptr pc, use a -1 index. This is checked against in
     // pc() and setPC() to set/get the right pc.
     static const int32_t NullPCOffset = -1;
 
     static size_t offsetOfLabel() { return offsetof(ProfileEntry, string); }
     static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); }
     static size_t offsetOfLineOrPc() { return offsetof(ProfileEntry, lineOrPc); }
-    static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags); }
+    static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags_); }
 };
 
 JS_FRIEND_API(void)
 SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
                          uint32_t max);
 
 JS_FRIEND_API(void)
 EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -210,16 +210,17 @@ SPSProfiler::enterNative(const char *str
     volatile ProfileEntry *stack = stack_;
     volatile uint32_t *size = size_;
     uint32_t current = *size;
 
     JS_ASSERT(enabled());
     if (current < max_) {
         stack[current].setLabel(string);
         stack[current].setCppFrame(sp, 0);
+        JS_ASSERT(stack[current].flags() == js::ProfileEntry::IS_CPP_ENTRY);
     }
     *size = current + 1;
 }
 
 void
 SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc, bool copy)
 {
     JS_ASSERT_IF(sp != nullptr, script == nullptr && pc == nullptr);
@@ -230,20 +231,24 @@ SPSProfiler::push(const char *string, vo
     volatile uint32_t *size = size_;
     uint32_t current = *size;
 
     JS_ASSERT(installed());
     if (current < max_) {
         volatile ProfileEntry &entry = stack[current];
         entry.setLabel(string);
 
-        if (sp != nullptr)
+        if (sp != nullptr) {
             entry.setCppFrame(sp, 0);
-        else
+            JS_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY);
+        }
+        else {
             entry.setJsFrame(script, pc);
+            JS_ASSERT(entry.flags() == 0);
+        }
 
         // Track if mLabel needs a copy.
         if (copy)
             entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
         else
             entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
     }
     *size = current + 1;
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -11,16 +11,18 @@ import java.io.File;
 import java.io.FileReader;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
 import java.net.Proxy;
 import java.net.URL;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -2715,9 +2717,36 @@ public class GeckoAppShell
     // Don't fail silently, tell the user that we weren't able to share the image
     private static final void showImageShareFailureToast() {
         Toast toast = Toast.makeText(getContext(),
                                      getContext().getResources().getString(R.string.share_image_failed),
                                      Toast.LENGTH_SHORT);
         toast.show();
     }
 
+    @WrapElementForJNI(allowMultithread = true)
+    static InputStream createInputStream(URLConnection connection) throws IOException {
+        return connection.getInputStream();
+    }
+
+    @WrapElementForJNI(allowMultithread = true, narrowChars = true)
+    static URLConnection getConnection(String url) throws MalformedURLException, IOException {
+        String spec;
+        if (url.startsWith("android://")) {
+            spec = url.substring(10);
+        } else {
+            spec = url.substring(8);
+        }
+
+        // if the colon got stripped, put it back
+        int colon = spec.indexOf(':');
+        if (colon == -1 || colon > spec.indexOf('/')) {
+            spec = spec.replaceFirst("/", ":/");
+        }
+        return new URL(spec).openConnection();
+    }
+
+    @WrapElementForJNI(allowMultithread = true, narrowChars = true)
+    static String connectionGetMimeType(URLConnection connection) {
+        return connection.getContentType();
+    }
+
 }
--- a/mobile/android/base/TelemetryContract.java
+++ b/mobile/android/base/TelemetryContract.java
@@ -59,16 +59,20 @@ public interface TelemetryContract {
 
         // Saving a resource (reader, bookmark, etc) for viewing later.
         // Note: Only used in JavaScript for now, but here for completeness.
         SAVE("save.1"),
 
         // Sharing content.
         SHARE("share.1"),
 
+        // Undoing a user action.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        UNDO("undo.1"),
+
         // Unpinning an item.
         UNPIN("unpin.1"),
 
         // Stop holding a resource (reader, bookmark, etc) for viewing later.
         // Note: Only used in JavaScript for now, but here for completeness.
         UNSAVE("unsave.1"),
 
         // VALUES BELOW THIS LINE ARE EXCLUSIVE TO TESTING.
@@ -132,16 +136,20 @@ public interface TelemetryContract {
 
         // Action triggered from a pageaction in the URLBar.
         // Note: Only used in JavaScript for now, but here for completeness.
         PAGEACTION("pageaction"),
 
         // Action triggered from a suggestion provided to the user.
         SUGGESTION("suggestion"),
 
+        // Action triggered from a SuperToast.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        TOAST("toast"),
+
         // VALUES BELOW THIS LINE ARE EXCLUSIVE TO TESTING.
         _TEST1("_test_method_1"),
         _TEST2("_test_method_2"),
         ;
 
         private final String string;
 
         Method(final String string) {
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -541,36 +541,40 @@ for var in ('ANDROID_PACKAGE_NAME', 'AND
 DEFINES['MANGLED_ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME'].replace('fennec', 'f3nn3c')
 DEFINES['MOZ_APP_ABI'] = CONFIG['TARGET_XPCOM_ABI']
 
 if '-march=armv7' in CONFIG['OS_CFLAGS']:
     DEFINES['MOZ_MIN_CPU_VERSION'] = 7
 else:
     DEFINES['MOZ_MIN_CPU_VERSION'] = 5
 
+generated_recursive_make_targets = ['.aapt.deps', '.locales.deps'] # Captures dependencies on Android manifest and all resources.
+
 generated = add_android_eclipse_library_project('FennecResourcesGenerated')
 generated.package_name = 'org.mozilla.fennec.resources.generated'
 generated.res = OBJDIR + '/res'
+generated.recursive_make_targets += generated_recursive_make_targets
 
 branding = add_android_eclipse_library_project('FennecResourcesBranding')
 branding.package_name = 'org.mozilla.fennec.resources.branding'
 branding.res = TOPSRCDIR + '/' + CONFIG['MOZ_BRANDING_DIRECTORY'] + '/res'
+branding.recursive_make_targets += generated_recursive_make_targets
 
 main = add_android_eclipse_project('Fennec', OBJDIR + '/AndroidManifest.xml')
 main.package_name = 'org.mozilla.gecko'
 
 # These values were extracted from an existing Eclipse project.  Use
 # Project > Resource > Resource Filters and inspect the resulting
 # .project file to modify this list.
 main.filtered_resources += [
     '1.0-projectRelativePath-matches-false-false-*org/mozilla/gecko/resources/**',
     '1.0-projectRelativePath-matches-false-false-*org/mozilla/gecko/tests/**',
 ]
 
-main.recursive_make_targets += ['.aapt.deps'] # Captures dependencies on Android manifest and all resources.
+main.recursive_make_targets += generated_recursive_make_targets
 main.recursive_make_targets += [OBJDIR + '/generated/' + f for f in mgjar.generated_sources]
 main.recursive_make_targets += [OBJDIR + '/generated/' + f for f in gbjar.generated_sources]
 
 main.included_projects += ['../' + generated.name, '../' + branding.name]
 main.extra_jars += [CONFIG['ANDROID_COMPAT_LIB']]
 main.assets = TOPOBJDIR + '/dist/' + CONFIG['MOZ_APP_NAME'] + '/assets'
 main.libs = TOPOBJDIR + '/dist/' + CONFIG['MOZ_APP_NAME'] + '/lib'
 main.res = None
@@ -586,16 +590,17 @@ main.add_classpathentry('generated', OBJ
 main.add_classpathentry('thirdparty', TOPSRCDIR + '/mobile/android/thirdparty',
     dstdir='thirdparty',
     ignore_warnings=True)
 
 resources = add_android_eclipse_library_project('FennecResources')
 resources.package_name = 'org.mozilla.fennec.resources'
 resources.res = SRCDIR + '/resources'
 resources.included_projects += ['../' + generated.name, '../' + branding.name]
+resources.recursive_make_targets += generated_recursive_make_targets
 
 main.included_projects += ['../' + resources.name]
 main.referenced_projects += [resources.name]
 
 if CONFIG['MOZ_CRASHREPORTER']:
     crashreporter = add_android_eclipse_library_project('FennecResourcesCrashReporter')
     crashreporter.package_name = 'org.mozilla.fennec.resources.crashreporter'
     crashreporter.res = SRCDIR + '/crashreporter/res'
--- a/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
+++ b/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
@@ -39,9 +39,11 @@ public @interface WrapElementForJNI {
      */
     boolean allowMultithread() default false;
 
     /**
      * If set, the generated stub will not handle uncaught exceptions.
      * Any exception must be handled or cleared by the code calling the stub.
      */
     boolean noThrow() default false;
+
+    boolean narrowChars() default false;
 }
--- a/mobile/android/base/tests/testSimpleDiscovery.js
+++ b/mobile/android/base/tests/testSimpleDiscovery.js
@@ -7,35 +7,44 @@
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm");
 
 function discovery_observer(subject, topic, data) {
   do_print("Observer: " + data);
 
-  let service = SimpleServiceDiscovery.findServiceForLocation(data);
+  let service = SimpleServiceDiscovery.findServiceForID(data);
   if (!service)
     return;
 
   do_check_eq(service.friendlyName, "Pretend Device");
   do_check_eq(service.uuid, "uuid:5ec9ff92-e8b2-4a94-a72c-76b34e6dabb1");
   do_check_eq(service.manufacturer, "Copy Cat Inc.");
   do_check_eq(service.modelName, "Eureka Dongle");
 
   run_next_test();
 };
 
+var testTarget = {
+  target: "test:service",
+  factory: function(service) { /* dummy */  }
+};
+
 add_test(function test_default() {
   do_register_cleanup(function cleanup() {
+    SimpleServiceDiscovery.unregisterTarget(testTarget);
     Services.obs.removeObserver(discovery_observer, "ssdp-service-found");
   });
 
   Services.obs.addObserver(discovery_observer, "ssdp-service-found", false);
 
+  // We need to register a target or processService will ignore us
+  SimpleServiceDiscovery.registerTarget(testTarget);
+
   // Create a pretend service
   let service = {
     location: "http://mochi.test:8888/tests/robocop/simpleservice.xml",
     target: "test:service"
   };
 
   do_print("Force a detailed ping from a pretend service");
 
--- a/mobile/android/base/toolbar/ToolbarEditText.java
+++ b/mobile/android/base/toolbar/ToolbarEditText.java
@@ -255,16 +255,21 @@ public class ToolbarEditText extends Cus
             }
 
             beginSettingAutocomplete();
 
             // Replace the existing autocomplete text with new one.
             // replace() preserves the autocomplete spans that we set before.
             text.replace(autoCompleteStart, textLength, result, autoCompleteStart, resultLength);
 
+            // Reshow the cursor if there is no longer any autocomplete text.
+            if (autoCompleteStart == resultLength) {
+                setCursorVisible(true);
+            }
+
             endSettingAutocomplete();
 
         } else {
             // No autocomplete text yet; we should add autocomplete text
 
             // If the result prefix doesn't match the current text,
             // the result is stale and we should wait for the another result to come in.
             if (resultLength <= textLength ||
--- a/mobile/android/base/webapp/EventListener.java
+++ b/mobile/android/base/webapp/EventListener.java
@@ -3,47 +3,50 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.webapp;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.util.GeckoEventListener;
+import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.NativeEventListener;
+import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.WebappAllocator;
 
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.util.Log;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-public class EventListener implements GeckoEventListener {
+public class EventListener implements NativeEventListener  {
 
     private static final String LOGTAG = "GeckoWebappEventListener";
 
     public void registerEvents() {
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "Webapps:Preinstall",
             "Webapps:InstallApk",
             "Webapps:Postinstall",
@@ -58,20 +61,20 @@ public class EventListener implements Ge
             "Webapps:InstallApk",
             "Webapps:Postinstall",
             "Webapps:Open",
             "Webapps:Uninstall",
             "Webapps:GetApkVersions");
     }
 
     @Override
-    public void handleMessage(String event, JSONObject message) {
+    public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
         try {
             if (AppConstants.MOZ_ANDROID_SYNTHAPKS && event.equals("Webapps:InstallApk")) {
-                installApk(GeckoAppShell.getGeckoInterface().getActivity(), message.getString("filePath"), message.getString("data"));
+                installApk(GeckoAppShell.getGeckoInterface().getActivity(), message, callback);
             } else if (event.equals("Webapps:Postinstall")) {
                 if (AppConstants.MOZ_ANDROID_SYNTHAPKS) {
                     postInstallWebapp(message.getString("apkPackageName"), message.getString("origin"));
                 } else {
                     postInstallWebapp(message.getString("name"),
                                       message.getString("manifestURL"),
                                       message.getString("origin"),
                                       message.getString("iconURL"),
@@ -89,22 +92,22 @@ public class EventListener implements Ge
                 uninstallWebapp(message.getString("origin"));
             } else if (!AppConstants.MOZ_ANDROID_SYNTHAPKS && event.equals("Webapps:Preinstall")) {
                 String name = message.getString("name");
                 String manifestURL = message.getString("manifestURL");
                 String origin = message.getString("origin");
 
                 JSONObject obj = new JSONObject();
                 obj.put("profile", preInstallWebapp(name, manifestURL, origin).toString());
-                EventDispatcher.sendResponse(message, obj);
+                callback.sendSuccess(obj);
             } else if (event.equals("Webapps:GetApkVersions")) {
                 JSONObject obj = new JSONObject();
                 obj.put("versions", getApkVersions(GeckoAppShell.getGeckoInterface().getActivity(),
-                                                   message.getJSONArray("packageNames")));
-                EventDispatcher.sendResponse(message, obj);
+                                                   message.getStringArray("packageNames")));
+                callback.sendSuccess(obj);
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
     // Not used by MOZ_ANDROID_SYNTHAPKS.
     public static File preInstallWebapp(String aTitle, String aURI, String aOrigin) {
@@ -175,45 +178,47 @@ public class EventListener implements Ge
                 if (proc.processName.equals(targetProcessName)) {
                     android.os.Process.killProcess(proc.pid);
                     break;
                 }
             }
         }
     }
 
-    public static void installApk(final Activity context, String filePath, String data) {
-        // This is the data that mozApps.install sent to Webapps.jsm.
-        JSONObject argsObj = null;
+    public static void installApk(final Activity context, NativeJSObject message, EventCallback callback) {
+        final JSONObject messageData;
 
         // We get the manifest url out of javascript here so we can use it as a checksum
         // in a minute, when a package has been installed.
         String manifestUrl = null;
+        String filePath = null;
+
         try {
-            argsObj = new JSONObject(data);
-            manifestUrl = argsObj.getJSONObject("app").getString("manifestURL");
+            filePath = message.getString("filePath");
+            messageData = new JSONObject(message.getObject("data").toString());
+            manifestUrl = messageData.getJSONObject("app").getString("manifestURL");
         } catch (JSONException e) {
-            Log.e(LOGTAG, "can't get manifest URL from JSON data", e);
-            // TODO: propagate the error back to the mozApps.install caller.
+            Log.wtf(LOGTAG, "Error getting file path and data", e);
+            callback.sendError("Error getting file path and data: " + e.toString());
             return;
         }
 
         // We will check the manifestUrl from the one in the APK.
         // Thus, we can have a one-to-one mapping of apk to receiver.
-        final InstallListener receiver = new InstallListener(manifestUrl, argsObj);
+        final InstallListener receiver = new InstallListener(manifestUrl, messageData);
 
         // Listen for packages being installed.
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addDataScheme("package");
         context.registerReceiver(receiver, filter);
 
         File file = new File(filePath);
         if (!file.exists()) {
             Log.wtf(LOGTAG, "APK file doesn't exist at path " + filePath);
-            // TODO: propagate the error back to the mozApps.install caller.
+            callback.sendError("APK file doesn't exist at path " + filePath);
             return;
         }
 
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
 
         // Now call the package installer.
         ActivityHandlerHelper.startIntentForActivity(context, intent, new ActivityResultHandler() {
@@ -234,25 +239,19 @@ public class EventListener implements Ge
                     }
                 }
             }
         });
     }
 
     private static final int DEFAULT_VERSION_CODE = -1;
 
-    public static JSONObject getApkVersions(Activity context, JSONArray packageNames) {
+    public static JSONObject getApkVersions(Activity context, String[] packageNames) {
         Set<String> packageNameSet = new HashSet<String>();
-        for (int i = 0; i < packageNames.length(); i++) {
-            try {
-                packageNameSet.add(packageNames.getString(i));
-            } catch (JSONException e) {
-                Log.w(LOGTAG, "exception populating settings item", e);
-            }
-        }
+        packageNameSet.addAll(Arrays.asList(packageNames));
 
         final PackageManager pm = context.getPackageManager();
         List<ApplicationInfo> apps = pm.getInstalledApplications(0);
 
         JSONObject jsonMessage = new JSONObject();
 
         for (ApplicationInfo app : apps) {
             if (packageNameSet.contains(app.packageName)) {
--- a/mobile/android/chrome/content/CastingApps.js
+++ b/mobile/android/chrome/content/CastingApps.js
@@ -1,26 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+// Define service targets. We should consider moving these to their respective
+// JSM files, but we left them here to allow for better lazy JSM loading.
+var rokuTarget = {
+  target: "roku:ecp",
+  factory: function(aService) {
+    Cu.import("resource://gre/modules/RokuApp.jsm");
+    return new RokuApp(aService);
+  }
+};
+
 var CastingApps = {
   _castMenuId: -1,
 
   init: function ca_init() {
     if (!this.isEnabled()) {
       return;
     }
 
-    // Register a service target
-    SimpleServiceDiscovery.registerTarget("roku:ecp", function(aService) {
-      Cu.import("resource://gre/modules/RokuApp.jsm");
-      return new RokuApp(aService);
-    });
+    // Register targets
+    SimpleServiceDiscovery.registerTarget(rokuTarget);
 
     // Search for devices continuously every 120 seconds
     SimpleServiceDiscovery.search(120 * 1000);
 
     this._castMenuId = NativeWindow.contextmenus.add(
       Strings.browser.GetStringFromName("contextmenu.castToScreen"),
       this.filterCast,
       this.openExternal.bind(this)
@@ -247,16 +254,20 @@ var CastingApps = {
           CastingApps.openExternal(video, 0, 0);
           return;
         }
       }
     }
   },
 
   _findCastableVideo: function _findCastableVideo(aBrowser) {
+      if (!aBrowser) {
+        return null;
+      }
+
       // Scan for a <video> being actively cast. Also look for a castable <video>
       // on the page.
       let castableVideo = null;
       let videos = aBrowser.contentDocument.querySelectorAll("video");
       for (let video of videos) {
         let unwrappedVideo = XPCNativeWrapper.unwrap(video);
         if (unwrappedVideo.mozIsCasting) {
           // This <video> is cast-active. Break out of loop.
@@ -446,9 +457,8 @@ var CastingApps = {
     }
 
     let status = aRemoteMedia.status;
     if (status == "completed") {
       this.closeExternal();
     }
   }
 };
-
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -984,16 +984,17 @@ var BrowserApp = {
 
     if (aShowUndoToast) {
       let message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
       NativeWindow.toast.show(message, "short", {
         button: {
           icon: "drawable://undo_button_icon",
           label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
           callback: function() {
+            UITelemetry.addEvent("undo.1", "toast", null, "closetab");
             let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
             ss.undoCloseTab(window, 0);
           }
         }
       });
     }
   },
 
--- a/mobile/android/modules/SimpleServiceDiscovery.jsm
+++ b/mobile/android/modules/SimpleServiceDiscovery.jsm
@@ -7,19 +7,19 @@
 
 this.EXPORTED_SYMBOLS = ["SimpleServiceDiscovery"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-function log(msg) {
-  Services.console.logStringMessage("[SSDP] " + msg);
-}
+// Define the "log" function as a binding of the Log.d function so it specifies
+// the "debug" priority and a log tag.
+let log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "SSDP");
 
 XPCOMUtils.defineLazyGetter(this, "converter", function () {
   let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
   conv.charset = "utf8";
   return conv;
 });
 
 // Spec information:
@@ -64,48 +64,42 @@ var SimpleServiceDiscovery = {
     return aURL;
   },
 
   // nsIUDPSocketListener implementation
   onPacketReceived: function(aSocket, aMessage) {
     // Listen for responses from specific targets. There could be more than one
     // available.
     let response = aMessage.data.split("\n");
-    let location;
-    let target;
-    let valid = false;
-    response.some(function(row) {
-      let header = row.toUpperCase();
-      if (header.startsWith("LOCATION")) {
-        location = row.substr(10).trim();
-      } else if (header.startsWith("ST")) {
-        target = row.substr(4).trim();
-        if (this._targets.has(target)) {
-          valid = true;
-        }
+    let service = {};
+    response.forEach(function(row) {
+      let name = row.toUpperCase();
+      if (name.startsWith("LOCATION")) {
+        service.location = row.substr(10).trim();
+      } else if (name.startsWith("ST")) {
+        service.target = row.substr(4).trim();
+      } else if (name.startsWith("SERVER")) {
+        service.server = row.substr(8).trim();
+      }
+    }.bind(this));
+
+    if (service.location && this._targets.has(service.target)) {
+      service.location = this._forceTrailingSlash(service.location);
+
+      // We add the server as an additional way to filter services
+      if (!("server" in service)) {
+        service.server = null;
       }
 
-      if (location && valid) {
-        location = this._forceTrailingSlash(location);
-
-        // When we find a valid response, package up the service information
-        // and pass it on.
-        let service = {
-          location: location,
-          target: target
-        };
-
-        try {
-          this._processService(service);
-        } catch (e) {}
-
-        return true;
-      }
-      return false;
-    }.bind(this));
+      // When we find a valid response, package up the service information
+      // and pass it on.
+      try {
+        this._processService(service);
+      } catch (e) {}
+    }
   },
 
   onStopListening: function(aSocket, aStatus) {
     // This is fired when the socket is closed expectedly or unexpectedly.
     // nsITimer.cancel() is a no-op if the timer is not active.
     this._searchTimeout.cancel();
     this._searchSocket = null;
   },
@@ -213,58 +207,99 @@ var SimpleServiceDiscovery = {
   _searchShutdown: function _searchShutdown() {
     if (this._searchSocket) {
       // This will call onStopListening.
       this._searchSocket.close();
 
       // Clean out any stale services
       for (let [key, service] of this._services) {
         if (service.lastPing != this._searchTimestamp) {
-          Services.obs.notifyObservers(null, EVENT_SERVICE_LOST, service.location);
-          this._services.delete(service.location);
+          Services.obs.notifyObservers(null, EVENT_SERVICE_LOST, service.uuid);
+          this._services.delete(service.uuid);
         }
       }
     }
   },
 
-  registerTarget: function registerTarget(aTarget, aAppFactory) {
+  registerTarget: function registerTarget(aTarget) {
+    // We must have "target" and "factory" defined
+    if (!("target" in aTarget) || !("factory" in aTarget)) {
+      // Fatal for registration
+      throw "Registration requires a target and a location";
+    }
+
     // Only add if we don't already know about this target
-    if (!this._targets.has(aTarget)) {
-      this._targets.set(aTarget, { target: aTarget, factory: aAppFactory });
+    if (!this._targets.has(aTarget.target)) {
+      this._targets.set(aTarget.target, aTarget);
+    }
+  },
+
+  unregisterTarget: function unregisterTarget(aTarget) {
+    // We must have "target" and "factory" defined
+    if (!("target" in aTarget) || !("factory" in aTarget)) {
+      return;
+    }
+
+    // Only remove if we know about this target
+    if (this._targets.has(aTarget.target)) {
+      this._targets.delete(aTarget.target);
     }
   },
 
   findAppForService: function findAppForService(aService) {
     if (!aService || !aService.target) {
       return null;
     }
 
     // Find the registration for the target
     if (this._targets.has(aService.target)) {
       return this._targets.get(aService.target).factory(aService);
     }
     return null;
   },
 
-  findServiceForLocation: function findServiceForLocation(aLocation) {
-    if (this._services.has(aLocation)) {
-      return this._services.get(aLocation);
+  findServiceForID: function findServiceForID(aUUID) {
+    if (this._services.has(aUUID)) {
+      return this._services.get(aUUID);
     }
     return null;
   },
 
   // Returns an array copy of the active services
   get services() {
     let array = [];
     for (let [key, service] of this._services) {
       array.push(service);
     }
     return array;
   },
 
+  // Returns false if the service does not match the target's filters
+  _filterService: function _filterService(aService) {
+    let target = this._targets.get(aService.target);
+    if (!target) {
+      return false;
+    }
+
+    // If we have no filter, everything passes
+    if (!("filters" in target)) {
+      return true;
+    }
+
+    // If any filter fails, the service fails
+    let filters = target.filters;
+    for (let filter in filters) {
+      if (filter in aService && aService[filter] != filters[filter]) {
+        return false;
+      }
+    }
+
+    return true;
+  },
+
   _processService: function _processService(aService) {
     // Use the REST api to request more information about this service
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", aService.location, true);
     xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
     xhr.overrideMimeType("text/xml");
 
     xhr.addEventListener("load", (function() {
@@ -273,22 +308,27 @@ var SimpleServiceDiscovery = {
         aService.appsURL = xhr.getResponseHeader("Application-URL");
         if (aService.appsURL && !aService.appsURL.endsWith("/"))
           aService.appsURL += "/";
         aService.friendlyName = doc.querySelector("friendlyName").textContent;
         aService.uuid = doc.querySelector("UDN").textContent;
         aService.manufacturer = doc.querySelector("manufacturer").textContent;
         aService.modelName = doc.querySelector("modelName").textContent;
 
+        // Filter out services that do not match the target filter
+        if (!this._filterService(aService)) {
+          return;
+        }
+
         // Only add and notify if we don't already know about this service
-        if (!this._services.has(aService.location)) {
-          this._services.set(aService.location, aService);
-          Services.obs.notifyObservers(null, EVENT_SERVICE_FOUND, aService.location);
+        if (!this._services.has(aService.uuid)) {
+          this._services.set(aService.uuid, aService);
+          Services.obs.notifyObservers(null, EVENT_SERVICE_FOUND, aService.uuid);
         }
 
         // Make sure we remember this service is not stale
-        this._services.get(aService.location).lastPing = this._searchTimestamp;
+        this._services.get(aService.uuid).lastPing = this._searchTimestamp;
       }
     }).bind(this), false);
 
     xhr.send(null);
   }
 }
--- a/mobile/android/modules/WebappManager.jsm
+++ b/mobile/android/modules/WebappManager.jsm
@@ -98,17 +98,23 @@ this.WebappManager = {
       aMessageManager.sendAsyncMessage("Webapps:Install:Return:KO", aMessage);
       debug("error downloading APK: " + ex);
       return;
     }
 
     sendMessageToJava({
       type: "Webapps:InstallApk",
       filePath: filePath,
-      data: JSON.stringify(aMessage),
+      data: aMessage,
+    }, (data, error) => {
+      if (!!error) {
+        aMessage.error = error;
+        aMessageManager.sendAsyncMessage("Webapps:Install:Return:KO", aMessage);
+        debug("error downloading APK: " + error);
+      }
     });
   }).bind(this)); },
 
   _downloadApk: function(aManifestUrl) {
     debug("_downloadApk for " + aManifestUrl);
     let deferred = Promise.defer();
 
     // Get the endpoint URL and convert it to an nsIURI/nsIURL object.
@@ -237,18 +243,18 @@ this.WebappManager = {
 
     // The manifest url may be subtly different between the
     // time the APK was built and the APK being installed.
     // Thus, we should take the APK as the source of truth.
     message.app.manifestURL = aData.manifestURL;
     message.app.manifest = aData.manifest;
     message.app.apkPackageName = aData.apkPackageName;
     message.profilePath = aData.profilePath;
-    message.autoInstall = true;
     message.mm = mm;
+    message.apkInstall = true;
 
     DOMApplicationRegistry.registryReady.then(() => {
       switch (aData.type) { // can be hosted or packaged.
         case "hosted":
           DOMApplicationRegistry.doInstall(message, mm);
           break;
 
         case "packaged":
@@ -488,17 +494,17 @@ this.WebappManager = {
         let msg = {
           app: apk.app,
           // TODO: figure out why Webapps:InstallApk needs the "from" property.
           from: apk.app.installOrigin,
         };
         sendMessageToJava({
           type: "Webapps:InstallApk",
           filePath: apk.filePath,
-          data: JSON.stringify(msg),
+          data: msg,
         });
       }
     } else {
       // The user cancelled the notification, so remove the downloaded APKs.
       for (let apk of downloadedApks) {
         try {
           yield OS.file.remove(apk.filePath);
         } catch(ex) {
--- a/toolkit/components/social/FrameWorker.jsm
+++ b/toolkit/components/social/FrameWorker.jsm
@@ -52,16 +52,18 @@ this.getFrameWorkerHandle =
     existingWorker = workerCache[url] = new _Worker(browserPromise, options);
   }
 
   // message the content so it can establish a new connection with the worker.
   let portid = _nextPortId++;
   existingWorker.browserPromise.then(browser => {
     browser.messageManager.sendAsyncMessage("frameworker:connect",
                                             { portId: portid });
+  }).then(null, (ex) => {
+    Cu.reportError("Could not send frameworker:connect: " + ex);
   });
   // return the pseudo worker object.
   let port = new ParentPort(portid, existingWorker.browserPromise, clientWindow);
   existingWorker.ports.set(portid, port);
   return new WorkerHandle(port, existingWorker);
 };
 
 // A "_Worker" is an internal representation of a worker.  It's never returned
--- a/toolkit/devtools/LayoutHelpers.jsm
+++ b/toolkit/devtools/LayoutHelpers.jsm
@@ -468,16 +468,20 @@ LayoutHelpers.prototype = {
 
     // If the boxType is border, no need to go any further, we're done
     if (region === "border") {
       return this._getBoxQuadsFromRect(borderRect, node);
     }
 
     // Else, need to get margin/padding/border distances
     let style = node.ownerDocument.defaultView.getComputedStyle(node);
+    if (!style) {
+      return null;
+    }
+
     let camel = s => s.substring(0, 1).toUpperCase() + s.substring(1);
     let distances = {border:{}, padding:{}, margin: {}};
 
     for (let side of ["top", "right", "bottom", "left"]) {
       distances.border[side] = this._parseNb(style["border" + camel(side) + "Width"]);
       distances.padding[side] = this._parseNb(style["padding" + camel(side)]);
       distances.margin[side] = this._parseNb(style["margin" + camel(side)]);
     }
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -57,16 +57,18 @@ const {Arg, Option, method, RetVal, type
 const {LongStringActor, ShortLongString} = require("devtools/server/actors/string");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const object = require("sdk/util/object");
 const events = require("sdk/event/core");
 const {Unknown} = require("sdk/platform/xpcom");
 const {Class} = require("sdk/core/heritage");
 const {PageStyleActor} = require("devtools/server/actors/styles");
 const {HighlighterActor} = require("devtools/server/actors/highlighter");
+const {getLayoutChangesObserver, releaseLayoutChangesObserver} =
+  require("devtools/server/actors/layout");
 
 const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
 const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const IMAGE_FETCHING_TIMEOUT = 500;
 // The possible completions to a ':' with added score to give certain values
@@ -172,16 +174,20 @@ exports.setValueSummaryLength = function
  */
 var NodeActor = exports.NodeActor = protocol.ActorClass({
   typeName: "domnode",
 
   initialize: function(walker, node) {
     protocol.Actor.prototype.initialize.call(this, null);
     this.walker = walker;
     this.rawNode = node;
+
+    // Storing the original display of the node, to track changes when reflows
+    // occur
+    this.wasDisplayed = this.isDisplayed;
   },
 
   toString: function() {
     return "[NodeActor " + this.actorID + " for " + this.rawNode.toString() + "]";
   },
 
   /**
    * Instead of storing a connection object, the NodeActor gets its connection
@@ -222,16 +228,18 @@ var NodeActor = exports.NodeActor = prot
       // doctype attributes
       name: this.rawNode.name,
       publicId: this.rawNode.publicId,
       systemId: this.rawNode.systemId,
 
       attrs: this.writeAttrs(),
 
       pseudoClassLocks: this.writePseudoClassLocks(),
+
+      isDisplayed: this.isDisplayed,
     };
 
     if (this.isDocumentElement()) {
       form.isDocumentElement = true;
     }
 
     if (this.rawNode.nodeValue) {
       // We only include a short version of the value if it's longer than
@@ -242,16 +250,38 @@ var NodeActor = exports.NodeActor = prot
       } else {
         form.shortValue = this.rawNode.nodeValue;
       }
     }
 
     return form;
   },
 
+  get computedStyle() {
+    if (Cu.isDeadWrapper(this.rawNode) ||
+        this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
+        !this.rawNode.ownerDocument) {
+      return null;
+    }
+    return this.rawNode.ownerDocument.defaultView.getComputedStyle(this.rawNode);
+  },
+
+  /**
+   * Is the node's display computed style value other than "none"
+   */
+  get isDisplayed() {
+    let style = this.computedStyle;
+    if (!style) {
+      // Consider all non-element nodes as displayed
+      return true;
+    } else {
+      return style.display !== "none";
+    }
+  },
+
   writeAttrs: function() {
     if (!this.rawNode.attributes) {
       return undefined;
     }
     return [{namespace: attr.namespace, name: attr.name, value: attr.value }
             for (attr of this.rawNode.attributes)];
   },
 
@@ -543,16 +573,22 @@ let NodeFront = protocol.FrontClass(Node
 
   get attributes() this._form.attrs,
 
   get pseudoClassLocks() this._form.pseudoClassLocks || [],
   hasPseudoClassLock: function(pseudo) {
     return this.pseudoClassLocks.some(locked => locked === pseudo);
   },
 
+  get isDisplayed() {
+    // The NodeActor's form contains the isDisplayed information as a boolean
+    // starting from FF32. Before that, the property is missing
+    return "isDisplayed" in this._form ? this._form.isDisplayed : true;
+  },
+
   getNodeValue: protocol.custom(function() {
     if (!this.incompleteValue) {
       return delayedResolve(new ShortLongString(this.shortValue));
     } else {
       return this._getNodeValue();
     }
   }, {
     impl: "_getNodeValue"
@@ -831,16 +867,20 @@ var WalkerActor = protocol.ActorClass({
       type: "pickerNodeHovered",
       node: Arg(0, "disconnectedNode")
     },
     "highlighter-ready" : {
       type: "highlighter-ready"
     },
     "highlighter-hide" : {
       type: "highlighter-hide"
+    },
+    "display-change" : {
+      type: "display-change",
+      nodes: Arg(0, "array:domnode")
     }
   },
 
   /**
    * Create the WalkerActor
    * @param DebuggerServerConnection conn
    *    The server connection.
    */
@@ -870,16 +910,20 @@ var WalkerActor = protocol.ActorClass({
     this.onFrameUnload = this.onFrameUnload.bind(this);
 
     events.on(tabActor, "will-navigate", this.onFrameUnload);
     events.on(tabActor, "navigate", this.onFrameLoad);
 
     // Ensure that the root document node actor is ready and
     // managed.
     this.rootNode = this.document();
+
+    this.reflowObserver = getLayoutChangesObserver(this.tabActor);
+    this._onReflows = this._onReflows.bind(this);
+    this.reflowObserver.on("reflows", this._onReflows);
   },
 
   // Returns the JSON representation of this object over the wire.
   form: function() {
     return {
       actor: this.actorID,
       root: this.rootNode.form()
     }
@@ -889,27 +933,32 @@ var WalkerActor = protocol.ActorClass({
     return "[WalkerActor " + this.actorID + "]";
   },
 
   destroy: function() {
     this._hoveredNode = null;
     this.clearPseudoClassLocks();
     this._activePseudoClassLocks = null;
     this.rootDoc = null;
+
+    this.reflowObserver.off("reflows", this._onReflows);
+    this.reflowObserver = null;
+    releaseLayoutChangesObserver(this.tabActor);
+
     events.emit(this, "destroyed");
     protocol.Actor.prototype.destroy.call(this);
   },
 
   release: method(function() {}, { release: true }),
 
   unmanage: function(actor) {
     if (actor instanceof NodeActor) {
       if (this._activePseudoClassLocks &&
           this._activePseudoClassLocks.has(actor)) {
-        this.clearPsuedoClassLocks(actor);
+        this.clearPseudoClassLocks(actor);
       }
       this._refMap.delete(actor.rawNode);
     }
     protocol.Actor.prototype.unmanage.call(this, actor);
   },
 
   _ref: function(node) {
     let actor = this._refMap.get(node);
@@ -923,16 +972,34 @@ var WalkerActor = protocol.ActorClass({
     this._refMap.set(node, actor);
 
     if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
       this._watchDocument(actor);
     }
     return actor;
   },
 
+  _onReflows: function(reflows) {
+    // Going through the nodes the walker knows about, see which ones have
+    // had their display changed and send a display-change event if any
+    let changes = [];
+    for (let [node, actor] of this._refMap) {
+      let isDisplayed = actor.isDisplayed;
+      if (isDisplayed !== actor.wasDisplayed) {
+        changes.push(actor);
+        // Updating the original value
+        actor.wasDisplayed = isDisplayed;
+      }
+    }
+
+    if (changes.length) {
+      events.emit(this, "display-change", changes);
+    }
+  },
+
   /**
    * This is kept for backward-compatibility reasons with older remote targets.
    * Targets prior to bug 916443.
    *
    * pick/cancelPick are used to pick a node on click on the content
    * document. But in their implementation prior to bug 916443, they don't allow
    * highlighting on hover.
    * The client-side now uses the highlighter actor's pick and cancelPick
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -54,18 +54,18 @@ ProfileEntry::ProfileEntry(char aTagName
   , mTagName(aTagName)
 { }
 
 ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
   : mTagAddress(aTagAddress)
   , mTagName(aTagName)
 { }
 
-ProfileEntry::ProfileEntry(char aTagName, int aTagLine)
-  : mTagLine(aTagLine)
+ProfileEntry::ProfileEntry(char aTagName, int aTagInt)
+  : mTagInt(aTagInt)
   , mTagName(aTagName)
 { }
 
 ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
   : mTagChar(aTagChar)
   , mTagName(aTagName)
 { }
 
@@ -89,28 +89,28 @@ void* ProfileEntry::get_tagPtr() {
 void ProfileEntry::log()
 {
   // There is no compiler enforced mapping between tag chars
   // and union variant fields, so the following was derived
   // by looking through all the use points of TableTicker.cpp.
   //   mTagMarker (ProfilerMarker*) m
   //   mTagData   (const char*)  c,s
   //   mTagPtr    (void*)        d,l,L,B (immediate backtrace), S(start-of-stack)
-  //   mTagLine   (int)          n,f
+  //   mTagInt    (int)          n,f,y
   //   mTagChar   (char)         h
   //   mTagFloat  (double)       r,t,p,R (resident memory)
   switch (mTagName) {
     case 'm':
       LOGF("%c \"%s\"", mTagName, mTagMarker->GetMarkerName()); break;
     case 'c': case 's':
       LOGF("%c \"%s\"", mTagName, mTagData); break;
     case 'd': case 'l': case 'L': case 'B': case 'S':
       LOGF("%c %p", mTagName, mTagPtr); break;
-    case 'n': case 'f':
-      LOGF("%c %d", mTagName, mTagLine); break;
+    case 'n': case 'f': case 'y':
+      LOGF("%c %d", mTagName, mTagInt); break;
     case 'h':
       LOGF("%c \'%c\'", mTagName, mTagChar); break;
     case 'r': case 't': case 'p': case 'R':
       LOGF("%c %f", mTagName, mTagFloat); break;
     default:
       LOGF("'%c' unknown_tag", mTagName); break;
   }
 }
@@ -357,17 +357,17 @@ void ThreadProfile::StreamJSObject(JSStr
               if (sample) {
                 b.NameValue("rss", entry.mTagFloat);
               }
             }
             break;
           case 'f':
             {
               if (sample) {
-                b.NameValue("frameNumber", entry.mTagLine);
+                b.NameValue("frameNumber", entry.mTagInt);
               }
             }
             break;
           case 't':
             {
               if (sample) {
                 b.NameValue("time", entry.mTagFloat);
               }
@@ -394,47 +394,55 @@ void ThreadProfile::StreamJSObject(JSStr
                   b.NameValue("location", "(root)");
                 b.EndObject();
 
                 int framePos = (readPos + 1) % mEntrySize;
                 ProfileEntry frame = mEntries[framePos];
                 while (framePos != mLastFlushPos && frame.mTagName != 's') {
                   int incBy = 1;
                   frame = mEntries[framePos];
+
                   // Read ahead to the next tag, if it's a 'd' tag process it now
                   const char* tagStringData = frame.mTagData;
                   int readAheadPos = (framePos + 1) % mEntrySize;
                   char tagBuff[DYNAMIC_MAX_STRING];
                   // Make sure the string is always null terminated if it fills up
                   // DYNAMIC_MAX_STRING-2
                   tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
 
                   if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
                     tagStringData = processDynamicTag(framePos, &incBy, tagBuff);
                   }
 
                   // Write one frame. It can have either
                   // 1. only location - 'l' containing a memory address
-                  // 2. location and line number - 'c' followed by 'd's and an optional 'n'
+                  // 2. location and line number - 'c' followed by 'd's,
+                  // an optional 'n' and an optional 'y'
                   if (frame.mTagName == 'l') {
                     b.BeginObject();
                       // Bug 753041
                       // We need a double cast here to tell GCC that we don't want to sign
                       // extend 32-bit addresses starting with 0xFXXXXXX.
                       unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
                       snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
                       b.NameValue("location", tagBuff);
                     b.EndObject();
                   } else if (frame.mTagName == 'c') {
                     b.BeginObject();
                       b.NameValue("location", tagStringData);
                       readAheadPos = (framePos + incBy) % mEntrySize;
                       if (readAheadPos != mLastFlushPos &&
                           mEntries[readAheadPos].mTagName == 'n') {
-                        b.NameValue("line", mEntries[readAheadPos].mTagLine);
+                        b.NameValue("line", mEntries[readAheadPos].mTagInt);
+                        incBy++;
+                      }
+                      readAheadPos = (framePos + incBy) % mEntrySize;
+                      if (readAheadPos != mLastFlushPos &&
+                          mEntries[readAheadPos].mTagName == 'y') {
+                        b.NameValue("category", mEntries[readAheadPos].mTagInt);
                         incBy++;
                       }
                     b.EndObject();
                   }
                   framePos = (framePos + incBy) % mEntrySize;
                 }
               b.EndArray();
             }
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -56,17 +56,17 @@ private:
   union {
     const char* mTagData;
     char        mTagChars[sizeof(void*)];
     void*       mTagPtr;
     ProfilerMarker* mTagMarker;
     float       mTagFloat;
     Address     mTagAddress;
     uintptr_t   mTagOffset;
-    int         mTagLine;
+    int         mTagInt;
     char        mTagChar;
   };
   char mTagName;
 };
 
 #pragma pack(pop)
 
 typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
--- a/tools/profiler/PseudoStack.h
+++ b/tools/profiler/PseudoStack.h
@@ -357,16 +357,17 @@ public:
     }
 
     volatile StackEntry &entry = mStack[mStackPointer];
 
     // Make sure we increment the pointer after the name has
     // been written such that mStack is always consistent.
     entry.setLabel(aName);
     entry.setCppFrame(aStackAddress, line);
+    MOZ_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY);
 
     uint32_t uint_category = static_cast<uint32_t>(aCategory);
     MOZ_ASSERT(
       uint_category >= static_cast<uint32_t>(js::ProfileEntry::Category::FIRST) &&
       uint_category <= static_cast<uint32_t>(js::ProfileEntry::Category::LAST));
 
     entry.setFlag(uint_category);
 
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -369,19 +369,28 @@ void addProfileEntry(volatile StackEntry
     aProfile.addTag(ProfileEntry('c', sampleLabel));
 
     // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
     // line for js entries as well.
     if (entry.isCpp()) {
       lineno = entry.line();
     }
   }
+
   if (lineno != -1) {
     aProfile.addTag(ProfileEntry('n', lineno));
   }
+
+  uint32_t category = entry.category();
+  MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
+  MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
+
+  if (category) {
+    aProfile.addTag(ProfileEntry('y', (int)category));
+  }
 }
 
 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
 typedef struct {
   void** array;
   void** sp_array;
   size_t size;
   size_t count;
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -33,17 +33,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
   int test_size = 50;
   for (int i = 0; i < test_size; i++) {
     tp.addTag(ProfileEntry('t', i));
   }
   ASSERT_TRUE(tp.mEntries != nullptr);
   int readPos = tp.mReadPos;
   while (readPos != tp.mWritePos) {
     ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't');
-    ASSERT_TRUE(tp.mEntries[readPos].mTagLine == readPos);
+    ASSERT_TRUE(tp.mEntries[readPos].mTagInt == readPos);
     readPos = (readPos + 1) % tp.mEntrySize;
   }
 }
 
 // See if wrapping works as it should in the basic case
 TEST(ThreadProfile, InsertTagsWrap) {
   PseudoStack stack;
   Thread::tid_t tid = 1000;
@@ -56,14 +56,14 @@ TEST(ThreadProfile, InsertTagsWrap) {
     tp.addTag(ProfileEntry('t', i));
   }
   ASSERT_TRUE(tp.mEntries != nullptr);
   int readPos = tp.mReadPos;
   int ctr = 0;
   while (readPos != tp.mWritePos) {
     ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't');
     // the first few tags were discarded when we wrapped
-    ASSERT_TRUE(tp.mEntries[readPos].mTagLine == ctr + (test_size - tags));
+    ASSERT_TRUE(tp.mEntries[readPos].mTagInt == ctr + (test_size - tags));
     ctr++;
     readPos = (readPos + 1) % tp.mEntrySize;
   }
 }
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -201,16 +201,26 @@ AndroidBridge::Init(JNIEnv *jEnv)
 
     jclass eglClass = getClassGlobalRef("com/google/android/gles_jni/EGLSurfaceImpl");
     if (eglClass) {
         jEGLSurfacePointerField = getField("mEGLSurface", "I");
     } else {
         jEGLSurfacePointerField = 0;
     }
 
+    jChannels = getClassGlobalRef("java/nio/channels/Channels");
+    jChannelCreate = jEnv->GetStaticMethodID(jChannels, "newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
+
+    jReadableByteChannel = getClassGlobalRef("java/nio/channels/ReadableByteChannel");
+    jByteBufferRead = jEnv->GetMethodID(jReadableByteChannel, "read", "(Ljava/nio/ByteBuffer;)I");
+
+    jInputStream = getClassGlobalRef("java/io/InputStream");
+    jClose = jEnv->GetMethodID(jInputStream, "close", "()V");
+    jAvailable = jEnv->GetMethodID(jInputStream, "available", "()I");
+
     InitAndroidJavaWrappers(jEnv);
 
     // jEnv should NOT be cached here by anything -- the jEnv here
     // is not valid for the real gecko main thread, which is set
     // at SetMainThread time.
 
     return true;
 }
@@ -2088,8 +2098,46 @@ AndroidBridge::RunDelayedTasks()
         mDelayedTaskQueue.RemoveElementAt(0);
         Task* task = nextTask->GetTask();
         delete nextTask;
 
         task->Run();
     }
     return -1;
 }
+
+jobject AndroidBridge::ChannelCreate(jobject stream) {
+    JNIEnv *env = GetJNIForThread();
+    env->PushLocalFrame(1);
+    jobject channel = env->CallStaticObjectMethod(sBridge->jReadableByteChannel, sBridge->jChannelCreate, stream);
+    return env->PopLocalFrame(channel);
+}
+
+void AndroidBridge::InputStreamClose(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    AutoLocalJNIFrame jniFrame(env, 1);
+    env->CallVoidMethod(obj, sBridge->jClose);
+}
+
+uint32_t AndroidBridge::InputStreamAvailable(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    AutoLocalJNIFrame jniFrame(env, 1);
+    return env->CallIntMethod(obj, sBridge->jAvailable);
+}
+
+nsresult AndroidBridge::InputStreamRead(jobject obj, char *aBuf, uint32_t aCount, uint32_t *aRead) {
+    JNIEnv *env = GetJNIForThread();
+    AutoLocalJNIFrame jniFrame(env, 1);
+    jobject arr =  env->NewDirectByteBuffer(aBuf, aCount);
+    jint read = env->CallIntMethod(obj, sBridge->jByteBufferRead, arr);
+
+    if (env->ExceptionCheck()) {
+        env->ExceptionClear();
+        return NS_ERROR_FAILURE;
+    }
+
+    if (read <= 0) {
+        *aRead = 0;
+        return NS_OK;
+    }
+    *aRead = read;
+    return NS_OK;
+}
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -112,17 +112,16 @@ public:
         return mTask;
     }
 
 private:
     Task* mTask;
     TimeStamp mRunTime;
 };
 
-
 class AndroidBridge MOZ_FINAL : public mozilla::layers::GeckoContentController
 {
 public:
     enum {
         // Values for NotifyIME, in addition to values from the Gecko
         // IMEMessage enum; use negative values here to prevent conflict
         NOTIFY_IME_OPEN_VKB = -2,
         NOTIFY_IME_REPLY_EVENT = -1,
@@ -339,16 +338,23 @@ public:
     static jstring NewJavaString(AutoLocalJNIFrame* frame, const char* string);
     static jstring NewJavaString(AutoLocalJNIFrame* frame, const nsACString& string);
 
     static jclass GetClassGlobalRef(JNIEnv* env, const char* className);
     static jfieldID GetFieldID(JNIEnv* env, jclass jClass, const char* fieldName, const char* fieldType);
     static jfieldID GetStaticFieldID(JNIEnv* env, jclass jClass, const char* fieldName, const char* fieldType);
     static jmethodID GetMethodID(JNIEnv* env, jclass jClass, const char* methodName, const char* methodType);
     static jmethodID GetStaticMethodID(JNIEnv* env, jclass jClass, const char* methodName, const char* methodType);
+
+    static jobject ChannelCreate(jobject);
+
+    static void InputStreamClose(jobject obj);
+    static uint32_t InputStreamAvailable(jobject obj);
+    static nsresult InputStreamRead(jobject obj, char *aBuf, uint32_t aCount, uint32_t *aRead);
+
 protected:
     static StaticRefPtr<AndroidBridge> sBridge;
     nsTArray<nsCOMPtr<nsIMobileMessageCallback> > mSmsRequests;
 
     // the global JavaVM
     JavaVM *mJavaVM;
 
     // the JNIEnv for the main thread
@@ -373,16 +379,26 @@ protected:
     bool mHasNativeBitmapAccess;
     bool mHasNativeWindowAccess;
     bool mHasNativeWindowFallback;
 
     int mAPIVersion;
 
     bool QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut);
 
+    // intput stream
+    jclass jReadableByteChannel;
+    jclass jChannels;
+    jmethodID jChannelCreate;
+    jmethodID jByteBufferRead;
+
+    jclass jInputStream;
+    jmethodID jClose;
+    jmethodID jAvailable;
+
     // other things
     jmethodID jNotifyAppShellReady;
     jmethodID jGetOutstandingDrawEvents;
     jmethodID jPostToJavaThread;
     jmethodID jCreateSurface;
     jmethodID jShowSurface;
     jmethodID jHideSurface;
     jmethodID jDestroySurface;
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -1006,8 +1006,35 @@ nsJNIString::nsJNIString(jstring jstr, J
 
     if (len <= 0) {
         SetIsVoid(true);
     } else {
         Assign(reinterpret_cast<const char16_t*>(jCharPtr), len);
     }
     jni->ReleaseStringChars(jstr, jCharPtr);
 }
+
+nsJNICString::nsJNICString(jstring jstr, JNIEnv *jenv)
+{
+    if (!jstr) {
+        SetIsVoid(true);
+        return;
+    }
+    JNIEnv *jni = jenv;
+    if (!jni) {
+        jni = AndroidBridge::GetJNIEnv();
+    }
+    const char* jCharPtr = jni->GetStringUTFChars(jstr, nullptr);
+
+    if (!jCharPtr) {
+        SetIsVoid(true);
+        return;
+    }
+
+    jsize len = jni->GetStringUTFLength(jstr);
+
+    if (len <= 0) {
+        SetIsVoid(true);
+    } else {
+        Assign(jCharPtr, len);
+    }
+    jni->ReleaseStringUTFChars(jstr, jCharPtr);
+}
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -745,11 +745,17 @@ public:
 };
 
 class nsJNIString : public nsString
 {
 public:
     nsJNIString(jstring jstr, JNIEnv *jenv);
 };
 
+class nsJNICString : public nsCString
+{
+public:
+    nsJNICString(jstring jstr, JNIEnv *jenv);
+};
+
 }
 
 #endif
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -15,30 +15,33 @@ jclass GeckoAppShell::mGeckoAppShellClas
 jmethodID GeckoAppShell::jAcknowledgeEvent = 0;
 jmethodID GeckoAppShell::jAddPluginViewWrapper = 0;
 jmethodID GeckoAppShell::jAlertsProgressListener_OnProgress = 0;
 jmethodID GeckoAppShell::jCancelVibrate = 0;
 jmethodID GeckoAppShell::jCheckURIVisited = 0;
 jmethodID GeckoAppShell::jClearMessageList = 0;
 jmethodID GeckoAppShell::jCloseCamera = 0;
 jmethodID GeckoAppShell::jCloseNotification = 0;
+jmethodID GeckoAppShell::jConnectionGetMimeType = 0;
+jmethodID GeckoAppShell::jCreateInputStream = 0;
 jmethodID GeckoAppShell::jCreateMessageListWrapper = 0;
 jmethodID GeckoAppShell::jCreateShortcut = 0;
 jmethodID GeckoAppShell::jDeleteMessageWrapper = 0;
 jmethodID GeckoAppShell::jDisableBatteryNotifications = 0;
 jmethodID GeckoAppShell::jDisableNetworkNotifications = 0;
 jmethodID GeckoAppShell::jDisableScreenOrientationNotifications = 0;
 jmethodID GeckoAppShell::jDisableSensor = 0;
 jmethodID GeckoAppShell::jEnableBatteryNotifications = 0;
 jmethodID GeckoAppShell::jEnableLocation = 0;
 jmethodID GeckoAppShell::jEnableLocationHighAccuracy = 0;
 jmethodID GeckoAppShell::jEnableNetworkNotifications = 0;
 jmethodID GeckoAppShell::jEnableScreenOrientationNotifications = 0;
 jmethodID GeckoAppShell::jEnableSensor = 0;
 jmethodID GeckoAppShell::jGamepadAdded = 0;
+jmethodID GeckoAppShell::jGetConnection = 0;
 jmethodID GeckoAppShell::jGetContext = 0;
 jmethodID GeckoAppShell::jGetCurrentBatteryInformationWrapper = 0;
 jmethodID GeckoAppShell::jGetCurrentNetworkInformationWrapper = 0;
 jmethodID GeckoAppShell::jGetDensity = 0;
 jmethodID GeckoAppShell::jGetDpiWrapper = 0;
 jmethodID GeckoAppShell::jGetExtensionFromMimeTypeWrapper = 0;
 jmethodID GeckoAppShell::jGetHandlersForMimeTypeWrapper = 0;
 jmethodID GeckoAppShell::jGetHandlersForURLWrapper = 0;
@@ -97,30 +100,33 @@ void GeckoAppShell::InitStubs(JNIEnv *jE
     jAcknowledgeEvent = getStaticMethod("acknowledgeEvent", "()V");
     jAddPluginViewWrapper = getStaticMethod("addPluginView", "(Landroid/view/View;FFFFZ)V");
     jAlertsProgressListener_OnProgress = getStaticMethod("alertsProgressListener_OnProgress", "(Ljava/lang/String;JJLjava/lang/String;)V");
     jCancelVibrate = getStaticMethod("cancelVibrate", "()V");
     jCheckURIVisited = getStaticMethod("checkUriVisited", "(Ljava/lang/String;)V");
     jClearMessageList = getStaticMethod("clearMessageList", "(I)V");
     jCloseCamera = getStaticMethod("closeCamera", "()V");
     jCloseNotification = getStaticMethod("closeNotification", "(Ljava/lang/String;)V");
+    jConnectionGetMimeType = getStaticMethod("connectionGetMimeType", "(Ljava/net/URLConnection;)Ljava/lang/String;");
+    jCreateInputStream = getStaticMethod("createInputStream", "(Ljava/net/URLConnection;)Ljava/io/InputStream;");
     jCreateMessageListWrapper = getStaticMethod("createMessageList", "(JJ[Ljava/lang/String;IIZI)V");
     jCreateShortcut = getStaticMethod("createShortcut", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     jDeleteMessageWrapper = getStaticMethod("deleteMessage", "(II)V");
     jDisableBatteryNotifications = getStaticMethod("disableBatteryNotifications", "()V");
     jDisableNetworkNotifications = getStaticMethod("disableNetworkNotifications", "()V");
     jDisableScreenOrientationNotifications = getStaticMethod("disableScreenOrientationNotifications", "()V");
     jDisableSensor = getStaticMethod("disableSensor", "(I)V");
     jEnableBatteryNotifications = getStaticMethod("enableBatteryNotifications", "()V");
     jEnableLocation = getStaticMethod("enableLocation", "(Z)V");
     jEnableLocationHighAccuracy = getStaticMethod("enableLocationHighAccuracy", "(Z)V");
     jEnableNetworkNotifications = getStaticMethod("enableNetworkNotifications", "()V");
     jEnableScreenOrientationNotifications = getStaticMethod("enableScreenOrientationNotifications", "()V");
     jEnableSensor = getStaticMethod("enableSensor", "(I)V");
     jGamepadAdded = getStaticMethod("gamepadAdded", "(II)V");
+    jGetConnection = getStaticMethod("getConnection", "(Ljava/lang/String;)Ljava/net/URLConnection;");
     jGetContext = getStaticMethod("getContext", "()Landroid/content/Context;");
     jGetCurrentBatteryInformationWrapper = getStaticMethod("getCurrentBatteryInformation", "()[D");
     jGetCurrentNetworkInformationWrapper = getStaticMethod("getCurrentNetworkInformation", "()[D");
     jGetDensity = getStaticMethod("getDensity", "()F");
     jGetDpiWrapper = getStaticMethod("getDpi", "()I");
     jGetExtensionFromMimeTypeWrapper = getStaticMethod("getExtensionFromMimeType", "(Ljava/lang/String;)Ljava/lang/String;");
     jGetHandlersForMimeTypeWrapper = getStaticMethod("getHandlersForMimeType", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;");
     jGetHandlersForURLWrapper = getStaticMethod("getHandlersForURL", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;");
@@ -290,16 +296,42 @@ void GeckoAppShell::CloseNotification(co
 
     jstring j0 = AndroidBridge::NewJavaString(env, a0);
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jCloseNotification, j0);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
 
+jstring GeckoAppShell::ConnectionGetMimeType(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jConnectionGetMimeType, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject GeckoAppShell::CreateInputStream(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jCreateInputStream, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
 void GeckoAppShell::CreateMessageListWrapper(int64_t a0, int64_t a1, jobjectArray a2, int32_t a3, int32_t a4, bool a5, int32_t a6) {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(1) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     jvalue args[7];
@@ -473,16 +505,31 @@ void GeckoAppShell::GamepadAdded(int32_t
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jGamepadAdded, a0, a1);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
 
+jobject GeckoAppShell::GetConnection(const nsACString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetConnection, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
 jobject GeckoAppShell::GetContext() {
     JNIEnv *env = GetJNIForThread();
     if (env->PushLocalFrame(1) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetContext);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -22,30 +22,33 @@ public:
     static void AcknowledgeEvent();
     static void AddPluginViewWrapper(jobject a0, jfloat a1, jfloat a2, jfloat a3, jfloat a4, bool a5);
     static void AlertsProgressListener_OnProgress(const nsAString& a0, int64_t a1, int64_t a2, const nsAString& a3);
     static void CancelVibrate();
     static void CheckURIVisited(const nsAString& a0);
     static void ClearMessageList(int32_t a0);
     static void CloseCamera();
     static void CloseNotification(const nsAString& a0);
+    static jstring ConnectionGetMimeType(jobject a0);
+    static jobject CreateInputStream(jobject a0);
     static void CreateMessageListWrapper(int64_t a0, int64_t a1, jobjectArray a2, int32_t a3, int32_t a4, bool a5, int32_t a6);
     static void CreateShortcut(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3);
     static void DeleteMessageWrapper(int32_t a0, int32_t a1);
     static void DisableBatteryNotifications();
     static void DisableNetworkNotifications();
     static void DisableScreenOrientationNotifications();
     static void DisableSensor(int32_t a0);
     static void EnableBatteryNotifications();
     static void EnableLocation(bool a0);
     static void EnableLocationHighAccuracy(bool a0);
     static void EnableNetworkNotifications();
     static void EnableScreenOrientationNotifications();
     static void EnableSensor(int32_t a0);
     static void GamepadAdded(int32_t a0, int32_t a1);
+    static jobject GetConnection(const nsACString& a0);
     static jobject GetContext();
     static jdoubleArray GetCurrentBatteryInformationWrapper();
     static jdoubleArray GetCurrentNetworkInformationWrapper();
     static jfloat GetDensity();
     static int32_t GetDpiWrapper();
     static jstring GetExtensionFromMimeTypeWrapper(const nsAString& a0);
     static jobjectArray GetHandlersForMimeTypeWrapper(const nsAString& a0, const nsAString& a1);
     static jobjectArray GetHandlersForURLWrapper(const nsAString& a0, const nsAString& a1);
@@ -103,30 +106,33 @@ protected:
     static jmethodID jAcknowledgeEvent;
     static jmethodID jAddPluginViewWrapper;
     static jmethodID jAlertsProgressListener_OnProgress;
     static jmethodID jCancelVibrate;
     static jmethodID jCheckURIVisited;
     static jmethodID jClearMessageList;
     static jmethodID jCloseCamera;
     static jmethodID jCloseNotification;
+    static jmethodID jConnectionGetMimeType;
+    static jmethodID jCreateInputStream;
     static jmethodID jCreateMessageListWrapper;
     static jmethodID jCreateShortcut;
     static jmethodID jDeleteMessageWrapper;
     static jmethodID jDisableBatteryNotifications;
     static jmethodID jDisableNetworkNotifications;
     static jmethodID jDisableScreenOrientationNotifications;
     static jmethodID jDisableSensor;
     static jmethodID jEnableBatteryNotifications;
     static jmethodID jEnableLocation;
     static jmethodID jEnableLocationHighAccuracy;
     static jmethodID jEnableNetworkNotifications;
     static jmethodID jEnableScreenOrientationNotifications;
     static jmethodID jEnableSensor;
     static jmethodID jGamepadAdded;
+    static jmethodID jGetConnection;
     static jmethodID jGetContext;
     static jmethodID jGetCurrentBatteryInformationWrapper;
     static jmethodID jGetCurrentNetworkInformationWrapper;
     static jmethodID jGetDensity;
     static jmethodID jGetDpiWrapper;
     static jmethodID jGetExtensionFromMimeTypeWrapper;
     static jmethodID jGetHandlersForMimeTypeWrapper;
     static jmethodID jGetHandlersForURLWrapper;
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -22,16 +22,17 @@ SOURCES += [
     'AndroidDirectTexture.cpp',
     'AndroidGraphicBuffer.cpp',
     'AndroidJavaWrappers.cpp',
     'AndroidJNI.cpp',
     'AndroidJNIWrapper.cpp',
     'GeneratedJNIWrappers.cpp',
     'GfxInfo.cpp',
     'NativeJSContainer.cpp',
+    'nsAndroidProtocolHandler.cpp',
     'nsAppShell.cpp',
     'nsClipboard.cpp',
     'nsDeviceContextAndroid.cpp',
     'nsIdleServiceAndroid.cpp',
     'nsIMEPicker.cpp',
     'nsLookAndFeel.cpp',
     'nsPrintOptionsAndroid.cpp',
     'nsScreenManagerAndroid.cpp',
@@ -46,15 +47,16 @@ LIBRARY_NAME = 'widget_android'
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
     '/dom/system/android',
+    '/netwerk/base/src',
     '/netwerk/cache',
     '/widget/android/android',
     '/widget/shared',
     '/widget/xpwidgets',
 ]
 
 #DEFINES['DEBUG_WIDGETS'] = True
new file mode 100644
--- /dev/null
+++ b/widget/android/nsAndroidProtocolHandler.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAndroidProtocolHandler.h"
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+#include "nsIIOService.h"
+#include "nsNetUtil.h"
+#include "android/log.h"
+#include "nsBaseChannel.h"
+#include "AndroidBridge.h"
+#include "GeneratedJNIWrappers.h"
+
+using namespace mozilla;
+using namespace mozilla::widget::android;
+
+class AndroidInputStream : public nsIInputStream
+{
+public:
+    AndroidInputStream(jobject connection) {
+        JNIEnv *env = GetJNIForThread();
+        mBridgeInputStream = env->NewGlobalRef(GeckoAppShell::CreateInputStream(connection));
+        mBridgeChannel = env->NewGlobalRef(AndroidBridge::ChannelCreate(mBridgeInputStream));
+    }
+    virtual ~AndroidInputStream() {
+        JNIEnv *env = GetJNIForThread();
+        env->DeleteGlobalRef(mBridgeInputStream);
+        env->DeleteGlobalRef(mBridgeChannel);
+    }
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSIINPUTSTREAM
+
+    private:
+    jobject mBridgeInputStream;
+    jobject mBridgeChannel;
+};
+
+NS_IMPL_ISUPPORTS(AndroidInputStream, nsIInputStream)
+
+NS_IMETHODIMP AndroidInputStream::Close(void) {
+    mozilla::AndroidBridge::InputStreamClose(mBridgeInputStream);
+    return NS_OK;
+}
+
+NS_IMETHODIMP AndroidInputStream::Available(uint64_t *_retval) {
+    *_retval = mozilla::AndroidBridge::InputStreamAvailable(mBridgeInputStream);
+    return NS_OK;
+}
+
+NS_IMETHODIMP AndroidInputStream::Read(char *aBuf, uint32_t aCount, uint32_t *_retval) {
+    return  mozilla::AndroidBridge::InputStreamRead(mBridgeChannel, aBuf, aCount, _retval);
+}
+
+NS_IMETHODIMP AndroidInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *_retval) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP AndroidInputStream::IsNonBlocking(bool *_retval) {
+    *_retval = false;
+    return NS_OK;
+}
+
+
+class AndroidChannel : public nsBaseChannel
+{
+private:
+    AndroidChannel(nsIURI *aURI, jobject aConnection) {
+        JNIEnv *env = GetJNIForThread();
+        mConnection = env->NewGlobalRef(aConnection);
+        mURI = aURI;
+        nsCString type;
+        jstring jtype = GeckoAppShell::ConnectionGetMimeType(mConnection);
+        if (jtype)
+            SetContentType(nsJNICString(jtype, env));
+    }
+public:
+    static AndroidChannel* CreateChannel(nsIURI *aURI)  {
+        nsCString spec;
+        aURI->GetSpec(spec);
+        jobject connection = GeckoAppShell::GetConnection(spec);
+        if (!connection)
+            return NULL;
+        return new AndroidChannel(aURI, connection);
+    }
+    ~AndroidChannel() {
+        JNIEnv *env = GetJNIForThread();
+        env->DeleteGlobalRef(mConnection);
+    }
+
+    virtual nsresult OpenContentStream(bool async, nsIInputStream **result,
+                                       nsIChannel** channel) {
+        nsCOMPtr<nsIInputStream> stream = new AndroidInputStream(mConnection);
+        NS_ADDREF(*result = stream);
+        return NS_OK;
+    }
+private:
+    jobject mConnection;
+};
+
+NS_IMPL_ISUPPORTS(nsAndroidProtocolHandler,
+                  nsIProtocolHandler,
+                  nsISupportsWeakReference)
+
+
+NS_IMETHODIMP
+nsAndroidProtocolHandler::GetScheme(nsACString &result)
+{
+    result.AssignLiteral("android");
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAndroidProtocolHandler::GetDefaultPort(int32_t *result)
+{
+    *result = -1;        // no port for android: URLs
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAndroidProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+    // don't override anything.
+    *_retval = false;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAndroidProtocolHandler::GetProtocolFlags(uint32_t *result)
+{
+    *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE | URI_NORELATIVE | URI_DANGEROUS_TO_LOAD;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAndroidProtocolHandler::NewURI(const nsACString &aSpec,
+                                 const char *aCharset,
+                                 nsIURI *aBaseURI,
+                                 nsIURI **result)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIStandardURL> surl(do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, aCharset, aBaseURI);
+    if (NS_FAILED(rv))
+        return rv;
+
+    nsCOMPtr<nsIURL> url(do_QueryInterface(surl, &rv));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    surl->SetMutable(false);
+
+    NS_ADDREF(*result = url);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAndroidProtocolHandler::NewChannel(nsIURI* aURI,
+                                     nsIChannel* *aResult)
+{
+    nsCOMPtr<nsIChannel> channel = AndroidChannel::CreateChannel(aURI);
+    if (!channel)
+        return NS_ERROR_FAILURE;
+    NS_ADDREF(*aResult = channel);
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/widget/android/nsAndroidProtocolHandler.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAndroidProtocolHandler_h___
+#define nsAndroidProtocolHandler_h___
+
+#include "nsIProtocolHandler.h"
+#include "nsWeakReference.h"
+#include "mozilla/Attributes.h"
+
+#define NS_ANDROIDPROTOCOLHANDLER_CID                 \
+{ /* e9cd2b7f-8386-441b-aaf5-0b371846bfd0 */         \
+    0xe9cd2b7f,                                      \
+    0x8386,                                          \
+    0x441b,                                          \
+    {0x0b, 0x37, 0x18, 0x46, 0xbf, 0xd0}             \
+}
+
+class nsAndroidProtocolHandler MOZ_FINAL : public nsIProtocolHandler,
+                                           public nsSupportsWeakReference
+{
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    // nsIProtocolHandler methods:
+    NS_DECL_NSIPROTOCOLHANDLER
+
+    // nsAndroidProtocolHandler methods:
+    nsAndroidProtocolHandler() {}
+    ~nsAndroidProtocolHandler() {}
+};
+
+#endif /* nsAndroidProtocolHandler_h___ */
--- a/widget/android/nsWidgetFactory.cpp
+++ b/widget/android/nsWidgetFactory.cpp
@@ -20,29 +20,31 @@
 #include "nsClipboardHelper.h"
 #include "nsTransferable.h"
 #include "nsPrintOptionsAndroid.h"
 #include "nsPrintSession.h"
 #include "nsDeviceContextAndroid.h"
 #include "nsHTMLFormatConverter.h"
 #include "nsIMEPicker.h"
 #include "nsXULAppAPI.h"
+#include "nsAndroidProtocolHandler.h"
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerAndroid)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceAndroid, nsIdleServiceAndroid::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsAndroid, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecAndroid)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIMEPicker)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroidBridge)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroidProtocolHandler)
 
 #include "GfxInfo.h"
 namespace mozilla {
 namespace widget {
 // This constructor should really be shared with all platforms.
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
 }
 }
@@ -57,16 +59,17 @@ NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID);
 NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_IMEPICKER_CID);
 NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
 NS_DEFINE_NAMED_CID(NS_ANDROIDBRIDGE_CID);
+NS_DEFINE_NAMED_CID(NS_ANDROIDPROTOCOLHANDLER_CID);
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
   { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor },
   { &kNS_CHILD_CID, false, nullptr, nsWindowConstructor },
   { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor },
   { &kNS_SCREENMANAGER_CID, false, nullptr, nsScreenManagerAndroidConstructor },
   { &kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceAndroidConstructor },
   { &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor },
@@ -74,16 +77,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_CLIPBOARDHELPER_CID, false, nullptr, nsClipboardHelperConstructor },
   { &kNS_PRINTSETTINGSSERVICE_CID, false, nullptr, nsPrintOptionsAndroidConstructor },
   { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor },
   { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecAndroidConstructor },
   { &kNS_HTMLFORMATCONVERTER_CID, false, nullptr, nsHTMLFormatConverterConstructor },
   { &kNS_IMEPICKER_CID, false, nullptr, nsIMEPickerConstructor },
   { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor },
   { &kNS_ANDROIDBRIDGE_CID, false, nullptr, nsAndroidBridgeConstructor },
+  { &kNS_ANDROIDPROTOCOLHANDLER_CID, false, nullptr, nsAndroidProtocolHandlerConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
   { "@mozilla.org/widgets/window/android;1", &kNS_WINDOW_CID },
   { "@mozilla.org/widgets/child_window/android;1", &kNS_CHILD_CID },
   { "@mozilla.org/widget/appshell/android;1", &kNS_APPSHELL_CID },
   { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID },
@@ -93,16 +97,17 @@ static const mozilla::Module::ContractID
   { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
   { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
   { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
   { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
   { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID },
   { "@mozilla.org/imepicker;1", &kNS_IMEPICKER_CID },
   { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
   { "@mozilla.org/android/bridge;1", &kNS_ANDROIDBRIDGE_CID },
+  { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "android", &kNS_ANDROIDPROTOCOLHANDLER_CID },
   { nullptr }
 };
 
 static void
 nsWidgetAndroidModuleDtor()
 {
     nsLookAndFeel::Shutdown();
     nsAppShellShutdown();
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -254,30 +254,25 @@ HwcComposer2D::PrepareLayerList(Layer* a
                 return false;
             }
         }
         return true;
     }
 
     LayerRenderState state = aLayer->GetRenderState();
     nsIntSize surfaceSize;
-    bool skipLayer = false;
 
     if (state.mSurface.get()) {
         surfaceSize = state.mSize;
     } else {
         if (aLayer->AsColorLayer() && mColorFill) {
             fillColor = true;
         } else {
             LOGD("%s Layer doesn't have a gralloc buffer", aLayer->Name());
-#if ANDROID_VERSION >= 18
-            skipLayer = true;
-#else
             return false;
-#endif
         }
     }
     // Buffer rotation is not to be confused with the angled rotation done by a transform matrix
     // It's a fancy ThebesLayer feature used for scrolling
     if (state.BufferRotated()) {
         LOGD("%s Layer has a rotated buffer", aLayer->Name());
         return false;
     }
@@ -335,18 +330,18 @@ HwcComposer2D::PrepareLayerList(Layer* a
     }
 
     HwcLayer& hwcLayer = mList->hwLayers[current];
     hwcLayer.displayFrame = displayFrame;
     setCrop(&hwcLayer, sourceCrop);
     buffer_handle_t handle = fillColor ? nullptr : state.mSurface->getNativeBuffer()->handle;
     hwcLayer.handle = handle;
 
+    hwcLayer.flags = 0;
     hwcLayer.hints = 0;
-    hwcLayer.flags = skipLayer ? HWC_SKIP_LAYER : 0;
     hwcLayer.blending = isOpaque ? HWC_BLENDING_NONE : HWC_BLENDING_PREMULT;
 #if ANDROID_VERSION >= 17
     hwcLayer.compositionType = HWC_FRAMEBUFFER;
 
     hwcLayer.acquireFenceFd = -1;
     hwcLayer.releaseFenceFd = -1;
 #if ANDROID_VERSION >= 18
     hwcLayer.planeAlpha = opacity;