Bug 870413 Implement permission prompt for desktop notifications in SeaMonkey r=Neil.
authorPhilip Chee <philip.chee@gmail.com>
Sat, 01 Jun 2013 02:29:05 +0800
changeset 12535 800dca20e0a25e96b7a55192281f023c0e4d4d5b
parent 12534 39546ca48c28919a875265d601f336e0ccabfe71
child 12536 8919c64f2bb26c5829d40ed77a1ff261a330c39a
push id9218
push userphilip.chee@gmail.com
push dateFri, 31 May 2013 18:45:03 +0000
treeherdercomm-central@1bc26fe50696 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil
bugs870413
Bug 870413 Implement permission prompt for desktop notifications in SeaMonkey r=Neil.
suite/browser/navigator.xul
suite/browser/pageinfo/pageInfo.xul
suite/browser/pageinfo/permissions.js
suite/common/bindings/notification.xml
suite/common/src/nsSuiteGlue.js
suite/locales/en-US/chrome/browser/pageInfo.dtd
suite/locales/en-US/chrome/common/notification.properties
suite/themes/classic/communicator/communicator.css
suite/themes/classic/communicator/icons/notification-16.png
suite/themes/classic/communicator/icons/notification-64.png
suite/themes/classic/jar.mn
suite/themes/classic/mac/communicator/communicator.css
suite/themes/classic/mac/navigator/navigator.css
suite/themes/classic/navigator/navigator.css
suite/themes/modern/communicator/communicator.css
suite/themes/modern/communicator/icons/notification-16.png
suite/themes/modern/communicator/icons/notification-64.png
suite/themes/modern/global/alerts/alert.css
suite/themes/modern/global/alerts/notification-48.png
suite/themes/modern/jar.mn
suite/themes/modern/navigator/navigator.css
--- a/suite/browser/navigator.xul
+++ b/suite/browser/navigator.xul
@@ -274,16 +274,17 @@
                onmousedown="event.stopPropagation();"
                onclick="event.stopPropagation();">
             <image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
           </box>
           <deck id="page-proxy-deck"
                 class="urlbar-icons"
                 ondragstart="proxyIconDNDObserver.onDragStart(event);"
                 onclick="handlePageProxyClick(event);">
             <image id="page-proxy-button"
                    tooltiptext="&proxyIcon.tooltip;"/>
             <image id="page-proxy-favicon" validate="never"
--- a/suite/browser/pageinfo/pageInfo.xul
+++ b/suite/browser/pageinfo/pageInfo.xul
@@ -39,21 +39,25 @@
     <command id="cmd_help"      oncommand="doHelpButton();"/>
     <command id="cmd_copy"      oncommand="doCopy(false);"/>
     <command id="cmd_selectall" oncommand="doSelectAll();"/>
 
     <!-- permissions tab -->
     <command id="cmd_imageDef"      oncommand="onCheckboxClick('image');"/>
     <command id="cmd_popupDef"      oncommand="onCheckboxClick('popup');"/>
     <command id="cmd_cookieDef"     oncommand="onCheckboxClick('cookie');"/>
+    <command id="cmd_desktop-notificationDef"
+                                    oncommand="onCheckboxClick('desktop-notification');"/>
     <command id="cmd_installDef"    oncommand="onCheckboxClick('install');"/>
     <command id="cmd_geoDef"        oncommand="onCheckboxClick('geo');"/>
     <command id="cmd_imageToggle"   oncommand="onRadioClick('image');"/>
     <command id="cmd_popupToggle"   oncommand="onRadioClick('popup');"/>
     <command id="cmd_cookieToggle"  oncommand="onRadioClick('cookie');"/>
+    <command id="cmd_desktop-notificationToggle"
+                                    oncommand="onRadioClick('desktop-notification');"/>
     <command id="cmd_installToggle" oncommand="onRadioClick('install');"/>
     <command id="cmd_geoToggle"     oncommand="onRadioClick('geo');"/>
 
     <!-- links tab -->
     <command id="cmd_openInNewTab"     oncommand="onOpenIn('tab');"/>
     <command id="cmd_openInNewWindow"  oncommand="onOpenIn('window');"/>
     <command id="cmd_copyLinks"        oncommand="doCopy(true);"/>
   </commandset>
@@ -358,16 +362,29 @@
               <radiogroup id="cookieRadioGroup" orient="horizontal">
                 <radio id="cookie-1" command="cmd_cookieToggle" label="&permAllow;"/>
                 <radio id="cookie-8" command="cmd_cookieToggle" label="&permAllowSession;"/>
                 <radio id="cookie-2" command="cmd_cookieToggle" label="&permBlock;"/>
               </radiogroup>
             </hbox>
           </richlistitem>
           <richlistitem class="permission" orient="vertical">
+            <label id="permNotificationLabel" class="permissionLabel"
+                   value="&permNotifications;" control="desktop-notificationRadioGroup"/>
+            <hbox role="group" aria-labelledby="permNotificationLabel">
+              <checkbox class="indent" id="desktop-notificationDef"
+                        command="cmd_desktop-notificationDef" label="&permUseDefault;"/>
+              <spacer flex="1"/>
+              <radiogroup id="desktop-notificationRadioGroup" orient="horizontal">
+                <radio id="desktop-notification-1" command="cmd_desktop-notificationToggle" label="&permAllow;"/>
+                <radio id="desktop-notification-2" command="cmd_desktop-notificationToggle" label="&permBlock;"/>
+              </radiogroup>
+            </hbox>
+          </richlistitem>
+          <richlistitem class="permission" orient="vertical">
             <label id="permInstallLabel" class="permissionLabel" value="&permInstall;" control="installRadioGroup"/>
             <hbox role="group" aria-labelledby="permInstallLabel">
               <checkbox class="indent" id="installDef" command="cmd_installDef" label="&permUseDefault;"/>
               <spacer flex="1"/>
               <radiogroup id="installRadioGroup" orient="horizontal">
                 <radio id="install-1" command="cmd_installToggle" label="&permAllow;"/>
                 <radio id="install-2" command="cmd_installToggle" label="&permBlock;"/>
               </radiogroup>
--- a/suite/browser/pageinfo/permissions.js
+++ b/suite/browser/pageinfo/permissions.js
@@ -22,16 +22,20 @@ var gPermObj = {
   {
     if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == 2)
       return BLOCK;
 
     if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") == 2)
       return SESSION;
     return ALLOW;
   },
+  "desktop-notification": function getNotificationDefaultPermission()
+  {
+    return BLOCK;
+  },
   popup: function getPopupDefaultPermission()
   {
     if (Services.prefs.getBoolPref("dom.disable_open_during_load"))
       return BLOCK;
     return ALLOW;
   },
   install: function getInstallDefaultPermission()
   {
@@ -129,17 +133,17 @@ function onCheckboxClick(aPartId)
     command.removeAttribute("disabled");
   }
 }
 
 function onRadioClick(aPartId)
 {
   var radioGroup = document.getElementById(aPartId + "RadioGroup");
   var id = radioGroup.selectedItem.id;
-  var permission = id.split('-')[1];
+  var permission = id.replace(/.*-/, "");
   Services.perms.add(gPermURI, aPartId, permission);
 }
 
 function setRadioState(aPartId, aValue)
 {
   var radio = document.getElementById(aPartId + "-" + aValue);
   radio.radioGroup.selectedItem = radio;
 }
--- a/suite/common/bindings/notification.xml
+++ b/suite/common/bindings/notification.xml
@@ -1432,16 +1432,72 @@
             link.className = "text-link";
             link.setAttribute("value", this._stringBundle.GetStringFromName(type + ".learnMore"));
             link.setAttribute("href", this._urlFormatter.formatURLPref("browser." + type + ".warning.infoURL"));
             document.getAnonymousElementByAttribute(box, "anonid", "messageText").appendChild(link);
           ]]>
         </body>
       </method>
 
+      <method name="showWebNotificationPrompt">
+        <parameter name="site"/>
+        <parameter name="allowCallback"/>
+        <parameter name="cancelCallback"/>
+        <body>
+          <![CDATA[
+            const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
+            var type = "webNotifications";
+            if (this.getNotificationWithValue(type))
+              return;
+
+            var buttons = [{
+              label: this._stringBundle.GetStringFromName(type + ".showForSession"),
+              accessKey: this._stringBundle.GetStringFromName(type + ".showForSession.accesskey"),
+              popup: null,
+              callback: function (aNotificationBox, aButton) {
+                var expireType = aNotificationBox.checkbox.checked ?
+                                 nsIPermissionManager.EXPIRE_NEVER :
+                                 nsIPermissionManager.EXPIRE_SESSION;
+                allowCallback(true, expireType);
+              }
+            }, {
+              label: this._stringBundle.GetStringFromName(type + ".dontShowForSession"),
+              accessKey: this._stringBundle.GetStringFromName(type + ".dontShowForSession.accesskey"),
+              popup: null,
+              callback: function (aNotificationBox, aButton) {
+                var expireType = aNotificationBox.checkbox.checked ?
+                                 nsIPermissionManager.EXPIRE_NEVER :
+                                 nsIPermissionManager.EXPIRE_SESSION;
+                cancelCallback(true, expireType);
+              }
+            }];
+            var message = this._stringBundle
+                              .formatStringFromName(type + ".showFromSite",
+                                                    [site], 1);
+
+            var box = this.appendNotification(message, type, null,
+                                              this.PRIORITY_INFO_HIGH, buttons);
+            // Force a style flush, so that we ensure the binding is attached.
+            box.clientTop;
+
+            // Create a dummy checkbox so private requests don't try to remember.
+            box.checkbox = { checked: false };
+
+            // Don't offer action in Private Browsing mode if the action
+            // remembers permission for more than a session.
+            if (!this.usePrivateBrowsing) {
+              box.checkbox = document.createElement("checkbox");
+              box.checkbox.className = "rememberChoice";
+              box.checkbox.setAttribute("label", this._stringBundle.GetStringFromName(type + ".remember"));
+              box.appendChild(box.checkbox);
+            }
+          ]]>
+        </body>
+      </method>
+
       <method name="promptIndexedDB">
         <parameter name="aRequestor"/>
         <parameter name="aWindow"/>
         <parameter name="aTopic"/>
         <parameter name="aData"/>
         <body>
           <![CDATA[
             var host = aWindow.document.documentURIObject.asciiHost;
@@ -2512,16 +2568,69 @@
             PopupNotifications.show(this.activeBrowser,
                                     "geolocation", message,
                                     "geo-notification-icon", mainAction,
                                     secondaryActions, options);
           ]]>
         </body>
       </method>
 
+      <method name="showWebNotificationPrompt">
+        <parameter name="site"/>
+        <parameter name="allowCallback"/>
+        <parameter name="cancelCallback"/>
+        <body>
+          <![CDATA[
+            const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
+            var type = "webNotifications";
+            var mainAction = {
+              label: this._stringBundle.GetStringFromName(type + ".showForSession"),
+              accessKey: this._stringBundle.GetStringFromName(type + ".showForSession.accesskey"),
+              callback: function(aNotification) {
+                allowCallback(true, nsIPermissionManager.EXPIRE_SESSION);
+              }
+            };
+
+            var secondaryActions = [{
+              label: this._stringBundle.GetStringFromName(type + ".dontShowForSession"),
+              accessKey: this._stringBundle.GetStringFromName(type + ".dontShowForSession.accesskey"),
+              callback: function (aNotification) {
+                cancelCallback(true, nsIPermissionManager.EXPIRE_SESSION);
+              }
+            }, {
+              label: this._stringBundle.GetStringFromName(type + ".alwaysShow"),
+              accessKey: this._stringBundle.GetStringFromName(type + ".alwaysShow.accesskey"),
+              callback: function (aNotification) {
+                allowCallback(true, nsIPermissionManager.EXPIRE_NEVER);
+              }
+            }, {
+              label: this._stringBundle.GetStringFromName(type + ".neverShow"),
+              accessKey: this._stringBundle.GetStringFromName(type + ".neverShow.accesskey"),
+              callback: function (aNotification) {
+                cancelCallback(true, nsIPermissionManager.EXPIRE_NEVER);
+              }
+            }];
+
+            // Don't offer action in Private Browsing mode if the action
+            // remembers permission for more than a session.
+            if (this.usePrivateBrowsing)
+              secondaryActions.length = 1;
+
+            var message = this._stringBundle
+                              .formatStringFromName(type + ".showFromSite",
+                                                    [site], 1);
+
+            PopupNotifications.show(this.activeBrowser,
+                                    "web-notifications", message,
+                                    "web-notifications-notification-icon",
+                                    mainAction, secondaryActions);
+          ]]>
+        </body>
+      </method>
+
       <method name="promptIndexedDB">
         <parameter name="aRequestor"/>
         <parameter name="aWindow"/>
         <parameter name="aTopic"/>
         <parameter name="aData"/>
         <body>
           <![CDATA[
             var host = aWindow.document.documentURIObject.asciiHost;
--- a/suite/common/src/nsSuiteGlue.js
+++ b/suite/common/src/nsSuiteGlue.js
@@ -1076,57 +1076,75 @@ function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
   classID: Components.ID("{9d4c845d-3f09-402a-b66d-50f291d7d50f}"),
 
   QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIContentPermissionPrompt]),
 
   prompt: function(aRequest)
   {
-    if (aRequest.type != "geolocation")
+    const kFeatureKeys = { "geolocation" : "geo",
+                           "desktop-notification" : "desktop-notification",
+                         };
+
+    // Make sure that we support the request.
+    if (!(aRequest.type in kFeatureKeys))
       return;
 
     var path, host;
     var requestingPrincipal = aRequest.principal;
     var requestingURI = requestingPrincipal.URI;
 
     if (requestingURI instanceof Components.interfaces.nsIFileURL)
       path = requestingURI.file.path;
     else if (requestingURI instanceof Components.interfaces.nsIStandardURL)
       host = requestingURI.host;
     // Ignore requests from non-nsIStandardURLs
     else
       return;
 
-    switch (Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, "geo")) {
+    var perm = kFeatureKeys[aRequest.type];
+    switch (Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, perm)) {
       case Services.perms.ALLOW_ACTION:
         aRequest.allow();
         return;
       case Services.perms.DENY_ACTION:
         aRequest.cancel();
         return;
     }
 
-    function allowCallback(remember) {
+    function allowCallback(remember, expireType) {
       if (remember)
-        Services.perms.addFromPrincipal(requestingPrincipal, "geo", Services.perms.ALLOW_ACTION);
+        Services.perms.addFromPrincipal(requestingPrincipal, perm,
+                                        Services.perms.ALLOW_ACTION,
+                                        expireType);
       aRequest.allow();
     }
 
-    function cancelCallback(remember) {
+    function cancelCallback(remember, expireType) {
       if (remember)
-        Services.perms.addFromPrincipal(requestingPrincipal, "geo", Services.perms.DENY_ACTION);
+        Services.perms.addFromPrincipal(requestingPrincipal, perm,
+                                        Services.perms.DENY_ACTION,
+                                        expireType);
       aRequest.cancel();
     }
 
-    aRequest.window
-            .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-            .getInterface(Components.interfaces.nsIWebNavigation)
-            .QueryInterface(Components.interfaces.nsIDocShell)
-            .chromeEventHandler.parentNode.wrappedJSObject
-            .showGeolocationPrompt(path, host,
-                                   allowCallback,
-                                   cancelCallback);
+    var nb = aRequest.window
+                     .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                     .getInterface(Components.interfaces.nsIWebNavigation)
+                     .QueryInterface(Components.interfaces.nsIDocShell)
+                     .chromeEventHandler.parentNode;
+
+    // Show the prompt.
+    switch (aRequest.type) {
+      case "geolocation":
+        nb.showGeolocationPrompt(path, host, allowCallback, cancelCallback);
+        break;
+      case "desktop-notification":
+        if (host)
+          nb.showWebNotificationPrompt(host, allowCallback, cancelCallback);
+        break;
+    }
   },
 };
 
 //module initialization
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([SuiteGlue, ContentPermissionPrompt]);
--- a/suite/locales/en-US/chrome/browser/pageInfo.dtd
+++ b/suite/locales/en-US/chrome/browser/pageInfo.dtd
@@ -88,16 +88,17 @@
 <!ENTITY  permAskAlways         "Always ask">
 <!ENTITY  permAllow             "Allow">
 <!ENTITY  permAllowSession      "Allow for Session">
 <!ENTITY  permBlock             "Block">
 <!ENTITY  permissionsFor        "Permissions for:">
 <!ENTITY  permImage             "Load Images">
 <!ENTITY  permPopup             "Open Popup Windows">
 <!ENTITY  permCookie            "Set Cookies">
+<!ENTITY  permNotifications     "Show Notifications">
 <!ENTITY  permInstall           "Install Extensions or Themes">
 <!ENTITY  permGeo               "Share Location">
 
 <!ENTITY  securityTab           "Security">
 <!ENTITY  securityTab.accesskey "S">
 <!ENTITY  securityHeader        "Security information for this page">
 <!ENTITY  securityView.certView "View Certificate">
 <!ENTITY  securityView.accesskey "V">
--- a/suite/locales/en-US/chrome/common/notification.properties
+++ b/suite/locales/en-US/chrome/common/notification.properties
@@ -121,16 +121,28 @@ geolocation.neverShareLocation=Never Sha
 geolocation.neverShareLocation.accesskey=N
 geolocation.siteWantsToKnow=This website (%S) wants to know your location.
 geolocation.fileWantsToKnow=The file %S wants to know your location.
 # LOCALIZATION NOTE (geolocation.learnMore): Use the unicode ellipsis char, \u2026,
 # or use "..." unless \u2026 doesn't suit traditions in your locale.
 geolocation.learnMore=Learn Moreā€¦
 geolocation.remember=Remember for this website
 
+# Desktop Notifications
+webNotifications.showForSession=Show Notifications
+webNotifications.showForSession.accesskey=w
+webNotifications.dontShowForSession=Don't Show
+webNotifications.dontShowForSession.accesskey=o
+webNotifications.alwaysShow=Always Show
+webNotifications.alwaysShow.accesskey=A
+webNotifications.neverShow=Never Show
+webNotifications.neverShow.accesskey=N
+webNotifications.showFromSite=Would you like to show notifications from %S?
+webNotifications.remember=Remember for this website
+
 # IndexedDB
 offlineApps.permissions=This website (%S) is asking to store data on your computer for offline use.
 offlineApps.quota=This website (%1$S) is attempting to store more than %2$SMB of data on your computer for offline use.
 offlineApps.allow=Allow
 offlineApps.allow.accesskey=A
 offlineApps.later=Not Now
 offlineApps.later.accesskey=N
 offlineApps.deny=Never for This Site
--- a/suite/themes/classic/communicator/communicator.css
+++ b/suite/themes/classic/communicator/communicator.css
@@ -138,16 +138,20 @@ treecols:-moz-lwtheme {
 .messageImage[value="plugin-crashed"] {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
 }
 
 .messageImage[value="geolocation"] {
   list-style-image: url("chrome://communicator/skin/icons/geo.png");
 }
 
+.messageImage[value="webNotifications"] {
+  list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
 .messageImage[value="indexedDB-permissions-prompt"],
 .messageImage[value="indexedDB-quota-prompt"] {
   list-style-image: url("chrome://global/skin/icons/question-16.png");
 }
 
 .messageImage[value="addon-install-blocked"],
 .messageImage[value="addon-install-cancelled"],
 .messageImage[value="addon-install-complete"],
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9e61a63973038c48b5e9b82849ba8735eff76f29
GIT binary patch
literal 524
zc$@(Y0`vWeP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00006VoOIv0RI60
z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru+zJB|0tuw22DAVG0hvie
zK~y-)jnhp?8$lGn@rmTB7QAT%5sLQU*;_pqdh(_>DS8mZQYn5MJk(S+5zU9X`D)D8
zjmD2eMWm+`K`I^^5n4S8-V`M2P5glIpR&SaK+S_6?0d8C4Lj@zQAR`-HrtTL87$O@
zawDAD?8X(kP>X)#QT=SAX>U{`4GFKd_7UMHn`h_OTX6#Ec&n_r9J{D5X`HsQwHU;2
zNXIpLaqr|a=n7eD-P6|EfCwcsE_}cuZeZXJ(ow*^l2@W5XstH}li92YBv!nTiXkn(
z!(SzD3M5uLdbVYSe=f5p{PCp%q~Z&1wc1bRIp>crj}`|^eP7uTlhH+~v60tmUvY#(
zbdK3M_i*N)+la01h>39O5>iosN2?vc^?2Cq?(#;LT6~doO!&<C1xU>}&b8Vnb%4*b
zO(T+iFN_)M4K5)SHyGCH_ZUGlJ|ow^b?W{<aXd8FfKK$`2a<@Q4pn#u1AlN1+cTSb
zRXoU&AL&~#(GKbO_0l~leo9Hf6NtZtbR47nTlW}aQ?XOr;sWMN?>_*AT(*UkW9zv9
O0000<MNUMnLSTZ~QR1Tj
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a01d0ab7764fd0766d4b38208e7e50ce996aba3a
GIT binary patch
literal 3373
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
Dh&Oq7
--- a/suite/themes/classic/jar.mn
+++ b/suite/themes/classic/jar.mn
@@ -137,16 +137,18 @@ classic.jar:
   skin/classic/communicator/icons/lock-insecure.png                     (communicator/icons/lock-insecure.png)
   skin/classic/communicator/icons/lock-insecure-16.png                  (communicator/icons/lock-insecure-16.png)
   skin/classic/communicator/icons/lock-secure.png                       (communicator/icons/lock-secure.png)
   skin/classic/communicator/icons/lock-secure-16.png                    (communicator/icons/lock-secure-16.png)
   skin/classic/communicator/icons/alwaysAsk.png                         (communicator/icons/alwaysAsk.png)
   skin/classic/communicator/icons/application.png                       (communicator/icons/application.png)
   skin/classic/communicator/icons/feedIcon.png                          (communicator/icons/feedIcon.png)
   skin/classic/communicator/icons/feedIcon16.png                        (communicator/icons/feedIcon16.png)
+  skin/classic/communicator/icons/notification-16.png                   (communicator/icons/notification-16.png)
+  skin/classic/communicator/icons/notification-64.png                   (communicator/icons/notification-64.png)
   skin/classic/communicator/icons/geo.png                               (communicator/icons/geo.png)
   skin/classic/communicator/icons/plugin.png                            (communicator/icons/plugin.png)
   skin/classic/communicator/icons/save.png                              (communicator/icons/save.png)
   skin/classic/communicator/icons/smileys/smiley-smile.png              (communicator/icons/smileys/smiley-smile.png)
   skin/classic/communicator/icons/smileys/smiley-frown.png              (communicator/icons/smileys/smiley-frown.png)
   skin/classic/communicator/icons/smileys/smiley-wink.png               (communicator/icons/smileys/smiley-wink.png)
   skin/classic/communicator/icons/smileys/smiley-tongue.png             (communicator/icons/smileys/smiley-tongue.png)
   skin/classic/communicator/icons/smileys/smiley-laughing.png           (communicator/icons/smileys/smiley-laughing.png)
--- a/suite/themes/classic/mac/communicator/communicator.css
+++ b/suite/themes/classic/mac/communicator/communicator.css
@@ -136,16 +136,20 @@ treecols:-moz-lwtheme {
 .messageImage[value="plugin-crashed"] {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
 }
 
 .messageImage[value="geolocation"] {
   list-style-image: url("chrome://communicator/skin/icons/geo.png");
 }
 
+.messageImage[value="webNotifications"] {
+  list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
 .messageImage[value="indexedDB-permissions-prompt"],
 .messageImage[value="indexedDB-quota-prompt"] {
   list-style-image: url("chrome://global/skin/icons/question-16.png");
 }
 
 .messageImage[value="addon-install-blocked"],
 .messageImage[value="addon-install-cancelled"],
 .messageImage[value="addon-install-complete"],
--- a/suite/themes/classic/mac/navigator/navigator.css
+++ b/suite/themes/classic/mac/navigator/navigator.css
@@ -240,16 +240,20 @@ toolbar[mode="text"] > #window-controls 
   height: 64px;
   -moz-margin-end: 10px;
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url("chrome://communicator/skin/icons/geolocation-64.png");
 }
 
+.popup-notification-icon[popupid="web-notifications"] {
+  list-style-image: url("chrome://communicator/skin/icons/notification-64.png");
+}
+
 .popup-notification-icon[popupid="addon-install-disabled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-started"],
 .popup-notification-icon[popupid="addon-install-cancelled"],
 .popup-notification-icon[popupid="addon-install-failed"],
 .popup-notification-icon[popupid="addon-install-complete"],
 .popup-notification-icon[popupid="lwtheme-install-request"],
 .popup-notification-icon[popupid="lwtheme-install-notification"] {
@@ -296,16 +300,22 @@ toolbar[mode="text"] > #window-controls 
 }
 
 #geo-notification-icon {
   list-style-image: url("chrome://communicator/skin/icons/geolocation-16.png");
   width: 16px;
   height: 16px;
 }
 
+#web-notifications-notification-icon {
+  list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+  width: 16px;
+  height: 16px;
+}
+
 #addons-notification-icon {
   list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
   width: 16px;
   height: 16px;
 }
 
 #indexedDB-notification-icon {
   list-style-image: url("chrome://global/skin/icons/question-16.png");
--- a/suite/themes/classic/navigator/navigator.css
+++ b/suite/themes/classic/navigator/navigator.css
@@ -274,16 +274,20 @@ toolbar[mode="text"] > #window-controls 
   height: 64px;
   -moz-margin-end: 10px;
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url("chrome://communicator/skin/icons/geolocation-64.png");
 }
 
+.popup-notification-icon[popupid="web-notifications"] {
+  list-style-image: url("chrome://communicator/skin/icons/notification-64.png");
+}
+
 .popup-notification-icon[popupid="addon-install-disabled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-started"],
 .popup-notification-icon[popupid="addon-install-cancelled"],
 .popup-notification-icon[popupid="addon-install-failed"],
 .popup-notification-icon[popupid="addon-install-complete"],
 .popup-notification-icon[popupid="lwtheme-install-request"],
 .popup-notification-icon[popupid="lwtheme-install-notification"] {
@@ -329,16 +333,22 @@ toolbar[mode="text"] > #window-controls 
 }
 
 #geo-notification-icon {
   list-style-image: url("chrome://communicator/skin/icons/geolocation-16.png");
   width: 16px;
   height: 16px;
 }
 
+#web-notifications-notification-icon {
+  list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+  width: 16px;
+  height: 16px;
+}
+
 #addons-notification-icon {
   list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
   width: 16px;
   height: 16px;
 }
 
 #indexedDB-notification-icon {
   list-style-image: url("chrome://global/skin/icons/question-16.png");
--- a/suite/themes/modern/communicator/communicator.css
+++ b/suite/themes/modern/communicator/communicator.css
@@ -105,16 +105,20 @@ toolbar[iconsize="small"] > #print-butto
 .messageImage[value="plugin-crashed"] {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
 }
 
 .messageImage[value="geolocation"] {
   list-style-image: url("chrome://communicator/skin/icons/geo.png");
 }
 
+.messageImage[value="webNotifications"] {
+  list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
 .messageImage[value="indexedDB-permissions-prompt"],
 .messageImage[value="indexedDB-quota-prompt"] {
   list-style-image: url("chrome://global/skin/icons/question-16.png");
 }
 
 .messageImage[value="addon-install-blocked"],
 .messageImage[value="addon-install-cancelled"],
 .messageImage[value="addon-install-complete"],
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9e61a63973038c48b5e9b82849ba8735eff76f29
GIT binary patch
literal 524
zc$@(Y0`vWeP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00006VoOIv0RI60
z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru+zJB|0tuw22DAVG0hvie
zK~y-)jnhp?8$lGn@rmTB7QAT%5sLQU*;_pqdh(_>DS8mZQYn5MJk(S+5zU9X`D)D8
zjmD2eMWm+`K`I^^5n4S8-V`M2P5glIpR&SaK+S_6?0d8C4Lj@zQAR`-HrtTL87$O@
zawDAD?8X(kP>X)#QT=SAX>U{`4GFKd_7UMHn`h_OTX6#Ec&n_r9J{D5X`HsQwHU;2
zNXIpLaqr|a=n7eD-P6|EfCwcsE_}cuZeZXJ(ow*^l2@W5XstH}li92YBv!nTiXkn(
z!(SzD3M5uLdbVYSe=f5p{PCp%q~Z&1wc1bRIp>crj}`|^eP7uTlhH+~v60tmUvY#(
zbdK3M_i*N)+la01h>39O5>iosN2?vc^?2Cq?(#;LT6~doO!&<C1xU>}&b8Vnb%4*b
zO(T+iFN_)M4K5)SHyGCH_ZUGlJ|ow^b?W{<aXd8FfKK$`2a<@Q4pn#u1AlN1+cTSb
zRXoU&AL&~#(GKbO_0l~leo9Hf6NtZtbR47nTlW}aQ?XOr;sWMN?>_*AT(*UkW9zv9
O0000<MNUMnLSTZ~QR1Tj
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a01d0ab7764fd0766d4b38208e7e50ce996aba3a
GIT binary patch
literal 3373
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
Dh&Oq7
--- a/suite/themes/modern/global/alerts/alert.css
+++ b/suite/themes/modern/global/alerts/alert.css
@@ -29,16 +29,21 @@
 .alertTitle {
   font-weight: bold;
   font-size: 110%;
 }
 
 #alertImage {
   max-width: 48px;
   max-height: 48px;
+  list-style-image: url("chrome://global/skin/alerts/notification-48.png");
+}
+
+#alertNotification {
+  border: ridge #5486DA 4px;
 }
 
 #alertNotification[clickable="true"] {
   cursor: pointer;
 }
 
 label {
   cursor: inherit;
@@ -62,8 +67,15 @@ label {
   }
   93.75% {
     opacity: 1;
   }
   to {
     opacity: 0;
   }
 }
+
+.alertCloseButton {
+  list-style-image: url("chrome://global/skin/icons/closebox.gif");
+  -moz-appearance: none;
+  padding: 2px 0px 0px;
+  border: none;
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..23ab5a0eb8fa19d70eb43c6c0a53c5ea8f3c695f
GIT binary patch
literal 2517
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
--- a/suite/themes/modern/jar.mn
+++ b/suite/themes/modern/jar.mn
@@ -82,16 +82,18 @@ modern.jar:
   skin/modern/communicator/icons/identity.png                      (communicator/icons/identity.png)
   skin/modern/communicator/icons/loading.gif                       (communicator/icons/loading.gif)
   skin/modern/communicator/icons/lock-broken.png                   (communicator/icons/lock-broken.png)
   skin/modern/communicator/icons/lock-broken-16.png                (communicator/icons/lock-broken-16.png)
   skin/modern/communicator/icons/lock-insecure.png                 (communicator/icons/lock-insecure.png)
   skin/modern/communicator/icons/lock-insecure-16.png              (communicator/icons/lock-insecure-16.png)
   skin/modern/communicator/icons/lock-secure.png                   (communicator/icons/lock-secure.png)
   skin/modern/communicator/icons/lock-secure-16.png                (communicator/icons/lock-secure-16.png)
+  skin/modern/communicator/icons/notification-16.png               (communicator/icons/notification-16.png)
+  skin/modern/communicator/icons/notification-64.png               (communicator/icons/notification-64.png)
   skin/modern/communicator/icons/offline.gif                       (communicator/icons/offline.gif)
   skin/modern/communicator/icons/online.gif                        (communicator/icons/online.gif)
   skin/modern/communicator/icons/plugin.png                        (communicator/icons/plugin.png)
   skin/modern/communicator/icons/save.png                          (communicator/icons/save.png)
   skin/modern/communicator/icons/smileys/smiley-smile.png          (communicator/icons/smileys/smiley-smile.png)
   skin/modern/communicator/icons/smileys/smiley-frown.png          (communicator/icons/smileys/smiley-frown.png)
   skin/modern/communicator/icons/smileys/smiley-wink.png           (communicator/icons/smileys/smiley-wink.png)
   skin/modern/communicator/icons/smileys/smiley-tongue.png         (communicator/icons/smileys/smiley-tongue.png)
@@ -228,16 +230,17 @@ modern.jar:
   skin/modern/global/textbox.css                                   (global/textbox.css)
   skin/modern/global/toolbar.css                                   (global/toolbar.css)
   skin/modern/global/toolbarbutton.css                             (global/toolbarbutton.css)
   skin/modern/global/tree.css                                      (global/tree.css)
   skin/modern/global/wizard.css                                    (global/wizard.css)
   skin/modern/global/scrollbars.css                                (global/scrollbars.css)
   skin/modern/global/scrollbars-mini.css                           (global/scrollbars-mini.css)
   skin/modern/global/alerts/alert.css                              (global/alerts/alert.css)
+  skin/modern/global/alerts/notification-48.png                    (global/alerts/notification-48.png)
   skin/modern/global/arrow/arrow-dn-dis.gif                        (global/arrow/arrow-dn-dis.gif)
   skin/modern/global/arrow/arrow-dn.gif                            (global/arrow/arrow-dn.gif)
   skin/modern/global/arrow/arrow-lft-dis.gif                       (global/arrow/arrow-lft-dis.gif)
   skin/modern/global/arrow/arrow-lft.gif                           (global/arrow/arrow-lft.gif)
   skin/modern/global/arrow/arrow-lft-sharp.gif                     (global/arrow/arrow-lft-sharp.gif)
   skin/modern/global/arrow/arrow-lft-sharp-end.gif                 (global/arrow/arrow-lft-sharp-end.gif)
   skin/modern/global/arrow/arrow-rit-dis.gif                       (global/arrow/arrow-rit-dis.gif)
   skin/modern/global/arrow/arrow-rit.gif                           (global/arrow/arrow-rit.gif)
--- a/suite/themes/modern/navigator/navigator.css
+++ b/suite/themes/modern/navigator/navigator.css
@@ -486,16 +486,20 @@ toolbar[mode="icons"] #search-button > .
   height: 64px;
   -moz-margin-end: 10px;
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url("chrome://communicator/skin/icons/geolocation-64.png");
 }
 
+.popup-notification-icon[popupid="web-notifications"] {
+  list-style-image: url("chrome://communicator/skin/icons/notification-64.png");
+}
+
 .popup-notification-icon[popupid="addon-install-disabled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-started"],
 .popup-notification-icon[popupid="addon-install-cancelled"],
 .popup-notification-icon[popupid="addon-install-failed"],
 .popup-notification-icon[popupid="addon-install-complete"],
 .popup-notification-icon[popupid="lwtheme-install-request"],
 .popup-notification-icon[popupid="lwtheme-install-notification"] {
@@ -541,16 +545,22 @@ toolbar[mode="icons"] #search-button > .
 }
 
 #geo-notification-icon {
   list-style-image: url("chrome://communicator/skin/icons/geolocation-16.png");
   width: 16px;
   height: 16px;
 }
 
+#web-notifications-notification-icon {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
 #addons-notification-icon {
   list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
   width: 16px;
   height: 16px;
 }
 
 #indexedDB-notification-icon {
   list-style-image: url("chrome://global/skin/icons/question-16.png");