Merge fx-team to m-c a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Tue, 05 Jan 2016 16:34:06 -0800
changeset 315566 9d6ffc7a08b6b47056eefe1e652710a3849adbf7
parent 315539 dd6d447fc6e06da33a6b4a0ff44e6ce31ebc47dd (current diff)
parent 315565 3c7d6b80345e197eea1d13bc32ebf3439bbc89fb (diff)
child 315567 ec25e284ca6fd536484fbb7252fbb1791b71d95f
child 315574 d68306ac604b0820b0ed22bc43115e0218f9e21e
child 315622 1462ab5b067e33763744981e3503fe4ea0859897
child 315708 42b90df5e0e790b353b3c0dcd14bad80412e02de
push id1079
push userjlund@mozilla.com
push dateFri, 15 Apr 2016 21:02:33 +0000
treeherdermozilla-release@575fbf6786d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone46.0a1
first release with
nightly linux32
9d6ffc7a08b6 / 46.0a1 / 20160106030225 / files
nightly linux64
9d6ffc7a08b6 / 46.0a1 / 20160106030225 / files
nightly mac
9d6ffc7a08b6 / 46.0a1 / 20160106030225 / files
nightly win32
9d6ffc7a08b6 / 46.0a1 / 20160106030225 / files
nightly win64
9d6ffc7a08b6 / 46.0a1 / 20160106030225 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c a=merge CLOSED TREE
browser/components/nsBrowserGlue.js
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
--- a/b2g/components/AlertsService.js
+++ b/b2g/components/AlertsService.js
@@ -61,33 +61,46 @@ AlertsService.prototype = {
       case "xpcom-shutdown":
         Services.obs.removeObserver(this, "xpcom-shutdown");
         cpmm.removeMessageListener(kMessageAppNotificationReturn, this);
         break;
     }
   },
 
   // nsIAlertsService
+  showAlert: function(aAlert, aAlertListener) {
+    if (!aAlert) {
+      return;
+    }
+    cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
+      imageURL: aAlert.imageURL,
+      title: aAlert.title,
+      text: aAlert.text,
+      clickable: aAlert.textClickable,
+      cookie: aAlert.cookie,
+      listener: aAlertListener,
+      id: aAlert.name,
+      dir: aAlert.dir,
+      lang: aAlert.lang,
+      dataStr: aAlert.data,
+      inPrivateBrowsing: aAlert.inPrivateBrowsing
+    });
+  },
+
   showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable,
                                   aCookie, aAlertListener, aName, aBidi,
                                   aLang, aDataStr, aPrincipal,
                                   aInPrivateBrowsing) {
-    cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
-      imageURL: aImageUrl,
-      title: aTitle,
-      text: aText,
-      clickable: aTextClickable,
-      cookie: aCookie,
-      listener: aAlertListener,
-      id: aName,
-      dir: aBidi,
-      lang: aLang,
-      dataStr: aDataStr,
-      inPrivateBrowsing: aInPrivateBrowsing
-    });
+    let alert = Cc["@mozilla.org/alert-notification;1"].
+      createInstance(Ci.nsIAlertNotification);
+
+    alert.init(aName, aImageUrl, aTitle, aText, aTextClickable, aCookie,
+               aBidi, aLang, aDataStr, aPrincipal, aInPrivateBrowsing);
+
+    this.showAlert(alert, aAlertListener);
   },
 
   closeAlert: function(aName) {
     cpmm.sendAsyncMessage(kMessageAlertNotificationClose, {
       name: aName
     });
   },
 
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -734,18 +734,16 @@ HistoryMenu.prototype = {
     if (!menuitem)
       return;
 
     if (!PlacesUIUtils.shouldShowTabsFromOtherComputersMenuitem()) {
       menuitem.setAttribute("hidden", true);
       return;
     }
 
-    let enabled = PlacesUIUtils.shouldEnableTabsFromOtherComputersMenuitem();
-    menuitem.setAttribute("disabled", !enabled);
     menuitem.setAttribute("hidden", false);
   },
 
   _onPopupShowing: function HM__onPopupShowing(aEvent) {
     PlacesMenu.prototype._onPopupShowing.apply(this, arguments);
 
     // Don't handle events for submenus.
     if (aEvent.target != aEvent.currentTarget)
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -798,16 +798,25 @@ BrowserGlue.prototype = {
   },
 
   // runs on startup, before the first command line handler is invoked
   // (i.e. before the first window is opened)
   _finalUIStartup: function BG__finalUIStartup() {
     this._sanitizer.onStartup();
     // check if we're in safe mode
     if (Services.appinfo.inSafeMode) {
+      // See https://bugzilla.mozilla.org/show_bug.cgi?id=1231112#c7 . We need to
+      // register the observer early if we have to migrate tab groups
+      let currentUIVersion = 0;
+      try {
+        currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
+      } catch(ex) {}
+      if (currentUIVersion < 35) {
+        this._maybeMigrateTabGroups();
+      }
       Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
                              "_blank", "chrome,centerscreen,modal,resizable=no", null);
     }
 
     // apply distribution customizations
     // prefs are applied in _onAppDefaults()
     this._distributionCustomizer.applyCustomizations();
 
@@ -2236,17 +2245,18 @@ BrowserGlue.prototype = {
       xulStore.removeValue(BROWSER_DOCURL, "bookmarks-menu-button", "class");
       xulStore.removeValue(BROWSER_DOCURL, "home-button", "class");
     }
 
     if (currentUIVersion < 32) {
       this._notifyNotificationsUpgrade().catch(Cu.reportError);
     }
 
-    if (currentUIVersion < 35) {
+    // Only do this outside of safe mode, because in safe mode we do this earlier.
+    if (currentUIVersion < 35 && !Services.appinfo.inSafeMode) {
       this._maybeMigrateTabGroups();
     }
 
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   _hasExistingNotificationPermission: function BG__hasExistingNotificationPermission() {
--- a/browser/extensions/loop/bootstrap.js
+++ b/browser/extensions/loop/bootstrap.js
@@ -551,26 +551,28 @@ var WindowListener = {
               bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
                 this._getString("infobar_screenshare_browser_message2");
               bar.classList.toggle("paused", paused);
               buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
                 this._getString("infobar_button_pause_label");
               buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
                 this._getString("infobar_button_pause_accesskey");
               return true;
-            }
+            },
+            type: "pause"
           },
           {
             label: this._getString("infobar_button_stop_label"),
             accessKey: this._getString("infobar_button_stop_accesskey"),
             isDefault: true,
             callback: () => {
               this._hideBrowserSharingInfoBar();
               LoopUI.MozLoopService.hangupAllChatWindows();
-            }
+            },
+            type: "stop"
           }]
         );
 
         // Keep showing the notification bar until the user explicitly closes it.
         bar.persistence = -1;
       },
 
       /**
--- a/browser/extensions/loop/skin/osx/platform.css
+++ b/browser/extensions/loop/skin/osx/platform.css
@@ -16,16 +16,21 @@
     background: #ebebeb;
   }
 
   notification[value="loop-sharing-notification"] .notification-button {
     background: #fff;
     border-radius: 0;
   }
 
+  /* Hide Pause/Resume button until the functionality is complete */
+  notification[value="loop-sharing-notification"] .notification-button[type="pause"] {
+    display:none;
+  }
+
   notification[value="loop-sharing-notification"].paused .notification-button {
     background: #57bd35;
   }
 
   notification[value="loop-sharing-notification"].paused .notification-button:hover {
     background: #39a017;
   }
 
--- a/browser/extensions/loop/skin/shared/loop.css
+++ b/browser/extensions/loop/skin/shared/loop.css
@@ -187,16 +187,21 @@
     width: 100px;
     height: 40px;
     margin: 0;
     list-style-image: url(chrome://loop/content/shared/img/pause-12x12.svg);
     box-shadow: 0 -1px 1px rgba(0,0,0,.5) inset;
     text-shadow: none;
   }
 
+  /* Hide Pause/Resume button until the functionality is complete */
+  notification[value="loop-sharing-notification"] .notification-button[type="pause"] {
+    display:none;
+  }
+
   notification[value="loop-sharing-notification"].paused .notification-button {
     background-color: #57bd35;
     color: #fff;
     list-style-image: url(chrome://loop/content/shared/img/play-12x12.svg);
   }
 
   notification[value="loop-sharing-notification"].paused .notification-button:hover {
     background-color: #39a017;
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1435,16 +1435,20 @@ richlistitem[type~="action"][actiontype$
   padding: 0;
 }
 
 /* Content area */
 #sidebar {
   background-color: Window;
 }
 
+#sidebar-header > .close-icon:not(:hover):-moz-lwtheme-brighttext {
+  background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
+}
+
 .browserContainer > findbar {
   background-color: -moz-dialog;
   color: -moz-DialogText;
   text-shadow: none;
 }
 
 /* Tabstrip */
 
--- a/browser/themes/linux/devedition.css
+++ b/browser/themes/linux/devedition.css
@@ -1,14 +1,16 @@
 % This Source Code Form is subject to the terms of the Mozilla Public
 % License, v. 2.0. If a copy of the MPL was not distributed with this
 % file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 %include ../shared/devedition.inc.css
 
+:root[devtoolstheme="dark"] .findbar-closebutton:not(:hover),
+:root[devtoolstheme="dark"] #sidebar-header > .close-icon:not(:hover),
 .tab-close-button[visuallyselected]:not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
 }
 
 /* The menubar and tabs toolbar should match the devedition theme */
 #TabsToolbar,
 #toolbar-menubar {
   -moz-appearance: none !important;
@@ -71,13 +73,14 @@
   border-color: transparent !important;
 }
 
 /* Prevent double border below tabs toolbar */
 #TabsToolbar:not([collapsed="true"]) + #nav-bar {
   border-top-width: 0 !important;
 }
 
-/* Prevent devedition foreground color from seeping into the sidebar-box (since
- * its background colors aren't affected by the devedition theme) */
-#sidebar-box {
-  color: initial;
+/* Fix the bad-looking text-shadow in the sidebar header: */
+.sidebar-header,
+#sidebar-header {
+  text-shadow: none;
 }
+
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -47,18 +47,18 @@
 
   .menu-accel,
   .menu-iconic-accel {
     color: graytext;
   }
 
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7) {
-    .sidebar-header,
-    #sidebar-header {
+    .sidebar-header:not(:-moz-lwtheme),
+    #sidebar-header:not(:-moz-lwtheme) {
       background-color: #EEF3FA;
     }
 
     .sidebar-splitter,
     #appcontent ~ .sidebar-splitter,
     .chatbar-button,
     chatbar > chatbox {
       border-color: #A9B7C9;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1867,16 +1867,28 @@ richlistitem[type~="action"][actiontype$
 
 #sidebar-header > .close-icon {
   -moz-appearance: none;
   padding: 2px;
   margin: 0;
   border: none;
 }
 
+@media not all and (min-resolution: 1.1dppx) {
+  #sidebar-header > .close-icon:-moz-lwtheme-brighttext {
+    list-style-image: url("chrome://global/skin/icons/close-inverted.png");
+  }
+}
+
+@media (min-resolution: 1.1dppx) {
+  #sidebar-header > .close-icon:-moz-lwtheme-brighttext {
+    list-style-image: url("chrome://global/skin/icons/close-inverted@2x.png");
+  }
+}
+
 @media (-moz-os-version: windows-xp),
        (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7) {
   #sidebar-header > .close-icon {
     padding-top: 4px;
     padding-bottom: 4px;
   }
 }
--- a/browser/themes/windows/devedition.css
+++ b/browser/themes/windows/devedition.css
@@ -102,24 +102,26 @@
   background-color: var(--tab-background-color);
 }
 
 #toolbar-menubar {
   text-shadow: none !important;
 }
 
 :root[devtoolstheme="dark"] .findbar-closebutton,
+:root[devtoolstheme="dark"] #sidebar-header > .close-icon,
 /* Tab styling - make sure to use an inverted icon for the selected tab
    (brighttext only covers the unselected tabs) */
 .tab-close-button[visuallyselected=true] {
   list-style-image: url("chrome://global/skin/icons/close-inverted.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   :root[devtoolstheme="dark"] .findbar-closebutton,
+  :root[devtoolstheme="dark"] #sidebar-header > .close-icon,
   .tab-close-button[visuallyselected=true] {
     list-style-image: url("chrome://global/skin/icons/close-inverted@2x.png");
   }
 }
 
 @media (-moz-os-version: windows-xp),
        (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7),
@@ -249,16 +251,24 @@
 
 #navigator-toolbox {
   /* The side borders on the toolbox also look out-of-place because we don't paint over
    * the native background color at all, and these are !important for the same reason as above. */
   border-left: none !important;
   border-right: none !important;
 }
 
+/* The sidebar header has no background now that the background of the #browser-panel
+ * has no image and is transparent. Fix: */
+.sidebar-header:-moz-lwtheme,
+#sidebar-header {
+  background-color: var(--chrome-background-color);
+  color: var(--chrome-color);
+}
+
 @media (-moz-os-version: windows-vista),
        (-moz-os-version: windows-win7),
        (-moz-os-version: windows-win8) {
   /* And then we add them back on toolbars so that they don't look borderless: */
   #main-window:not([customizing])[sizemode=normal] #navigator-toolbox::after,
   #main-window:not([customizing])[sizemode=normal] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
     border-left: 1px solid hsla(209,67%,12%,0.35);
     border-right: 1px solid hsla(209,67%,12%,0.35);
new file mode 100644
--- /dev/null
+++ b/devtools/bootstrap.js
@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+
+// Helper to listen to a key on all windows
+function MultiWindowKeyListener({ keyCode, ctrlKey, altKey, callback }) {
+  let keyListener = function (event) {
+    if (event.ctrlKey == !!ctrlKey &&
+        event.altKey == !!altKey &&
+        event.keyCode === keyCode) {
+      callback(event);
+
+      // Call preventDefault to avoid duplicated events when
+      // doing the key stroke within a tab.
+      event.preventDefault();
+    }
+  };
+
+  let observer = function (window, topic, data) {
+    // Listen on keyup to call keyListener only once per stroke
+    if (topic === "domwindowopened") {
+      window.addEventListener("keyup", keyListener);
+    } else {
+      window.removeEventListener("keyup", keyListener);
+    }
+  };
+
+  return {
+    start: function () {
+      // Automatically process already opened windows
+      let e = Services.ww.getWindowEnumerator();
+      while (e.hasMoreElements()) {
+        let window = e.getNext();
+        observer(window, "domwindowopened", null);
+      }
+      // And listen for new ones to come
+      Services.ww.registerNotification(observer);
+    },
+
+    stop: function () {
+      Services.ww.unregisterNotification(observer);
+      let e = Services.ww.getWindowEnumerator();
+      while (e.hasMoreElements()) {
+        let window = e.getNext();
+        observer(window, "domwindowclosed", null);
+      }
+    }
+  };
+};
+
+let getTopLevelWindow = function (window) {
+  return window.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIWebNavigation)
+               .QueryInterface(Ci.nsIDocShellTreeItem)
+               .rootTreeItem
+               .QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIDOMWindow);
+};
+
+function reload(event) {
+  // We automatically reload the toolbox if we are on a browser tab
+  // with a toolbox already opened
+  let top = getTopLevelWindow(event.view)
+  let isBrowser = top.location.href.includes("/browser.xul") && top.gDevToolsBrowser;
+  let reloadToolbox = false;
+  if (isBrowser && top.gDevToolsBrowser.hasToolboxOpened) {
+    reloadToolbox = top.gDevToolsBrowser.hasToolboxOpened(top);
+  }
+  dump("Reload DevTools.  (reload-toolbox:"+reloadToolbox+")\n");
+
+  // Invalidate xul cache in order to see changes made to chrome:// files
+  Services.obs.notifyObservers(null, "startupcache-invalidate", null);
+
+  // Ask the loader to update itself and reopen the toolbox if needed
+  const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  devtools.reload(reloadToolbox);
+}
+
+let listener;
+function startup() {
+  dump("DevTools addon started.\n");
+  listener = new MultiWindowKeyListener({
+    keyCode: Ci.nsIDOMKeyEvent.DOM_VK_R, ctrlKey: true, altKey: true,
+    callback: reload
+  });
+  listener.start();
+}
+function shutdown() {
+  listener.stop();
+  listener = null;
+}
+function install() {}
+function uninstall() {}
new file mode 100644
--- /dev/null
+++ b/devtools/chrome.manifest
@@ -0,0 +1,6 @@
+content devtools client/
+skin devtools classic/1.0 client/themes/
+resource devtools .
+
+content webide client/webide/content/
+skin webide classic/1.0 client/webide/themes/
--- a/devtools/client/debugger/debugger-controller.js
+++ b/devtools/client/debugger/debugger-controller.js
@@ -13,17 +13,17 @@ const NEW_SOURCE_DISPLAY_DELAY = 200; //
 const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
 const FRAME_STEP_CLEAR_DELAY = 100; // ms
 const CALL_STACK_PAGE_SIZE = 25; // frames
 
 // The panel's window global is an EventEmitter firing the following events:
 const EVENTS = {
   // When the debugger's source editor instance finishes loading or unloading.
   EDITOR_LOADED: "Debugger:EditorLoaded",
-  EDITOR_UNLOADED: "Debugger:EditorUnoaded",
+  EDITOR_UNLOADED: "Debugger:EditorUnloaded",
 
   // When new sources are received from the debugger server.
   NEW_SOURCE: "Debugger:NewSource",
   SOURCES_ADDED: "Debugger:SourcesAdded",
 
   // When a source is shown in the source editor.
   SOURCE_SHOWN: "Debugger:EditorSourceShown",
   SOURCE_ERROR_SHOWN: "Debugger:EditorSourceErrorShown",
--- a/devtools/client/framework/gDevTools.jsm
+++ b/devtools/client/framework/gDevTools.jsm
@@ -1156,30 +1156,34 @@ var gDevToolsBrowser = {
       cmd: cmd,
       key: key,
       bc: bc,
       appmenuitem: appmenuitem,
       menuitem: menuitem
     };
   },
 
+  hasToolboxOpened: function(win) {
+    let tab = win.gBrowser.selectedTab;
+    for (let [target, toolbox] of gDevTools._toolboxes) {
+      if (target.tab == tab) {
+        return true;
+      }
+    }
+    return false;
+  },
+
   /**
    * Update the "Toggle Tools" checkbox in the developer tools menu. This is
    * called when a toolbox is created or destroyed.
    */
   _updateMenuCheckbox: function DT_updateMenuCheckbox() {
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
 
-      let hasToolbox = false;
-      if (TargetFactory.isKnownTab(win.gBrowser.selectedTab)) {
-        let target = TargetFactory.forTab(win.gBrowser.selectedTab);
-        if (gDevTools._toolboxes.has(target)) {
-          hasToolbox = true;
-        }
-      }
+      let hasToolbox = gDevToolsBrowser.hasToolboxOpened(win);
 
       let broadcaster = win.document.getElementById("devtoolsMenuBroadcaster_DevToolbox");
       if (hasToolbox) {
         broadcaster.setAttribute("checked", "true");
       } else {
         broadcaster.removeAttribute("checked");
       }
     }
--- a/devtools/client/jsonview/components/reps/tree-view.js
+++ b/devtools/client/jsonview/components/reps/tree-view.js
@@ -46,19 +46,18 @@ var TreeView = React.createClass({
           searchFilter: this.state.searchFilter || this.props.searchFilter
         }));
       }
     } else {
       children.push(React.addons.createFragment(root));
     }
 
     return (
-      DOM.div({className: "domTable", cellPadding: 0, cellSpacing: 0,
-        onClick: this.onClick},
-          children
+      DOM.div({className: "domTable", cellPadding: 0, cellSpacing: 0},
+        children
       )
     );
   },
 
   // Data
 
   componentDidMount: function() {
     var members = initMembers(this.props.data, 0);
@@ -146,18 +145,19 @@ var TreeNode = React.createFactory(React
       }
 
       if (member.valueString && member.valueString.indexOf(filter) < 0) {
         classNames.push("hidden");
       }
     }
 
     return (
-      DOM.div({className: classNames.join(" "), onClick: this.onClick},
-        DOM.span({className: "memberLabelCell"},
+      DOM.div({className: classNames.join(" ")},
+        DOM.span({className: "memberLabelCell", onClick: this.onClick},
+          DOM.span({className: "memberIcon"}),
           DOM.span({className: "memberLabel " + member.type + "Label"},
             member.name)
         ),
         DOM.span({className: "memberValueCell"},
           DOM.span({},
             Rep({
               object: member.value,
               mode: this.props.mode,
--- a/devtools/client/jsonview/converter-sniffer.js
+++ b/devtools/client/jsonview/converter-sniffer.js
@@ -17,17 +17,16 @@ loader.lazyRequireGetter(this, "NetworkH
                                "devtools/shared/webconsole/network-helper");
 
 // Constants
 const JSON_TYPE = "application/json";
 const CONTRACT_ID = "@mozilla.org/devtools/jsonview-sniffer;1";
 const CLASS_ID = "{4148c488-dca1-49fc-a621-2a0097a62422}";
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 const JSON_VIEW_TYPE = "JSON View";
-const JSON_EXTENSION = "json";
 const CONTENT_SNIFFER_CATEGORY = "net-content-sniffers";
 
 /**
  * This component represents a sniffer (implements nsIContentSniffer
  * interface) responsible for changing top level 'application/json'
  * document types to: 'application/vnd.mozilla.json.view'.
  *
  * This internal type is consequently rendered by JSON View component
@@ -59,20 +58,16 @@ var Sniffer = Class({
         // Channel doesn't support content dispositions
       }
 
       // Check the response content type and if it's application/json
       // change it to new internal type consumed by JSON View.
       if (aRequest.contentType == JSON_TYPE) {
         return JSON_VIEW_MIME_TYPE;
       }
-
-      if (NetworkHelper.getFileExtension(aRequest.name) == JSON_EXTENSION) {
-        return JSON_VIEW_MIME_TYPE;
-      }
     }
 
     return "";
   }
 });
 
 var service = xpcom.Service({
   id: components.ID(CLASS_ID),
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..569c266e4e4c79a60c4b023728b1ee3e648fe551
GIT binary patch
literal 1630
zc$}S7eM}Q)7%wfh$OH*EzlWFe3zh5LmFvef6sQ!WXaP$XS&VMi_AT7tdbM}ZmibZU
z2av@tP={`V32X`m5uMANF2;-j8i^xg&Me9%$d)PDfDJY_rn@@omc{tT-sIl*ect!?
zd!FZgKCUt+YfVJ>{BV&-6k*ITn1xj%d<(;*!c1J;nj|dq__RDemv!(S$_+$%J8K1y
zk)iT|8BliLma8C9B$9BnC6CWDt<&0AMotAVaxdc&1x8}B*G1V10S{S0KJC=OKV7^G
zL$qB7Z%~>rlPeVz&>5v}kXxE%v6U9uNIRUI1SNX4f&vEc6y#-!oSfFHgCFbF3j4q@
z3PX<}e4!5h+o?QL4wTBe0i={G5gVq!AvGz-l>~|7@sI+;6HpvO6(oY=T0*VGRnXLc
z1#51*Lu)pqPuUVA9bCZkE-i|BJRZ3xLC(7KQJf@66jPuI1tK62&gbMQFXH4D2Q?S~
zXLHjoo@Sj;K&Gs0F|UK+;1d{^$@E0AlbZ^Z5Hi$Dxlmk=p$vlt(Hw6Ej%TAei_Zm6
zGvL@_w@t{0WAVRCJ)0T;3f^e5-L#Mts>s0FiW%VKjRqYo+{o>;T?-rpu24A$#ERi6
zgixtSgwo(j1h-nPDvZLF4wXHq=V^RQW6+QWjfPxpP~v!60*MovR0C!p^m;v>MkWNg
zMkmKpP8$ev>3_KDXL7ZvZb0#@+rqL%!79ipV0o4+U|mpZDimj;Y_u~VuL$I5%2nW|
zOMpGy%`(vAAZzKTH~<Jcg{jqcM5R!Y2%)e#5E26zLa1%Z1eL?40crqG@$LUNc&OkM
z`k%@P9tq_b*gok4L3q+hz$vtkTj&$tFa8mcNL*$#=q=u_t&@))C4cIY#zXZXVHrK4
zx!I?aqN)v^s+RrJ-WY7xR>9krXpX?!)_t%pb`H9^x3#sEogBWqQ}m9xF)2zd#&0Jb
z^9_H0qizU65anyX{#d+y?Ec}oFC=zGwkuPHX0+E^#=dRJJ#jrVGqZI2qlew$F>m3v
z^p1hf{U9bfdiKetraSd#^RN3ql2yEPuwCKLn_V$SG{Q8O4*xbXaAeKCg>6R(+8kT4
zV`3ECIlOAao>R@ueNx{6X&ac{u1a}ZHuyliMbm404$EAhsV^xR?_K+*{D+p!pJ*)`
zH~w*da^mNhqct@Tzr1-<b?(fW$>U|R6XLemsB-bhi%?c*|BjT~q3v<w>z7LPwFL~*
z^4;LzpEFh>i;}j+Wj8cVmq6JT$w+<yy<&eY(X_cMBO~L|$y28!yCF$L^)iJwWG^l)
zGFCSmXGx0~W@cwchrGn&`Nr7LSQ!~98L>irm9+%jug}}w$Q2f?yz>5hvRj@xcCcp%
zUb|?(#TF&tGdTZUeR_KOddTr!!-B@+-SHoP_U^Q(1z+@3_oWQ2UX|USSzphOHxez0
zm95A8zR~zI(C>TC9kSX(&-YyjyTyqMItpRk1zGRBD-CjS#Y9-m`R6O@qI0{VVyb6O
zuL&8wC0p5XFGB3_CywtqWVXz@AgPS><D4vGQ|ADAX@#hKdD?K-%c8N*hrN+Yr$r51
zd9bT$PGok~04Y5=P#2xEv%5OGZ7I79PV|(|-EiDj6@6n9WJb;|uj{{dF}`<r;o0S7
z6Jv|xZuH#Zx@xyZbaTm-NZRGH@JO?+{NB~Cmb^L<ifH860a@+757XA@G`m7l{2`*)
X(U8H4rfao<KfW<7%W!)2E3f|r2vt^M
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fb062516db8e49e90b5081f63bd7881ebfc5df65
GIT binary patch
literal 2045
zc$}S8eNYo;8czxY<f~`_iB`KTMa7bj4f!CEoFuGKC=rCvraeKk$u5Z`*^n#{5K$2e
zu@yWmP_QB>ihKxzg$hN=v~qIw;J`zz%1A|pVwI6Ph*BAQ8w<A6>G;R(%<lU<@BV(j
z_u1!T))u>Qy@%@(R|<vV5xD`5BUcl7hq}<nXW;$pC~~0_>y$)1ju9CuGfI(Ya0CS+
z4XR``4pnI~Q@=yQC=>^aPN5`}@@SD7H!xLp4AW|`OHn9cYpo`gIt3*F1Wnc%#o)t^
zE)dXZ#NhipIa_XuK()FJS!OgoYokJ)m7*4Ez_ot^!mJ`vz<?4ez-rJNEh4KJe4#5M
zZTmJ01YSUh6fyW)P)d0$5P_RffXC!9)NBp}2!u?C#}z_QFu-9$AuNc^;s_ZKB;pE0
z5FePkK=L%R1{1}>vbnQJPYh}a!X#p`GBPrl86iyEoXmoRLLrOIVR1MN62Y)!8VQw^
zVYCFyE5N8lZPu9x9c~2dz6!z92r&rGe}cgzm%kJ?TILc(ri^7(nOG2$%`zBR^JohZ
zhhncrTNIfllof|s@HDfUtOpkGhofFiwFAj$L@{O^SrnBX#?@&C)JR0aVvu~p)aW!K
z6yri1KE`DrY>3a`^7%rBDiq=|AOu19Y!$@A_?mg0zv8n);ZPwQ$`wjs9t5om5klP1
z2$&6XB@zj=P8c%JjWk*al~Ik(b9KLSLte=hMVL_)ftwXLuAlFMSS?QA7A<Z9A|ilQ
za+O+VwEIE!8qI}@nsw=@MrOthz>6e{bbk>50ttiyHNs)=*&G3bi*R@h6~>1c5FZJ_
zgeVe<pnPzSulawI$0CDb{i$>2x5#$1n=j{pbY4ypY9z<UOwP$-*nNmXaRee^iNbp0
z!C|r1TjJf5T;yh3kA%y;<KNz<(t9KR8<0TcAKv@+I$o0P-$Hf!MhYFNRlrUG;J&vC
z_Dfw(ZC&UyxFE7B_i2I_K6cRW(Mau&JKHisgFCJd4Yl7aOI<$uMf91n_Vyoo4?n?@
zk8Ec4G~7$kJy%CQ-ltshENteZz)QoIqxmJ%oCKfdv!%Wlm!Za{9})^XI=|+e{k|C9
z9&|s?-Bd*l5Y(mReI7JwY<kzceMjV%$;p*t81=(lwxJc;rFZ=<j#G$ln|A8@)|~ac
z5p|?~ZzG*n^kl;CF%A*es_EC<C%k22+2?PyN|W=RUn#pQU0Ssx*LlVzF)xSy8ty^R
z>nb0;s}+2jK;P=o;=FA^ThsENJ{sFJvxe|2fA7tU;1bOQ^$AZRa4h1uPV8EtNOCBZ
zI4`&zHO`wl?`#$%(r;QyfI5A4Y*Jvs{VM<Q`kozbRevW?qCY+N6yYse-RA6BGkB+B
zb?)Yv(W3)<+eI}8ex?fF>FrJ`+Flprl5&jmWL3xb&f=H=tnz;BM!#kTGp6|{w)*Va
z7TLK$jr>l<?Sg@>^wM`mQ*QcH^>@{F^`~8Q*b?5KXI|MS%RknK{UZK#kx_cS+-b$0
z0Yg>xLF{<MC9Hm+>-fNVWOV}L^>YrVbA4SJr`$%jJ9RjVuW-M5u$v$5;aC)r9W#^k
zO_pxiW%J4l|1LJB@4x+QNsaTN<&9GZCNsXKRlFWwwJ)=BsMuG3Z8Vo?l^o3p+f|r<
zI{FG+(eYngI^#pwau>hRkk5b1^+IW0r}sHOYAkW$<iwe_#M+0<j|(NA3>AA0jDL=e
z(KAle1Z^!_)w<})QQ1_;7ZZxht`8g%`}$I7>D{yR+s3B;>gfmWTrKTF=WzGUq*B|2
za9Z0rj3u6O()PX9Vy}44roual@K)MR=i=aB_5b+{9yZKUH8ijFUd=gduUoz)YpK(D
zqk@wCmMZ#JjkIo=1J$?iVQcfOj&l6_@OXbtqJPPvZe>CF>D@VAWuNdCK5qCYs_Wi%
zva;uC*atSRvT9yxKuZ|1Y&LTwar&XY_EWC(YIMV)*!41HeZpaz$HA-j%FTCobAU(c
zu}#UIMF)C((rO%%LRMD)?2_5A*_N#+xU<l|WKE~dH8mLQ77i}`U=8#pvVvNWI(pI&
zeB)XE38{Op9HsU~nf9ps3&T?l!M~Zlt<47xI^rAR9F@V{Z6n_)d%g`1&U$}U;DU-?
z3h06?n!ND#Rk|Z|xg;qHvP!(t@)tWoQ#tx5i8aSdzWYb#=IXwwmSdA}L6P#g)4ziD
z+_HJie9SCv=pMX&uX@BM^;DEBuO18wT4;>AR92c7NM)VzahN<bGCBN{Ytdox;=1<h
o5pv&3p4&jV)6=dSTc|Y3XSq=odrtgv%l<=-T(=Q!ly1%a9{{={@&Et;
--- a/devtools/client/jsonview/css/dom-tree.css
+++ b/devtools/client/jsonview/css/dom-tree.css
@@ -15,28 +15,26 @@
 }
 
 .hidden {
   display: none;
 }
 
 .memberLabelCell {
   padding: 2px 0 2px 0px;
-  vertical-align: top;
 }
 
 .memberValueCell {
   padding: 2px 0 2px 5px;
   overflow: hidden;
 }
 
 .memberLabel {
   cursor: default;
   overflow: hidden;
-  padding-left: 18px;
   white-space: nowrap;
 }
 
 .memberLabelPrefix {
   color: gray;
   margin-right: 3px;
   font-weight: normal;
 }
@@ -55,27 +53,36 @@
 .memberValueIcon.readOnly {
   background: url("read-only-prop.svg") no-repeat;
   background-position: 4px 4px;
   background-size: 10px 10px;
 }
 
 /******************************************************************************/
 
+.memberRow.hasChildren > .memberLabelCell > .memberIcon:hover,
+.memberRow.cropped > .memberLabelCell > .memberIcon:hover {
+  cursor: pointer;
+}
+
 .memberRow.hasChildren > .memberLabelCell > .memberLabel:hover,
 .memberRow.cropped > .memberLabelCell > .memberLabel:hover {
   cursor: pointer;
   color: blue;
   text-decoration: underline;
 }
 
 .memberRow:hover {
   background-color: #EFEFEF;
 }
 
+.memberRow {
+  padding: 3px 0 3px 0;
+}
+
 .panelNode-dom .memberRow td,
 .panelNode-domSide .memberRow td {
   border-bottom: 1px solid #EFEFEF;
 }
 
 /******************************************************************************/
 
 .userLabel,
@@ -111,42 +118,99 @@
 .ordinalLabel {
   color: SlateBlue;
   font-weight: bold;
 }
 
 /******************************************************************************/
 /* Twisties */
 
-.memberRow.hasChildren > .memberLabelCell > .memberLabel,
-.memberRow.cropped > .memberLabelCell > .memberLabel {
-  background-image: url(twisty-closed.svg);
+.memberRow > .memberLabelCell > .memberIcon {
+  height: 14px;
+  width: 14px;
+  display: inline-block;
+  line-height: 15px;
+  vertical-align: bottom;
+  padding-right: 2px;
+  margin-left: 3px;
+}
+
+.memberRow.hasChildren > .memberLabelCell > .memberIcon,
+.memberRow.cropped > .memberLabelCell > .memberIcon {
+  background-image: url("./twisty-closed.svg");
   background-repeat: no-repeat;
-  background-position: 2px calc(0.5em - 3px);
-  min-height: 12px;
 }
 
-.memberRow.hasChildren.opened > .memberLabelCell > .memberLabel,
-.memberRow.cropped.opened > .memberLabelCell > .memberLabel {
-  background-image: url(twisty-open.svg);
+.memberRow.hasChildren.opened > .memberLabelCell > .memberIcon,
+.memberRow.cropped.opened > .memberLabelCell > .memberIcon {
+  background-image: url("./twisty-open.svg");
   background-repeat: no-repeat;
-  min-height: 12px;
+}
+
+@media (min-resolution: 1.1dppx) {
+.memberRow.hasChildren > .memberLabelCell > .memberIcon,
+.memberRow.cropped > .memberLabelCell > .memberIcon {
+  background-image: url("./controls@2x.png");
+}
+
+.memberRow.hasChildren.opened > .memberLabelCell > .memberIcon,
+.memberRow.cropped.opened > .memberLabelCell > .memberIcon {
+  background-image: url("./controls@2x.png");
+}
 }
 
 /******************************************************************************/
 /* Layout support */
 
 .memberChildren {
   padding-left: 16px;
 }
 
 .memberLabelCell,
 .memberValueCell {
-  display: table-cell;
 }
 
 .memberLabelCell {
   min-width: 30px;
 }
 
 .memberRow:hover {
   background-color: transparent !important;
 }
+
+/******************************************************************************/
+/* Themes */
+
+.theme-light .memberRow.hasChildren > .memberLabelCell > .memberIcon,
+.theme-light .memberRow.cropped > .memberLabelCell > .memberIcon {
+  background-image: url("./controls.png");
+  background-size: 56px 28px;
+  background-repeat: no-repeat;
+  background-position: 0 -14px;
+}
+
+.theme-light .memberRow.hasChildren.opened > .memberLabelCell > .memberIcon,
+.theme-light .memberRow.cropped.opened > .memberLabelCell > .memberIcon {
+  background-image: url("./controls.png");
+  background-size: 56px 28px;
+  background-repeat: no-repeat;
+  background-position: -14px -14px;
+}
+
+.theme-dark .memberRow.hasChildren > .memberLabelCell > .memberIcon,
+.theme-dark .memberRow.cropped > .memberLabelCell > .memberIcon {
+  background-image: url("./controls.png");
+  background-size: 56px 28px;
+  background-repeat: no-repeat;
+  background-position: -28px -14px;
+}
+
+.theme-dark .memberRow.hasChildren.opened > .memberLabelCell > .memberIcon,
+.theme-dark .memberRow.cropped.opened > .memberLabelCell > .memberIcon {
+  background-image: url("./controls.png");
+  background-size: 56px 28px;
+  background-repeat: no-repeat;
+  background-position: -42px -14px;
+}
+
+.theme-dark .memberRow:hover {
+  background-color: var(--theme-selection-background-semitransparent);
+}
--- a/devtools/client/jsonview/css/moz.build
+++ b/devtools/client/jsonview/css/moz.build
@@ -1,16 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 DevToolsModules(
+    'controls.png',
+    'controls@2x.png',
     'dom-tree.css',
     'general.css',
     'headers-panel.css',
     'json-panel.css',
     'main.css',
     'read-only-prop.svg',
     'reps.css',
     'search-box.css',
--- a/devtools/client/jsonview/css/reps.css
+++ b/devtools/client/jsonview/css/reps.css
@@ -170,17 +170,17 @@
   color:  #444444;
 }
 
 /******************************************************************************/
 /* Light Theme & Dark Theme */
 
 .theme-dark .domLabel,
 .theme-light .domLabel {
-  color: var(--theme-highlight-bluegrey);
+  color: var(--theme-highlight-blue);
 }
 
 .theme-dark .objectBox-array .length,
 .theme-light .objectBox-array .length,
 .theme-dark .objectBox-number,
 .theme-light .objectBox-number {
   color: var(--theme-highlight-green);
 }
@@ -202,18 +202,18 @@
 .theme-light .objectBox-array {
   color: var(--theme-body-color);
 }
 
 .theme-dark .objectBox-object,
 .theme-light .objectBox-object {
   font-family: Lucida Grande, sans-serif;
   font-weight: normal;
-  color: var(--theme-highlight-bluegrey);
+  color: var(--theme-highlight-blue);
   white-space: pre-wrap;
 }
 
 .theme-dark .caption,
 .theme-light .caption {
   font-family: Lucida Grande, Tahoma, sans-serif;
   font-weight: normal;
-  color: var(--theme-highlight-bluegrey);
+  color: var(--theme-highlight-blue);
 }
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -15,20 +15,20 @@ Services.prefs.setBoolPref("devtools.deb
 
 // Enable the memory tool for all tests.
 var gMemoryToolEnabled = Services.prefs.getBoolPref("devtools.memory.enabled");
 Services.prefs.setBoolPref("devtools.memory.enabled", true);
 
 var { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
 var { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-var { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
-var { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
+var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
 DevToolsUtils.testing = true;
 var { require: browserRequire } = BrowserLoader("resource://devtools/client/shared/", this);
 
 var EXAMPLE_URL = "http://example.com/browser/browser/devtools/shared/test/";
 
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -163,17 +163,17 @@ body {
 .variable-or-property .token-domnode {
   font-weight: bold;
 }
 
 .theme-toolbar,
 .devtools-toolbar,
 .devtools-sidebar-tabs tabs,
 .devtools-sidebar-alltabs,
-.CodeMirror-dialog { /* General toolbar styling */
+.cm-s-mozilla .CodeMirror-dialog { /* General toolbar styling */
   color: var(--theme-body-color-alt);
   background-color: var(--theme-toolbar-background);
   border-color: hsla(210,8%,5%,.6);
 }
 
 .theme-fg-contrast { /* To be used for text on theme-bg-contrast */
   color: black;
 }
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -166,17 +166,17 @@ body {
 .theme-fg-contrast { /* To be used for text on theme-bg-contrast */
   color: black;
 }
 
 .theme-toolbar,
 .devtools-toolbar,
 .devtools-sidebar-tabs tabs,
 .devtools-sidebar-alltabs,
-.CodeMirror-dialog { /* General toolbar styling */
+.cm-s-mozilla .CodeMirror-dialog { /* General toolbar styling */
   color: var(--theme-body-color-alt);
   background-color: var(--theme-toolbar-background);
   border-color: var(--theme-splitter-color);
 }
 
 .ruleview-swatch,
 .computedview-colorswatch {
   box-shadow: 0 0 0 1px #c4c4c4;
new file mode 100644
--- /dev/null
+++ b/devtools/install.rdf
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!--
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+  <Description about="urn:mozilla:install-manifest"
+               em:id="devtools@mozilla.org"
+               em:name="Developer Tools (local version)"
+               em:description="Add-on to load DevTools from local sources and easily reload them with Ctrl+Alt+r shortcut"
+               em:version="44.0a1"
+               em:type="2"
+               em:creator="Mozilla">
+
+    <em:bootstrap>true</em:bootstrap>
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>44.0a1</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
--- a/devtools/shared/webconsole/network-helper.js
+++ b/devtools/shared/webconsole/network-helper.js
@@ -798,44 +798,14 @@ var NetworkHelper = {
   nsIURL: function(aUrl, aStore = gNSURLStore) {
     if (aStore.has(aUrl)) {
       return aStore.get(aUrl);
     }
 
     let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
     aStore.set(aUrl, uri);
     return uri;
-  },
-
-  /**
-   * Returns extension for file URLs (e.g. 'json').
-   * Not everyURL has an extension and this method works as follows:
-   * 1) Remove query string
-   * 2) Get part after the last slash (a file name)
-   * 3) Look for the last dot (an extension)
-   */
-  getFileExtension: function(aUrl) {
-    if (!aUrl) {
-      return;
-    }
-
-    // Remove query string from the URL if any.
-    let queryString = aUrl.indexOf("?");
-    if (queryString != -1) {
-      aUrl = aUrl.substr(0, queryString);
-    }
-
-    // Look for the part after last slash
-    var lastSlash = aUrl.lastIndexOf("/");
-    var fileName = aUrl.substr(lastSlash + 1);
-    if (!fileName) {
-      return;
-    }
-
-    // Now get the file extension.
-    var lastDot = fileName.lastIndexOf(".");
-    return fileName.substr(lastDot + 1);
   }
 };
 
 for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
   exports[prop] = NetworkHelper[prop];
 }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4410,35 +4410,36 @@ ContentParent::HasNotificationPermission
     return false;
   }
 #endif /* MOZ_CHILD_PERMISSIONS */
 
   return true;
 }
 
 bool
-ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
-                                         const nsString& aText, const bool& aTextClickable,
-                                         const nsString& aCookie, const nsString& aName,
-                                         const nsString& aBidi, const nsString& aLang,
-                                         const nsString& aData,
-                                         const IPC::Principal& aPrincipal,
-                                         const bool& aInPrivateBrowsing)
-{
-  if (!HasNotificationPermission(aPrincipal)) {
+ContentParent::RecvShowAlert(const AlertNotificationType& aAlert)
+{
+  nsCOMPtr<nsIAlertNotification> alert(dont_AddRef(aAlert));
+  if (NS_WARN_IF(!alert)) {
     return true;
   }
 
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = alert->GetPrincipal(getter_AddRefs(principal));
+  if (NS_WARN_IF(NS_FAILED(rv)) ||
+      !HasNotificationPermission(IPC::Principal(principal))) {
+
+      return true;
+  }
+
   nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
   if (sysAlerts) {
-    sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable,
-                                     aCookie, this, aName, aBidi, aLang,
-                                     aData, aPrincipal, aInPrivateBrowsing);
-    }
-    return true;
+      sysAlerts->ShowAlert(alert, this);
+  }
+  return true;
 }
 
 bool
 ContentParent::RecvCloseAlert(const nsString& aName,
                               const IPC::Principal& aPrincipal)
 {
   if (!HasNotificationPermission(aPrincipal)) {
     return true;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -896,24 +896,17 @@ private:
                             const OptionalURIParams& referrer,
                             const uint32_t& flags) override;
 
   virtual bool RecvSetURITitle(const URIParams& uri,
                                const nsString& title) override;
 
   bool HasNotificationPermission(const IPC::Principal& aPrincipal);
 
-  virtual bool
-  RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
-                            const nsString& aText, const bool& aTextClickable,
-                            const nsString& aCookie, const nsString& aName,
-                            const nsString& aBidi, const nsString& aLang,
-                            const nsString& aData,
-                            const IPC::Principal& aPrincipal,
-                            const bool& aInPrivateBrowsing) override;
+  virtual bool RecvShowAlert(const AlertNotificationType& aAlert) override;
 
   virtual bool RecvCloseAlert(const nsString& aName,
                               const IPC::Principal& aPrincipal) override;
 
   virtual bool RecvDisableNotifications(const IPC::Principal& aPrincipal) override;
 
   virtual bool RecvOpenNotificationSettings(const IPC::Principal& aPrincipal) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -69,16 +69,17 @@ include GraphicsMessages;
 include ProfilerTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
+using AlertNotificationType from "mozilla/AlertNotificationIPCSerializer.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
 using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ChildPrivileges from "base/process_util.h";
 using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
@@ -876,27 +877,17 @@ parent:
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (StructuredCloneData[] retval);
 
     prio(high) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
                                CpowEntry[] aCpows, Principal aPrincipal)
       returns (StructuredCloneData[] retval);
 
-    ShowAlertNotification(nsString imageUrl,
-                          nsString title,
-                          nsString text,
-                          bool textClickable,
-                          nsString cookie,
-                          nsString name,
-                          nsString bidi,
-                          nsString lang,
-                          nsString data,
-                          Principal principal,
-                          bool inPrivateBrowsing);
+    ShowAlert(AlertNotificationType alert);
 
     CloseAlert(nsString name, Principal principal);
 
     DisableNotifications(Principal principal);
 
     OpenNotificationSettings(Principal principal);
 
     PPSMContentDownloader(uint32_t aCertType);
--- a/dom/notification/DesktopNotification.cpp
+++ b/dom/notification/DesktopNotification.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/dom/DesktopNotification.h"
 #include "mozilla/dom/DesktopNotificationBinding.h"
 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "nsComponentManagerUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "nsIDOMDesktopNotification.h"
 #include "mozilla/Preferences.h"
 #include "nsGlobalWindow.h"
 #include "nsIAppsService.h"
 #include "nsIScriptSecurityManager.h"
@@ -109,26 +110,30 @@ DesktopNotification::PostDesktopNotifica
   // In the case of IPC, the parent process will use the cookie to map
   // to nsIObservers, thus cookies must be unique to differentiate observers.
   nsString uniqueName = NS_LITERAL_STRING("desktop-notification:");
   uniqueName.AppendInt(sCount++);
   nsCOMPtr<nsIDocument> doc = GetOwner()->GetDoc();
   nsIPrincipal* principal = doc->NodePrincipal();
   nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
   bool inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
-  return alerts->ShowAlertNotification(mIconURL, mTitle, mDescription,
-                                       true,
-                                       uniqueName,
-                                       mObserver,
-                                       uniqueName,
-                                       NS_LITERAL_STRING("auto"),
-                                       EmptyString(),
-                                       EmptyString(),
-                                       principal,
-                                       inPrivateBrowsing);
+  nsCOMPtr<nsIAlertNotification> alert =
+    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+  NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+  nsresult rv = alert->Init(uniqueName, mIconURL, mTitle,
+                            mDescription,
+                            true,
+                            uniqueName,
+                            NS_LITERAL_STRING("auto"),
+                            EmptyString(),
+                            EmptyString(),
+                            principal,
+                            inPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return alerts->ShowAlert(alert, mObserver);
 }
 
 DesktopNotification::DesktopNotification(const nsAString & title,
                                          const nsAString & description,
                                          const nsAString & iconURL,
                                          nsPIDOMWindow *aWindow,
                                          nsIPrincipal* principal)
   : DOMEventTargetHelper(aWindow)
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/NotificationEvent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 
 #include "nsAlertsUtils.h"
+#include "nsComponentManagerUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
 #include "nsCRTGlue.h"
 #include "nsDOMJSUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIAlertsService.h"
 #include "nsIAppsService.h"
 #include "nsIContentPermissionPrompt.h"
@@ -1782,21 +1783,29 @@ Notification::ShowInternal()
   // In the case of IPC, the parent process uses the cookie to map to
   // nsIObserver. Thus the cookie must be unique to differentiate observers.
   nsString uniqueCookie = NS_LITERAL_STRING("notification:");
   uniqueCookie.AppendInt(sCount++);
   bool inPrivateBrowsing = IsInPrivateBrowsing();
 
   nsAutoString alertName;
   GetAlertName(alertName);
-  alertService->ShowAlertNotification(iconUrl, mTitle, mBody, true,
-                                      uniqueCookie, alertObserver, alertName,
-                                      DirectionToString(mDir), mLang,
-                                      mDataAsBase64, GetPrincipal(),
-                                      inPrivateBrowsing);
+  nsCOMPtr<nsIAlertNotification> alert =
+    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(alert);
+  rv = alert->Init(alertName, iconUrl, mTitle, mBody,
+                   true,
+                   uniqueCookie,
+                   DirectionToString(mDir),
+                   mLang,
+                   mDataAsBase64,
+                   GetPrincipal(),
+                   inPrivateBrowsing);
+  NS_ENSURE_SUCCESS_VOID(rv);
+  alertService->ShowAlert(alert, alertObserver);
 }
 
 /* static */ bool
 Notification::RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */)
 {
   // requestPermission() is not allowed on workers. The calling page should ask
   // for permission on the worker's behalf. This is to prevent 'which window
   // should show the browser pop-up'. See discussion:
--- a/dom/tests/browser/browser_test_toolbars_visibility.js
+++ b/dom/tests/browser/browser_test_toolbars_visibility.js
@@ -93,25 +93,25 @@ function testNonDefaultContentToolbars(t
  * on the chrome context.
  *
  * @param toolbars
  *        the visibility state of the toolbar elements
  */
 function testNonDefaultChromeToolbars(toolbars) {
   // None of the toolbars should be visible if hidden with chrome privileges
   ok(!toolbars.locationbar,
-     "locationbar should be visible on default window.open()");
+     "locationbar should not be visible with location=no");
   ok(!toolbars.menubar,
-     "menubar be visible on default window.open()");
+     "menubar should not be visible with menubar=no");
   ok(!toolbars.personalbar,
-     "personalbar should be visible on default window.open()");
+     "personalbar should not be visible with personalbar=no");
   ok(!toolbars.statusbar,
-     "statusbar should be visible on default window.open()");
+     "statusbar should not be visible with status=no");
   ok(!toolbars.toolbar,
-     "toolbar should be visible on default window.open()");
+     "toolbar should not be visible with toolbar=no");
 }
 
 /**
  * Ensure that toolbars of a window opened in the content context have the
  * correct visibility.
  *
  * A window opened with default parameters should have all toolbars visible.
  *
@@ -142,17 +142,52 @@ add_task(function*() {
 
     let popupBrowser = popupWindow.gBrowser.selectedBrowser;
     yield BrowserTestUtils.browserLoaded(popupBrowser);
 
     // Test toolbars visibility
     let popupToolbars = yield getToolbarsFromBrowserContent(popupBrowser);
     testNonDefaultContentToolbars(popupToolbars);
 
-    // Cleanup
+    // Ensure that chrome toolbars agree with content
+    let chromeToolbars = getToolbarsFromWindowChrome(popupWindow);
+    testNonDefaultContentToolbars(chromeToolbars);
+
+    // Close the new window
+    yield BrowserTestUtils.closeWindow(popupWindow);
+  });
+});
+
+/**
+ * Ensure that toolbars of a window opened to about:blank in the content context
+ * have the correct visibility.
+ *
+ * A window opened with "location=no, personalbar=no, toolbar=no, scrollbars=no,
+ * menubar=no, status=no", should only have location visible.
+ */
+add_task(function*() {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: CONTENT_PAGE,
+  }, function*(browser) {
+    // Open a blank window with toolbars hidden
+    let winPromise = BrowserTestUtils.waitForNewWindow();
+    yield BrowserTestUtils.synthesizeMouseAtCenter("#winOpenNoURLNonDefault", {}, browser);
+    let popupWindow = yield winPromise;
+
+    // No need to wait for this window to load, since it's loading about:blank
+    let popupBrowser = popupWindow.gBrowser.selectedBrowser;
+    let popupToolbars = yield getToolbarsFromBrowserContent(popupBrowser);
+    testNonDefaultContentToolbars(popupToolbars);
+
+    // Ensure that chrome toolbars agree with content
+    let chromeToolbars = getToolbarsFromWindowChrome(popupWindow);
+    testNonDefaultContentToolbars(chromeToolbars);
+
+    // Close the new window
     yield BrowserTestUtils.closeWindow(popupWindow);
   });
 });
 
 /**
  * Ensure that toolbars of a window opened in the chrome context have the
  * correct visibility.
  *
--- a/dom/tests/browser/test_new_window_from_content_child.html
+++ b/dom/tests/browser/test_new_window_from_content_child.html
@@ -2,18 +2,24 @@
 <head>
   <meta charset="utf-8">
   <title>Test popup window opening behaviour</title>
 </head>
 <body>
   <p><a id="winOpenDefault" href="#" onclick="return openWindow();">Open a new window via window.open with default features.</a></p>
   <p><a id="winOpenNonDefault" href="#" onclick="return openWindow('resizable=no, location=no, personalbar=no, toolbar=no, scrollbars=no, menubar=no, status=no, directories=no, height=100, width=500');">Open a new window via window.open with non-default features.</a></p>
   <p><a id="winOpenDialog" href="#" onclick="return openWindow('dialog=yes');">Open a new window via window.open with dialog=1.</a></p>
+  <p><a id="winOpenNoURLNonDefault" href="#" onclick="return openBlankWindow('location=no, toolbar=no, height=100, width=100');">Open a blank new window via window.open with non-default features.</a></p>
   <p><a id="targetBlank" href="about:robots" target="_blank">Open a new window via target="_blank".</a></p>
 </body>
 </html>
 
 <script>
 function openWindow(aFeatures="") {
   window.open("about:robots", "_blank", aFeatures);
   return false;
 }
+
+function openBlankWindow(aFeatures="") {
+  window.open("", "_blank", aFeatures);
+  return false;
+}
 </script>
--- a/dom/tests/mochitest/notification/MockServices.js
+++ b/dom/tests/mochitest/notification/MockServices.js
@@ -24,36 +24,44 @@ var MockServices = (function () {
         delete activeAlertNotifications[alertName];
         delete activeAppNotifications[alertName];
         return;
       }
     }
   });
 
   var mockAlertsService = {
-    showAlertNotification: function(imageUrl, title, text, textClickable,
-                                    cookie, alertListener, name) {
+    showAlert: function(alert, alertListener) {
       var listener = SpecialPowers.wrap(alertListener);
-      activeAlertNotifications[name] = {
+      activeAlertNotifications[alert.name] = {
         listener: listener,
-        cookie: cookie,
-        title: title
+        cookie: alert.cookie,
+        title: alert.title
       };
 
       // fake async alert show event
       if (listener) {
         setTimeout(function () {
-          listener.observe(null, "alertshow", cookie);
+          listener.observe(null, "alertshow", alert.cookie);
         }, 100);
         setTimeout(function () {
-          listener.observe(null, "alertclickcallback", cookie);
+          listener.observe(null, "alertclickcallback", alert.cookie);
         }, 100);
       }
     },
 
+    showAlertNotification: function(imageUrl, title, text, textClickable,
+                                    cookie, alertListener, name) {
+      this.showAlert({
+        name: name,
+        cookie: cookie,
+        title: title
+      }, alertListener);
+    },
+
     showAppNotification: function(aImageUrl, aTitle, aText, aAlertListener, aDetails) {
       var listener = aAlertListener || (activeAlertNotifications[aDetails.id] ? activeAlertNotifications[aDetails.id].listener : undefined);
       activeAppNotifications[aDetails.id] = {
         observer: listener,
         title: aTitle,
         text: aText,
         manifestURL: aDetails.manifestURL,
         imageURL: aImageUrl,
--- a/dom/tests/mochitest/notification/desktop-notification/notification_common.js
+++ b/dom/tests/mochitest/notification/desktop-notification/notification_common.js
@@ -3,27 +3,33 @@ const ALERTS_SERVICE_CONTRACT_ID = "@moz
 
 const MOCK_SYSTEM_ALERTS_CID = SpecialPowers.wrap(SpecialPowers.Components).ID("{e86d888c-e41b-4b78-9104-2f2742a532de}");
 const SYSTEM_ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/system-alerts-service;1";
 
 var registrar = SpecialPowers.wrap(SpecialPowers.Components).manager.
   QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar);
 
 var mockAlertsService = {
+  showAlert: function(alert, alertListener) {
+    // probably should do this async....
+    SpecialPowers.wrap(alertListener).observe(null, "alertshow", alert.cookie);
+
+    if (SpecialPowers.getBoolPref("notification.prompt.testing.click_on_notification") == true) {
+       SpecialPowers.wrap(alertListener).observe(null, "alertclickcallback", alert.cookie);
+    }
+
+    SpecialPowers.wrap(alertListener).observe(null, "alertfinished", alert.cookie);
+  },
+
   showAlertNotification: function(imageUrl, title, text, textClickable,
                                   cookie, alertListener, name, bidi,
                                   lang, data) {
-    // probably should do this async....
-    SpecialPowers.wrap(alertListener).observe(null, "alertshow", cookie);
-
-    if (SpecialPowers.getBoolPref("notification.prompt.testing.click_on_notification") == true) {
-       SpecialPowers.wrap(alertListener).observe(null, "alertclickcallback", cookie);
-    }
-
-    SpecialPowers.wrap(alertListener).observe(null, "alertfinished", cookie);
+    return this.showAlert({
+      cookie: cookie
+    }, alertListener);
   },
 
   showAppNotification: function(imageUrl, title, text, alertListener, details) {
     this.showAlertNotification(imageUrl, title, text, details.textClickable, "",
                                alertListener, details.name, details.dir,
                                details.lang, details.data);
   },
 
--- a/dom/tests/mochitest/notification/desktop-notification/test_notification_tag.html
+++ b/dom/tests/mochitest/notification/desktop-notification/test_notification_tag.html
@@ -18,23 +18,27 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 </pre>
 <script type="text/javascript">
   const MOCK_CID = SpecialPowers.wrap(SpecialPowers.Components).ID("{dbe37e64-d9a3-402c-8d8a-0826c619f7ad}");
   const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
 
   var mockAlertsService = {
+    showAlert: function(alert, alertListener) {
+      notificationsCreated.push(alert.name);
+      if (notificationsCreated.length == 3) {
+        checkNotifications();
+      }
+    },
+
     showAlertNotification: function(imageUrl, title, text, textClickable,
                                     cookie, alertListener, name, dir,
                                     lang, data) {
-      notificationsCreated.push(name);
-      if (notificationsCreated.length == 3) {
-        checkNotifications();
-      }
+      this.showAlert({ name: name });
     },
 
     QueryInterface: function(aIID) {
       if (SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
           SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService)) {
         return this;
       }
       throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
--- a/dom/tests/mochitest/notification/desktop-notification/test_system_principal.xul
+++ b/dom/tests/mochitest/notification/desktop-notification/test_system_principal.xul
@@ -18,21 +18,25 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug 874090 **/
   const MOCK_CID = Components.ID("{2a0f83c4-8818-4914-a184-f1172b4eaaa7}");
   const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
 
   var mockAlertsService = {
+    showAlert: function(alert, alertListener) {
+      ok(true, "System principal was granted permission and is able to call showAlert.");
+      unregisterMock();
+      SimpleTest.finish();
+    },
+
     showAlertNotification: function(imageUrl, title, text, textClickable,
                                     cookie, alertListener, name, dir, lang, data) {
-      ok(true, "System principal was granted permission and is able to call showAlertNotification.");
-      unregisterMock();
-      SimpleTest.finish();
+      this.showAlert();
     },
 
     QueryInterface: function(aIID) {
       if (aIID.equals(Components.interfaces.nsISupports) ||
           aIID.equals(Components.interfaces.nsIAlertsService)) {
         return this;
       }
       throw Components.results.NS_ERROR_NO_INTERFACE;
--- a/dom/tests/mochitest/webapps/head.js
+++ b/dom/tests/mochitest/webapps/head.js
@@ -112,16 +112,19 @@ var AlertsService = {
       "", ALERTS_SERVICE_CONTRACT_ID, this);
   },
 
   restore: function() {
     Components.manager.nsIComponentRegistrar.registerFactory(ALERTS_SERVICE_CID,
       "", ALERTS_SERVICE_CONTRACT_ID, null);
   },
 
+  showAlert: function() {
+  },
+
   showAlertNotification: function() {
   },
 };
 
 AlertsService.init();
 
 SimpleTest.registerCleanupFunction(() => {
   AlertsService.restore();
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -2165,23 +2165,31 @@ public class BrowserApp extends GeckoApp
         if (hideFirstrunPager()) {
             Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.ACTIONBAR, "firstrun-pane");
         }
 
         String url = "";
 
         final Tab tab = Tabs.getInstance().getSelectedTab();
         if (tab != null) {
-            final String userRequested = tab.getUserRequested();
+            final String userSearchTerm = tab.getUserRequested();
 
             // Check to see if there's a user-entered search term,
             // which we save whenever the user performs a search.
-            url = (TextUtils.isEmpty(userRequested) ? tab.getURL() : userRequested);
+            final String telemetryMsg;
+            if (!TextUtils.isEmpty(userSearchTerm)) {
+                url = userSearchTerm;
+                telemetryMsg = "urlbar-userentered";
+            } else {
+                url = tab.getURL();
+                telemetryMsg = url.isEmpty() ? "urlbar-empty" : "urlbar-url";
+            }
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.ACTIONBAR, telemetryMsg);
         }
-
         enterEditingMode(url);
     }
 
     /**
      * Enters editing mode with the specified URL. If a null
      * url is given, the empty String will be used instead.
      */
     private void enterEditingMode(String url) {
@@ -2205,17 +2213,22 @@ public class BrowserApp extends GeckoApp
 
         final PropertyAnimator animator = new PropertyAnimator(250);
         animator.setUseHardwareLayer(false);
 
         TransitionsTracker.track(animator);
 
         mBrowserToolbar.startEditing(url, animator);
 
-        showHomePagerWithAnimator(panelId, animator);
+        final boolean isUserSearchTerm = !TextUtils.isEmpty(selectedTab.getUserRequested());
+        if (isUserSearchTerm && AppConstants.NIGHTLY_BUILD) {
+            showBrowserSearchAfterAnimation(animator);
+        } else {
+            showHomePagerWithAnimator(panelId, animator);
+        }
 
         animator.start();
         Telemetry.startUISession(TelemetryContract.Session.AWESOMESCREEN);
     }
 
     private void commitEditingMode() {
         if (!mBrowserToolbar.isEditing()) {
             return;
@@ -2617,16 +2630,34 @@ public class BrowserApp extends GeckoApp
         }
 
         mBrowserToolbar.setNextFocusDownId(R.id.layer_view);
 
         // Refresh toolbar height to possibly restore the toolbar padding
         refreshToolbarHeight();
     }
 
+    private void showBrowserSearchAfterAnimation(PropertyAnimator animator) {
+        if (animator == null) {
+            showBrowserSearch();
+            return;
+        }
+
+        animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
+            @Override
+            public void onPropertyAnimationStart() {
+            }
+
+            @Override
+            public void onPropertyAnimationEnd() {
+                showBrowserSearch();
+            }
+        });
+    }
+
     private void showBrowserSearch() {
         if (mBrowserSearch.getUserVisibleHint()) {
             return;
         }
 
         mBrowserSearchContainer.setVisibility(View.VISIBLE);
 
         // Prevent overdraw by hiding the underlying web content and HomePager View
@@ -2667,17 +2698,17 @@ public class BrowserApp extends GeckoApp
 
     private void hideBrowserSearch() {
         if (!mBrowserSearch.getUserVisibleHint()) {
             return;
         }
 
         // To prevent overdraw, the HomePager is hidden when BrowserSearch is displayed:
         // reverse that.
-        mHomePagerContainer.setVisibility(View.VISIBLE);
+        showHomePager(Tabs.getInstance().getSelectedTab().getMostRecentHomePanel());
 
         mBrowserSearchContainer.setVisibility(View.INVISIBLE);
 
         getSupportFragmentManager().beginTransaction()
                 .hide(mBrowserSearch).commitAllowingStateLoss();
         mBrowserSearch.setUserVisibleHint(false);
 
         getWindow().setBackgroundDrawable(null);
--- a/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadAction.java
+++ b/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadAction.java
@@ -82,16 +82,21 @@ public class DownloadAction extends Base
                 if (destinationFile.exists() && verify(destinationFile, content.getChecksum())) {
                     Log.d(LOGTAG, "Content already exists and is up-to-date.");
                     catalog.markAsDownloaded(content);
                     continue;
                 }
 
                 temporaryFile = createTemporaryFile(context, content);
 
+                if (!canWrite(temporaryFile, destinationFile)) {
+                    throw new RecoverableDownloadContentException(RecoverableDownloadContentException.DISK_IO,
+                                                                  "Temporary or destination file not writeable");
+                }
+
                 if (!hasEnoughDiskSpace(content, destinationFile, temporaryFile)) {
                     Log.d(LOGTAG, "Not enough disk space to save content. Skipping download.");
                     continue;
                 }
 
                 // TODO: Check space on disk before downloading content (bug 1220145)
                 final String url = createDownloadURL(content);
 
@@ -120,17 +125,17 @@ public class DownloadAction extends Base
                 if (callback != null) {
                     callback.onContentDownloaded(content);
                 }
 
                 if (temporaryFile != null && temporaryFile.exists()) {
                     temporaryFile.delete();
                 }
             } catch (RecoverableDownloadContentException e) {
-                Log.w(LOGTAG, "Downloading content failed (Recoverable): " + content , e);
+                Log.w(LOGTAG, "Downloading content failed (Recoverable): " + content, e);
 
                 if (e.shouldBeCountedAsFailure()) {
                     catalog.rememberFailure(content, e.getErrorType());
                 }
 
                 // TODO: Reschedule download (bug 1209498)
             } catch (UnrecoverableDownloadContentException e) {
                 Log.w(LOGTAG, "Downloading content failed (Unrecoverable): " + content, e);
@@ -331,9 +336,19 @@ public class DownloadAction extends Base
         final File destinationDirectory = destinationFile.getParentFile();
         // We need some more space to extract the file (getSize() returns the uncompressed size)
         if (destinationDirectory.getUsableSpace() < content.getSize() * 2) {
             return false;
         }
 
         return true;
     }
+
+    protected boolean canWrite(File... files) {
+        for (File file : files) {
+            if (!file.canWrite()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestDownloadAction.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestDownloadAction.java
@@ -163,16 +163,17 @@ public class TestDownloadAction {
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
 
         File file = mockNotExistingFile();
         doReturn(file).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
         doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
+        doReturn(true).when(action).canWrite(any(File.class), any(File.class));
         doReturn(false).when(action).verify(eq(file), anyString());
         doNothing().when(action).download(any(HttpClient.class), anyString(), eq(file));
         doReturn(true).when(action).verify(eq(file), anyString());
         doNothing().when(action).extract(eq(file), eq(file), anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
         verify(action).buildHttpClient();
@@ -197,16 +198,17 @@ public class TestDownloadAction {
                 .setSize(4223)
                 .build();
 
         DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
         doReturn(Collections.singletonList(content)).when(catalog).getScheduledDownloads();
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
+        doReturn(true).when(action).canWrite(any(File.class), any(File.class));
 
         File temporaryFile = mockFileWithSize(1337L);
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         doReturn(outputStream).when(action).openFile(eq(temporaryFile), anyBoolean());
 
         HttpClient client = mockHttpClient(HttpStatus.SC_PARTIAL_CONTENT, "HelloWorld");
@@ -296,16 +298,17 @@ public class TestDownloadAction {
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
 
         File temporaryFile = mockFileWithSize(1337L);
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         File destinationFile = mockNotExistingFile();
         doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
+        doReturn(true).when(action).canWrite(any(File.class), any(File.class));
         doReturn(true).when(action).verify(eq(temporaryFile), anyString());
         doNothing().when(action).extract(eq(temporaryFile), eq(destinationFile), anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
         verify(action, never()).download(any(HttpClient.class), anyString(), eq(temporaryFile));
         verify(action).verify(eq(temporaryFile), anyString());
         verify(action).extract(eq(temporaryFile), eq(destinationFile), anyString());
@@ -328,16 +331,17 @@ public class TestDownloadAction {
                 .setSize(1337L)
                 .build();
 
         DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
         doReturn(Collections.singletonList(content)).when(catalog).getScheduledDownloads();
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
+        doReturn(true).when(action).canWrite(any(File.class), any(File.class));
         doNothing().when(action).download(any(HttpClient.class), anyString(), any(File.class));
         doReturn(false).when(action).verify(any(File.class), anyString());
 
         File temporaryFile = mockNotExistingFile();
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         File destinationFile = mockNotExistingFile();
         doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
@@ -438,16 +442,17 @@ public class TestDownloadAction {
         DownloadContentCatalog catalog = mockCatalogWithScheduledDownloads(content);
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(true).when(action).isConnectedToNetwork(RuntimeEnvironment.application);
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
         doReturn(mockNotExistingFile()).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
         doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
         doReturn(true).when(action).hasEnoughDiskSpace(eq(content), any(File.class), any(File.class));
+        doReturn(true).when(action).canWrite(any(File.class), any(File.class));
 
         HttpClient client = mock(HttpClient.class);
         doThrow(IOException.class).when(client).execute(any(HttpUriRequest.class));
         doReturn(client).when(action).buildHttpClient();
 
         action.perform(RuntimeEnvironment.application, catalog);
 
         verify(catalog, never()).rememberFailure(eq(content), anyInt());
@@ -494,16 +499,42 @@ public class TestDownloadAction {
         }
 
         action.perform(RuntimeEnvironment.application, catalog);
 
         Assert.assertEquals(DownloadContent.STATE_FAILED, content.getState());
         verify(catalog, times(11)).rememberFailure(eq(content), anyInt());
     }
 
+    /**
+     * Scenario: Temporary or destination file is not writable.
+     *
+     * Verify that:
+     *  * No download is performed
+     *  * Error is counted as failure
+     */
+    @Test
+    public void testNoDownIsPerformedIfFilesAreNotWritable() throws Exception{
+        DownloadContent content = createFont();
+        DownloadContentCatalog catalog = mockCatalogWithScheduledDownloads(content);
+
+        DownloadAction action = spy(new DownloadAction(null));
+        doReturn(true).when(action).isConnectedToNetwork(RuntimeEnvironment.application);
+        doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
+        doReturn(mockNotExistingFile()).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
+        doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
+        doReturn(false).when(action).canWrite(any(File.class), any(File.class));
+
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        verify(action).canWrite(any(File.class), any(File.class));
+        verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
+        verify(catalog).rememberFailure(eq(content), anyInt());
+    }
+
     private DownloadContent createFont() {
         return createFontWithSize(102400L);
     }
 
     private DownloadContent createFontWithSize(long size) {
         return new DownloadContent.Builder()
                 .setKind(DownloadContent.KIND_FONT)
                 .setType(DownloadContent.TYPE_ASSET_ARCHIVE)
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
@@ -154,17 +154,17 @@ public class AppMenuComponent extends Ba
      *
      * When using legacy menus, make sure the menu has been opened to the appropriate level
      * (i.e. base menu or "More" menu) to ensure the appropriate menu views are in memory.
      * TODO: ^ Maybe we just need to have opened the "More" menu and the current one doesn't matter.
      *
      * This method is dependent on not having two views with equivalent contentDescription / text.
      */
     private View findAppMenuItemView(String text) {
-        mSolo.waitForText(text, 1, MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS);
+        mSolo.waitForText(String.format("^%s$", text), 1, MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS);
 
         final List<View> views = mSolo.getViews();
 
         final List<MenuItemActionBar> menuItemActionBarList = RobotiumUtils.filterViews(MenuItemActionBar.class, views);
         for (MenuItemActionBar menuItem : menuItemActionBarList) {
             if (TextUtils.equals(menuItem.getContentDescription(), text)) {
                 return menuItem;
             }
@@ -293,29 +293,30 @@ public class AppMenuComponent extends Ba
     private boolean isMenuOpen() {
         // We choose these options because New Tab is near the top of the menu and Page is near the middle/bottom.
         // Intermittently, the menu doesn't scroll to top so we can't just use the first item in the list.
         return isMenuOpen(MenuItem.NEW_TAB.getString(mSolo)) || isMenuOpen(MenuItem.PAGE.getString(mSolo));
     }
 
     private boolean isLegacyMoreMenuOpen() {
         // Check if the first menu option is visible.
-        return mSolo.searchText(mSolo.getString(R.string.share), true);
+        final String shareTitle = mSolo.getString(R.string.share);
+        return mSolo.searchText(String.format("^%s$", shareTitle), true);
     }
 
     /**
      * Determines whether the app menu is open by searching for the text in menuItemTitle.
      *
      * @param menuItemTitle, The contentDescription of menu item to search.
      *
      * @return true if app menu is open.
      */
     private boolean isMenuOpen(String menuItemTitle) {
         final View menuItemView = findAppMenuItemView(menuItemTitle);
-        return isMenuOpen(menuItemView) ? true : mSolo.searchText(menuItemTitle, true);
+        return isMenuOpen(menuItemView) ? true : mSolo.searchText(String.format("^%s$", menuItemTitle), true);
     }
 
     /**
      * If a ListMenuItemView with menuItemTitle is visible then the app menu is open .
      *
      * @param menuItemView, must be a ListMenuItemView with menuItemTitle.
      *                      You must use findAppMenuItemView(menuItemTitle) to obtain it.
      *
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/AlertNotification.cpp
@@ -0,0 +1,125 @@
+/* This Source Code Form is subject to the terms of the Mozilla Pub
+ * License, v. 2.0. If a copy of the MPL was not distributed with t
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/AlertNotification.h"
+
+namespace mozilla {
+
+NS_IMPL_CYCLE_COLLECTION(AlertNotification, mPrincipal)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AlertNotification)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAlertNotification)
+  NS_INTERFACE_MAP_ENTRY(nsIAlertNotification)
+NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AlertNotification)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AlertNotification)
+
+AlertNotification::AlertNotification()
+  : mTextClickable(false)
+  , mPrincipal(nullptr)
+  , mInPrivateBrowsing(false)
+{}
+
+AlertNotification::~AlertNotification()
+{}
+
+NS_IMETHODIMP
+AlertNotification::Init(const nsAString& aName, const nsAString& aImageURL,
+                        const nsAString& aTitle, const nsAString& aText,
+                        bool aTextClickable, const nsAString& aCookie,
+                        const nsAString& aDir, const nsAString& aLang,
+                        const nsAString& aData, nsIPrincipal* aPrincipal,
+                        bool aInPrivateBrowsing)
+{
+  mName = aName;
+  mImageURL = aImageURL;
+  mTitle = aTitle;
+  mText = aText;
+  mTextClickable = aTextClickable;
+  mCookie = aCookie;
+  mDir = aDir;
+  mLang = aLang;
+  mData = aData;
+  mPrincipal = aPrincipal;
+  mInPrivateBrowsing = aInPrivateBrowsing;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetName(nsAString& aName)
+{
+  aName = mName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetImageURL(nsAString& aImageURL)
+{
+  aImageURL = mImageURL;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetTitle(nsAString& aTitle)
+{
+  aTitle = mTitle;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetText(nsAString& aText)
+{
+  aText = mText;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetTextClickable(bool* aTextClickable)
+{
+  *aTextClickable = mTextClickable;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetCookie(nsAString& aCookie)
+{
+  aCookie = mCookie;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetDir(nsAString& aDir)
+{
+  aDir = mDir;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetLang(nsAString& aLang)
+{
+  aLang = mLang;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetData(nsAString& aData)
+{
+  aData = mData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  NS_IF_ADDREF(*aPrincipal = mPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetInPrivateBrowsing(bool* aInPrivateBrowsing)
+{
+  *aInPrivateBrowsing = mInPrivateBrowsing;
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/AlertNotification.h
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AlertNotification_h__
+#define mozilla_AlertNotification_h__
+
+#include "nsIAlertsService.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIPrincipal.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+class AlertNotification final : public nsIAlertNotification
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(AlertNotification,
+                                           nsIAlertNotification)
+  NS_DECL_NSIALERTNOTIFICATION
+  AlertNotification();
+
+protected:
+  virtual ~AlertNotification();
+
+private:
+  nsString mName;
+  nsString mImageURL;
+  nsString mTitle;
+  nsString mText;
+  bool mTextClickable;
+  nsString mCookie;
+  nsString mDir;
+  nsString mLang;
+  nsString mData;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  bool mInPrivateBrowsing;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_AlertNotification_h__ */
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/AlertNotificationIPCSerializer.h
@@ -0,0 +1,119 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AlertNotificationIPCSerializer_h__
+#define mozilla_AlertNotificationIPCSerializer_h__
+
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIAlertsService.h"
+#include "nsIPrincipal.h"
+#include "nsString.h"
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "mozilla/dom/PermissionMessageUtils.h"
+
+typedef nsIAlertNotification* AlertNotificationType;
+
+namespace IPC {
+
+template <>
+struct ParamTraits<AlertNotificationType>
+{
+  typedef AlertNotificationType paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    bool isNull = !aParam;
+    if (isNull) {
+      WriteParam(aMsg, isNull);
+      return;
+    }
+
+    nsString name, imageURL, title, text, cookie, dir, lang, data;
+    bool textClickable, inPrivateBrowsing;
+    nsCOMPtr<nsIPrincipal> principal;
+
+    if (NS_WARN_IF(NS_FAILED(aParam->GetName(name))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetImageURL(imageURL))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetTitle(title))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetText(text))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetTextClickable(&textClickable))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetCookie(cookie))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetDir(dir))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetLang(lang))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetData(data))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetPrincipal(getter_AddRefs(principal)))) ||
+        NS_WARN_IF(NS_FAILED(aParam->GetInPrivateBrowsing(&inPrivateBrowsing)))) {
+
+      // Write a `null` object if any getter returns an error. Otherwise, the
+      // receiver will try to deserialize an incomplete object and crash.
+      WriteParam(aMsg, /* isNull */ true);
+      return;
+    }
+
+    WriteParam(aMsg, isNull);
+    WriteParam(aMsg, name);
+    WriteParam(aMsg, imageURL);
+    WriteParam(aMsg, title);
+    WriteParam(aMsg, text);
+    WriteParam(aMsg, textClickable);
+    WriteParam(aMsg, cookie);
+    WriteParam(aMsg, dir);
+    WriteParam(aMsg, lang);
+    WriteParam(aMsg, data);
+    WriteParam(aMsg, IPC::Principal(principal));
+    WriteParam(aMsg, inPrivateBrowsing);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    bool isNull;
+    NS_ENSURE_TRUE(ReadParam(aMsg, aIter, &isNull), false);
+    if (isNull) {
+      *aResult = nullptr;
+      return true;
+    }
+
+    nsString name, imageURL, title, text, cookie, dir, lang, data;
+    bool textClickable, inPrivateBrowsing;
+    IPC::Principal principal;
+
+    if (!ReadParam(aMsg, aIter, &name) ||
+        !ReadParam(aMsg, aIter, &imageURL) ||
+        !ReadParam(aMsg, aIter, &title) ||
+        !ReadParam(aMsg, aIter, &text) ||
+        !ReadParam(aMsg, aIter, &textClickable) ||
+        !ReadParam(aMsg, aIter, &cookie) ||
+        !ReadParam(aMsg, aIter, &dir) ||
+        !ReadParam(aMsg, aIter, &lang) ||
+        !ReadParam(aMsg, aIter, &data) ||
+        !ReadParam(aMsg, aIter, &principal) ||
+        !ReadParam(aMsg, aIter, &inPrivateBrowsing)) {
+
+      return false;
+    }
+
+    nsCOMPtr<nsIAlertNotification> alert =
+      do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+    if (NS_WARN_IF(!alert)) {
+      *aResult = nullptr;
+      return true;
+    }
+    nsresult rv = alert->Init(name, imageURL, title, text, textClickable,
+                              cookie, dir, lang, data, principal,
+                              inPrivateBrowsing);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      *aResult = nullptr;
+      return true;
+    }
+    alert.forget(aResult);
+    return true;
+  }
+};
+
+} // namespace IPC
+
+#endif /* mozilla_AlertNotificationIPCSerializer_h__ */
--- a/toolkit/components/alerts/moz.build
+++ b/toolkit/components/alerts/moz.build
@@ -11,17 +11,23 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'alerts'
 
 EXPORTS += [
     'nsAlertsUtils.h',
 ]
 
+EXPORTS.mozilla += [
+    'AlertNotification.h',
+    'AlertNotificationIPCSerializer.h',
+]
+
 UNIFIED_SOURCES += [
+    'AlertNotification.cpp',
     'nsAlertsService.cpp',
     'nsAlertsUtils.cpp',
     'nsXULAlerts.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/toolkit/components/alerts/nsAlertsService.cpp
+++ b/toolkit/components/alerts/nsAlertsService.cpp
@@ -68,65 +68,107 @@ NS_IMETHODIMP nsAlertsService::ShowAlert
                                                      nsIObserver * aAlertListener,
                                                      const nsAString & aAlertName,
                                                      const nsAString & aBidi,
                                                      const nsAString & aLang,
                                                      const nsAString & aData,
                                                      nsIPrincipal * aPrincipal,
                                                      bool aInPrivateBrowsing)
 {
+  nsCOMPtr<nsIAlertNotification> alert =
+    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+  NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+  nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+                            aAlertText, aAlertTextClickable,
+                            aAlertCookie, aBidi, aLang, aData,
+                            aPrincipal, aInPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return ShowAlert(alert, aAlertListener);
+}
+
+
+NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification * aAlert,
+                                         nsIObserver * aAlertListener)
+{
+  NS_ENSURE_ARG(aAlert);
+
+  nsAutoString cookie;
+  nsresult rv = aAlert->GetCookie(cookie);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (XRE_IsContentProcess()) {
     ContentChild* cpc = ContentChild::GetSingleton();
 
     if (aAlertListener)
-      cpc->AddRemoteAlertObserver(PromiseFlatString(aAlertCookie), aAlertListener);
+      cpc->AddRemoteAlertObserver(cookie, aAlertListener);
 
-    cpc->SendShowAlertNotification(PromiseFlatString(aImageUrl),
-                                   PromiseFlatString(aAlertTitle),
-                                   PromiseFlatString(aAlertText),
-                                   aAlertTextClickable,
-                                   PromiseFlatString(aAlertCookie),
-                                   PromiseFlatString(aAlertName),
-                                   PromiseFlatString(aBidi),
-                                   PromiseFlatString(aLang),
-                                   PromiseFlatString(aData),
-                                   IPC::Principal(aPrincipal),
-                                   aInPrivateBrowsing);
+    cpc->SendShowAlert(aAlert);
     return NS_OK;
   }
 
+  nsAutoString imageUrl;
+  rv = aAlert->GetImageURL(imageUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString title;
+  rv = aAlert->GetTitle(title);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString text;
+  rv = aAlert->GetText(text);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString name;
+  rv = aAlert->GetName(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPrincipal> principal;
+  rv = aAlert->GetPrincipal(getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
 #ifdef MOZ_WIDGET_ANDROID
-  mozilla::AndroidBridge::Bridge()->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertCookie,
-                                                          aAlertListener, aAlertName, aPrincipal);
+  mozilla::AndroidBridge::Bridge()->ShowAlertNotification(imageUrl, title, text, cookie,
+                                                          aAlertListener, name, principal);
   return NS_OK;
 #else
   // Check if there is an optional service that handles system-level notifications
   nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID));
-  nsresult rv;
   if (sysAlerts) {
-    rv = sysAlerts->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
-                                          aAlertCookie, aAlertListener, aAlertName,
-                                          aBidi, aLang, aData,
-                                          IPC::Principal(aPrincipal),
-                                          aInPrivateBrowsing);
+    rv = sysAlerts->ShowAlert(aAlert, aAlertListener);
     if (NS_SUCCEEDED(rv))
       return NS_OK;
   }
 
   if (!ShouldShowAlert()) {
     // Do not display the alert. Instead call alertfinished and get out.
     if (aAlertListener)
-      aAlertListener->Observe(nullptr, "alertfinished", PromiseFlatString(aAlertCookie).get());
+      aAlertListener->Observe(nullptr, "alertfinished", cookie.get());
     return NS_OK;
   }
 
+  bool textClickable;
+  rv = aAlert->GetTextClickable(&textClickable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString bidi;
+  rv = aAlert->GetDir(bidi);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString lang;
+  rv = aAlert->GetLang(lang);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool inPrivateBrowsing;
+  rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Use XUL notifications as a fallback if above methods have failed.
-  rv = mXULAlerts.ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
-                                        aAlertCookie, aAlertListener, aAlertName,
-                                        aBidi, aLang, aPrincipal, aInPrivateBrowsing);
+  rv = mXULAlerts.ShowAlertNotification(imageUrl, title, text, textClickable,
+                                        cookie, aAlertListener, name,
+                                        bidi, lang, principal, inPrivateBrowsing);
   return rv;
 #endif // !MOZ_WIDGET_ANDROID
 }
 
 NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName,
                                           nsIPrincipal* aPrincipal)
 {
   if (XRE_IsContentProcess()) {
--- a/toolkit/components/alerts/nsIAlertsService.idl
+++ b/toolkit/components/alerts/nsIAlertsService.idl
@@ -3,19 +3,106 @@
  * 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 "nsISupports.idl"
 #include "nsIObserver.idl"
 
 interface nsIPrincipal;
 
-[scriptable, uuid(9d0284bf-db40-42da-8f0d-c2769dbde7aa)]
+%{C++
+#define ALERT_NOTIFICATION_CONTRACTID "@mozilla.org/alert-notification;1"
+%}
+
+[scriptable, uuid(b26b4a67-81b0-4270-8311-1e00a097ef92)]
+interface nsIAlertNotification : nsISupports
+{
+  /** Initializes an alert notification. */
+  void init([optional] in AString name,
+            [optional] in AString imageURL,
+            [optional] in AString title,
+            [optional] in AString text,
+            [optional] in boolean textClickable,
+            [optional] in AString cookie,
+            [optional] in AString dir,
+            [optional] in AString lang,
+            [optional] in AString data,
+            [optional] in nsIPrincipal principal,
+            [optional] in boolean inPrivateBrowsing);
+
+  /**
+   * The name of the notification. This is currently only used on Android and
+   * OS X. On Android, the name is hashed and used as a notification ID.
+   * Notifications will replace previous notifications with the same name.
+   */
+  readonly attribute AString name;
+
+  /**
+   * A URL identifying the image to put in the alert. The OS X backend limits
+   * the amount of time it will wait for the image to load to six seconds. After
+   * that time, the alert will show without an image.
+   */
+  readonly attribute AString imageURL;
+
+  /** The title for the alert. */
+  readonly attribute AString title;
+
+  /** The contents of the alert. */
+  readonly attribute AString text;
+
+  /**
+   * Controls the click behavior. If true, the alert listener will be notified
+   * when the user clicks on the alert.
+   */
+  readonly attribute boolean textClickable;
+
+  /**
+   * An opaque cookie that will be passed to the alert listener for each
+   * callback.
+   */
+  readonly attribute AString cookie;
+
+  /**
+   * Bidi override for the title and contents. Valid values are "auto", "ltr",
+   * or "rtl". Ignored if the backend doesn't support localization.
+   */
+  readonly attribute AString dir;
+
+  /**
+   * Language of the title and text. Ignored if the backend doesn't support
+   * localization.
+   */
+  readonly attribute AString lang;
+
+  /**
+   * A Base64-encoded structured clone buffer containing data associated with
+   * this alert. Only used for web notifications. Chrome callers should use a
+   * cookie instead.
+   */
+  readonly attribute AString data;
+
+  /**
+   * The principal of the page that created the alert. Used for IPC security
+   * checks, and to determine whether the alert should show the source string
+   * and action buttons.
+   */
+  readonly attribute nsIPrincipal principal;
+
+  /**
+   * Controls the image loading behavior. If true, the image URL will be loaded
+   * in private browsing mode.
+   */
+  readonly attribute boolean inPrivateBrowsing;
+};
+
+[scriptable, uuid(f7a36392-d98b-4141-a7d7-4e46642684e3)]
 interface nsIAlertsService : nsISupports
 {
+  void showAlert(in nsIAlertNotification alert,
+                 [optional] in nsIObserver alertListener);
   /**
    * Displays a sliding notification window.
    *
    * @param imageUrl       A URL identifying the image to put in the alert.
    *                       The OS X implemenation limits the amount of time it
    *                       will wait for an icon to load to six seconds. After
    *                       that time the alert will show with no icon.
    * @param title          The title for the alert.
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -92,16 +92,19 @@
 #define NS_ADDONCONTENTPOLICY_CONTRACTID \
   "@mozilla.org/addons/content-policy;1"
 
 #define NS_ADDONPATHSERVICE_CONTRACTID \
     "@mozilla.org/addon-path-service;1"
 
 /////////////////////////////////////////////////////////////////////////////
 
+#define ALERT_NOTIFICATION_CID \
+{ 0x9a7b7a41, 0x0b47, 0x47f7, { 0xb6, 0x1b, 0x15, 0xa2, 0x10, 0xd6, 0xf0, 0x20 } }
+
 // {A0CCAAF8-09DA-44D8-B250-9AC3E93C8117}
 #define NS_ALERTSSERVICE_CID \
 { 0xa0ccaaf8, 0x9da, 0x44d8, { 0xb2, 0x50, 0x9a, 0xc3, 0xe9, 0x3c, 0x81, 0x17 } }
 
 // {84E11F80-CA55-11DD-AD8B-0800200C9A66}
 #define NS_SYSTEMALERTSSERVICE_CID \
 { 0x84e11f80, 0xca55, 0x11dd, { 0xad, 0x8b, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
 
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -11,16 +11,17 @@
 #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
 #include "nsUpdateDriver.h"
 #endif
 
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 #include "nsParentalControlsService.h"
 #endif
 
+#include "mozilla/AlertNotification.h"
 #include "nsAlertsService.h"
 
 #include "nsDownloadManager.h"
 #include "DownloadPlatform.h"
 #include "nsDownloadProxy.h"
 #include "rdf.h"
 
 #include "nsTypeAheadFind.h"
@@ -77,16 +78,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsTermina
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindService)
 
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsParentalControlsService)
 #endif
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(AlertNotification)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAlertsService)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDownloadManager,
                                          nsDownloadManager::GetSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DownloadPlatform)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTypeAheadFind)
@@ -133,16 +135,17 @@ NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTU
 #if defined(MOZ_HAS_PERFSTATS)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID);
 #endif // defined (MOZ_HAS_PERFSTATS)
 
 #if defined(MOZ_HAS_TERMINATOR)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_TERMINATOR_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_USERINFO_CID);
+NS_DEFINE_NAMED_CID(ALERT_NOTIFICATION_CID);
 NS_DEFINE_NAMED_CID(NS_ALERTSSERVICE_CID);
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_DOWNLOADMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOWNLOADPLATFORM_CID);
 NS_DEFINE_NAMED_CID(NS_DOWNLOAD_CID);
 NS_DEFINE_NAMED_CID(NS_FIND_SERVICE_CID);
@@ -168,16 +171,17 @@ static const Module::CIDEntry kToolkitCI
   { &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor },
 #if defined(MOZ_HAS_TERMINATOR)
   { &kNS_TOOLKIT_TERMINATOR_CID, false, nullptr, nsTerminatorConstructor },
 #endif
 #if defined(MOZ_HAS_PERFSTATS)
   { &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID, false, nullptr, nsPerformanceStatsServiceConstructor },
 #endif // defined (MOZ_HAS_PERFSTATS)
   { &kNS_USERINFO_CID, false, nullptr, nsUserInfoConstructor },
+  { &kALERT_NOTIFICATION_CID, false, nullptr, AlertNotificationConstructor },
   { &kNS_ALERTSSERVICE_CID, false, nullptr, nsAlertsServiceConstructor },
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { &kNS_PARENTALCONTROLSSERVICE_CID, false, nullptr, nsParentalControlsServiceConstructor },
 #endif
   { &kNS_DOWNLOADMANAGER_CID, false, nullptr, nsDownloadManagerConstructor },
   { &kNS_DOWNLOADPLATFORM_CID, false, nullptr, DownloadPlatformConstructor },
   { &kNS_DOWNLOAD_CID, false, nullptr, nsDownloadProxyConstructor },
   { &kNS_FIND_SERVICE_CID, false, nullptr, nsFindServiceConstructor },
@@ -205,16 +209,17 @@ static const Module::ContractIDEntry kTo
   { NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
 #if defined(MOZ_HAS_TERMINATOR)
   { NS_TOOLKIT_TERMINATOR_CONTRACTID, &kNS_TOOLKIT_TERMINATOR_CID },
 #endif
 #if defined(MOZ_HAS_PERFSTATS)
   { NS_TOOLKIT_PERFORMANCESTATSSERVICE_CONTRACTID, &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID },
 #endif // defined (MOZ_HAS_PERFSTATS)
   { NS_USERINFO_CONTRACTID, &kNS_USERINFO_CID },
+  { ALERT_NOTIFICATION_CONTRACTID, &kALERT_NOTIFICATION_CID },
   { NS_ALERTSERVICE_CONTRACTID, &kNS_ALERTSSERVICE_CID },
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { NS_PARENTALCONTROLSSERVICE_CONTRACTID, &kNS_PARENTALCONTROLSSERVICE_CID },
 #endif
   { NS_DOWNLOADMANAGER_CONTRACTID, &kNS_DOWNLOADMANAGER_CID },
   { NS_DOWNLOADPLATFORM_CONTRACTID, &kNS_DOWNLOADPLATFORM_CID },
   { NS_FIND_SERVICE_CONTRACTID, &kNS_FIND_SERVICE_CID },
   { NS_TYPEAHEADFIND_CONTRACTID, &kNS_TYPEAHEADFIND_CID },
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAlertsIconListener.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "imgIRequest.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
+#include "nsIAlertsService.h"
 #include "nsIImageToPixbuf.h"
 #include "nsIStringBundle.h"
 #include "nsIObserverService.h"
 #include "nsIURI.h"
 #include "nsCRT.h"
 
 #include <dlfcn.h>
 #include <gdk/gdk.h>
@@ -181,16 +182,21 @@ nsresult
 nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
 {
   mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
                                           nullptr, nullptr);
 
   if (!mNotification)
     return NS_ERROR_OUT_OF_MEMORY;
 
+  nsCOMPtr<nsIObserverService> obsServ =
+      do_GetService("@mozilla.org/observer-service;1");
+  if (obsServ)
+    obsServ->AddObserver(this, "quit-application", true);
+
   if (aPixbuf)
     notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
 
   NS_ADDREF(this);
   if (mAlertHasAction) {
     // What we put as the label doesn't matter here, if the action
     // string is "default" then that makes the entire bubble clickable
     // rather than creating a button.
@@ -272,23 +278,18 @@ nsAlertsIconListener::Observe(nsISupport
     g_object_unref(mNotification);
     mNotification = nullptr;
     Release(); // equivalent to NS_RELEASE(this)
   }
   return NS_OK;
 }
 
 nsresult
-nsAlertsIconListener::InitAlertAsync(const nsAString & aImageUrl,
-                                     const nsAString & aAlertTitle, 
-                                     const nsAString & aAlertText,
-                                     bool aAlertTextClickable,
-                                     const nsAString & aAlertCookie,
-                                     nsIObserver * aAlertListener,
-                                     bool aInPrivateBrowsing)
+nsAlertsIconListener::InitAlertAsync(nsIAlertNotification* aAlert,
+                                     nsIObserver* aAlertListener)
 {
   if (!libNotifyHandle)
     return NS_ERROR_FAILURE;
 
   if (!notify_is_initted()) {
     // Give the name of this application to libnotify
     nsCOMPtr<nsIStringBundleService> bundleService = 
       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
@@ -330,32 +331,43 @@ nsAlertsIconListener::InitAlertAsync(con
   }
 
   if (!gHasCaps) {
     // if notify_get_server_caps() failed above we need to assume
     // there is no notification-server to display anything
     return NS_ERROR_FAILURE;
   }
 
-  if (!gHasActions && aAlertTextClickable)
+  nsresult rv = aAlert->GetTextClickable(&mAlertHasAction);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!gHasActions && mAlertHasAction)
     return NS_ERROR_FAILURE; // No good, fallback to XUL
 
-  nsCOMPtr<nsIObserverService> obsServ =
-      do_GetService("@mozilla.org/observer-service;1");
-  if (obsServ)
-    obsServ->AddObserver(this, "quit-application", true);
-
+  nsAutoString title;
+  rv = aAlert->GetTitle(title);
+  NS_ENSURE_SUCCESS(rv, rv);
   // Workaround for a libnotify bug - blank titles aren't dealt with
   // properly so we use a space
-  if (aAlertTitle.IsEmpty()) {
+  if (title.IsEmpty()) {
     mAlertTitle = NS_LITERAL_CSTRING(" ");
   } else {
-    mAlertTitle = NS_ConvertUTF16toUTF8(aAlertTitle);
+    mAlertTitle = NS_ConvertUTF16toUTF8(title);
   }
 
-  mAlertText = NS_ConvertUTF16toUTF8(aAlertText);
-  mAlertHasAction = aAlertTextClickable;
+  nsAutoString text;
+  rv = aAlert->GetText(text);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mAlertText = NS_ConvertUTF16toUTF8(text);
 
   mAlertListener = aAlertListener;
-  mAlertCookie = aAlertCookie;
+
+  rv = aAlert->GetCookie(mAlertCookie);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return StartRequest(aImageUrl, aInPrivateBrowsing);
+  nsAutoString imageUrl;
+  rv = aAlert->GetImageURL(imageUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+  bool inPrivateBrowsing;
+  rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return StartRequest(imageUrl, inPrivateBrowsing);
 }
--- a/toolkit/system/gnome/nsAlertsIconListener.h
+++ b/toolkit/system/gnome/nsAlertsIconListener.h
@@ -10,37 +10,33 @@
 #include "imgINotificationObserver.h"
 #include "nsString.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
 class imgIRequest;
+class nsIAlertNotification;
 
 struct NotifyNotification;
 
 class nsAlertsIconListener : public imgINotificationObserver,
                              public nsIObserver,
                              public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGINOTIFICATIONOBSERVER
   NS_DECL_NSIOBSERVER
 
   nsAlertsIconListener();
 
-  nsresult InitAlertAsync(const nsAString & aImageUrl,
-                          const nsAString & aAlertTitle, 
-                          const nsAString & aAlertText,
-                          bool aAlertTextClickable,
-                          const nsAString & aAlertCookie,
-                          nsIObserver * aAlertListener,
-                          bool aInPrivateBrowsing);
+  nsresult InitAlertAsync(nsIAlertNotification* aAlert,
+                          nsIObserver* aAlertListener);
 
   void SendCallback();
   void SendClosed();
 
 protected:
   virtual ~nsAlertsIconListener();
 
   nsresult OnLoadComplete(imgIRequest* aRequest);
--- a/toolkit/system/gnome/nsSystemAlertsService.cpp
+++ b/toolkit/system/gnome/nsSystemAlertsService.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "nsComponentManagerUtils.h"
 #include "nsXULAppAPI.h"
 #include "nsSystemAlertsService.h"
 #include "nsAlertsIconListener.h"
 #include "nsAutoPtr.h"
 
 NS_IMPL_ADDREF(nsSystemAlertsService)
 NS_IMPL_RELEASE(nsSystemAlertsService)
 
@@ -35,21 +36,36 @@ NS_IMETHODIMP nsSystemAlertsService::Sho
                                                            nsIObserver * aAlertListener,
                                                            const nsAString & aAlertName,
                                                            const nsAString & aBidi,
                                                            const nsAString & aLang,
                                                            const nsAString & aData,
                                                            nsIPrincipal * aPrincipal,
                                                            bool aInPrivateBrowsing)
 {
+  nsCOMPtr<nsIAlertNotification> alert =
+    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+  NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+  nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+                            aAlertText, aAlertTextClickable,
+                            aAlertCookie, aBidi, aLang, aData,
+                            aPrincipal, aInPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return ShowAlert(alert, aAlertListener);
+}
+
+NS_IMETHODIMP nsSystemAlertsService::ShowAlert(nsIAlertNotification* aAlert,
+                                               nsIObserver* aAlertListener)
+{
+  NS_ENSURE_ARG(aAlert);
+
   RefPtr<nsAlertsIconListener> alertListener = new nsAlertsIconListener();
   if (!alertListener)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  return alertListener->InitAlertAsync(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable,
-                                       aAlertCookie, aAlertListener, aInPrivateBrowsing);
+  return alertListener->InitAlertAsync(aAlert, aAlertListener);
 }
 
 NS_IMETHODIMP nsSystemAlertsService::CloseAlert(const nsAString& aAlertName,
                                                 nsIPrincipal* aPrincipal)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/widget/cocoa/OSXNotificationCenter.mm
+++ b/widget/cocoa/OSXNotificationCenter.mm
@@ -6,16 +6,17 @@
 #include "OSXNotificationCenter.h"
 #import <AppKit/AppKit.h>
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "imgLoader.h"
 #import "nsCocoaUtils.h"
+#include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsObjCExceptions.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsIContentPolicy.h"
 #include "nsAlertsUtils.h"
 #include "imgRequestProxy.h"
@@ -236,44 +237,72 @@ OSXNotificationCenter::ShowAlertNotifica
                                              nsIObserver * aAlertListener,
                                              const nsAString & aAlertName,
                                              const nsAString & aBidi,
                                              const nsAString & aLang,
                                              const nsAString & aData,
                                              nsIPrincipal * aPrincipal,
                                              bool aInPrivateBrowsing)
 {
+  nsCOMPtr<nsIAlertNotification> alert =
+    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+  NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+  nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+                            aAlertText, aAlertTextClickable,
+                            aAlertCookie, aBidi, aLang, aData,
+                            aPrincipal, aInPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return ShowAlert(alert, aAlertListener);
+}
+
+NS_IMETHODIMP
+OSXNotificationCenter::ShowAlert(nsIAlertNotification* aAlert,
+                                 nsIObserver* aAlertListener)
+{
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
+  NS_ENSURE_ARG(aAlert);
+
   Class unClass = NSClassFromString(@"NSUserNotification");
   id<FakeNSUserNotification> notification = [[unClass alloc] init];
-  notification.title = nsCocoaUtils::ToNSString(aAlertTitle);
+
+  nsAutoString title;
+  nsresult rv = aAlert->GetTitle(title);
+  NS_ENSURE_SUCCESS(rv, rv);
+  notification.title = nsCocoaUtils::ToNSString(title);
 
+  nsCOMPtr<nsIPrincipal> principal;
+  rv = aAlert->GetPrincipal(getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
   nsAutoString hostPort;
-  nsAlertsUtils::GetSourceHostPort(aPrincipal, hostPort);
+  nsAlertsUtils::GetSourceHostPort(principal, hostPort);
   nsCOMPtr<nsIStringBundle> bundle;
   nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   sbs->CreateBundle("chrome://alerts/locale/alert.properties", getter_AddRefs(bundle));
 
   if (!hostPort.IsEmpty() && bundle) {
     const char16_t* formatStrings[] = { hostPort.get() };
     nsXPIDLString notificationSource;
     bundle->FormatStringFromName(MOZ_UTF16("source.label"),
                                  formatStrings,
                                  ArrayLength(formatStrings),
                                  getter_Copies(notificationSource));
     notification.subtitle = nsCocoaUtils::ToNSString(notificationSource);
   }
 
-  notification.informativeText = nsCocoaUtils::ToNSString(aAlertText);
+  nsAutoString text;
+  rv = aAlert->GetText(text);
+  NS_ENSURE_SUCCESS(rv, rv);
+  notification.informativeText = nsCocoaUtils::ToNSString(text);
+
   notification.soundName = NSUserNotificationDefaultSoundName;
   notification.hasActionButton = NO;
 
   // If this is not an application/extension alert, show additional actions dealing with permissions.
-  if (nsAlertsUtils::IsActionablePrincipal(aPrincipal)) {
+  if (nsAlertsUtils::IsActionablePrincipal(principal)) {
     if (bundle) {
       nsXPIDLString closeButtonTitle, actionButtonTitle, disableButtonTitle, settingsButtonTitle;
       bundle->GetStringFromName(MOZ_UTF16("closeButton.title"),
                                 getter_Copies(closeButtonTitle));
       bundle->GetStringFromName(MOZ_UTF16("actionButton.label"),
                                 getter_Copies(actionButtonTitle));
       if (!hostPort.IsEmpty()) {
         const char16_t* formatStrings[] = { hostPort.get() };
@@ -302,52 +331,67 @@ OSXNotificationCenter::ShowAlertNotifica
         [(NSObject*)notification setValue:@[
                                             nsCocoaUtils::ToNSString(disableButtonTitle),
                                             nsCocoaUtils::ToNSString(settingsButtonTitle)
                                             ]
                                  forKey:@"_alternateActionButtonTitles"];
       }
     }
   }
-  NSString *alertName = nsCocoaUtils::ToNSString(aAlertName);
+  nsAutoString name;
+  rv = aAlert->GetName(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NSString *alertName = nsCocoaUtils::ToNSString(name);
   if (!alertName) {
     return NS_ERROR_FAILURE;
   }
   notification.userInfo = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:alertName, nil]
                                                       forKeys:[NSArray arrayWithObjects:@"name", nil]];
 
-  OSXNotificationInfo *osxni = new OSXNotificationInfo(alertName, aAlertListener, aAlertCookie);
+  nsAutoString cookie;
+  rv = aAlert->GetCookie(cookie);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  OSXNotificationInfo *osxni = new OSXNotificationInfo(alertName, aAlertListener, cookie);
+
+  nsAutoString imageUrl;
+  rv = aAlert->GetImageURL(imageUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool inPrivateBrowsing;
+  rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Show the notification without waiting for an image if there is no icon URL or
   // notification icons are not supported on this version of OS X.
-  if (aImageUrl.IsEmpty() || ![unClass instancesRespondToSelector:@selector(setContentImage:)]) {
+  if (imageUrl.IsEmpty() || ![unClass instancesRespondToSelector:@selector(setContentImage:)]) {
     CloseAlertCocoaString(alertName);
     mActiveAlerts.AppendElement(osxni);
     [GetNotificationCenter() deliverNotification:notification];
     [notification release];
     if (aAlertListener) {
-      aAlertListener->Observe(nullptr, "alertshow", PromiseFlatString(aAlertCookie).get());
+      aAlertListener->Observe(nullptr, "alertshow", cookie.get());
     }
   } else {
     mPendingAlerts.AppendElement(osxni);
     osxni->mPendingNotifiction = notification;
     RefPtr<imgLoader> il = imgLoader::GetInstance();
     if (il) {
       nsCOMPtr<nsIURI> imageUri;
-      NS_NewURI(getter_AddRefs(imageUri), aImageUrl);
+      NS_NewURI(getter_AddRefs(imageUri), imageUrl);
       if (imageUri) {
-        nsresult rv = il->LoadImage(imageUri, nullptr, nullptr,
-                                    mozilla::net::RP_Default,
-                                    aPrincipal, nullptr,
-                                    this, nullptr,
-                                    aInPrivateBrowsing ? nsIRequest::LOAD_ANONYMOUS :
-                                                         nsIRequest::LOAD_NORMAL,
-                                    nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
-                                    EmptyString(),
-                                    getter_AddRefs(osxni->mIconRequest));
+        rv = il->LoadImage(imageUri, nullptr, nullptr,
+                           mozilla::net::RP_Default,
+                           principal, nullptr,
+                           this, nullptr,
+                           inPrivateBrowsing ? nsIRequest::LOAD_ANONYMOUS :
+                                               nsIRequest::LOAD_NORMAL,
+                           nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
+                           EmptyString(),
+                           getter_AddRefs(osxni->mIconRequest));
         if (NS_SUCCEEDED(rv)) {
           // Set a timer for six seconds. If we don't have an icon by the time this
           // goes off then we go ahead without an icon.
           nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
           osxni->mIconTimeoutTimer = timer;
           timer->InitWithCallback(this, 6000, nsITimer::TYPE_ONE_SHOT);
           return NS_OK;
         }