Bug 774506 - Implement toast notification support. r=jaws

This commit is contained in:
Shane Caraveo 2012-08-26 16:46:45 -07:00
parent 949e962f6c
commit 488e0f589d
6 changed files with 184 additions and 146 deletions

View File

@ -82,50 +82,5 @@ var tests = {
});
}, "social:ambient-notification-changed", false);
}
},
testServiceWindow: function(next) {
// our test provider was initialized in the test above, we just
// initiate our specific test now.
let port = Social.provider.port;
ok(port, "provider has a port");
port.postMessage({topic: "test-service-window"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-service-window-message":
// The sidebar message will always come first, since it loads by default
ok(true, "got service window message");
// the service window URL should not be stored in history.
ensureSocialUrlNotRemembered(e.data.location);
port.postMessage({topic: "test-close-service-window"});
break;
case "got-service-window-closed-message":
ok(true, "got service window closed message");
next();
break;
}
}
},
testServiceWindowTwice: function(next) {
let port = Social.provider.port;
port.postMessage({topic: "test-service-window-twice"});
Social.provider.port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-service-window-twice-result":
is(e.data.result, "ok", "only one window should open when name is reused");
break;
case "got-service-window-message":
ok(true, "got service window message");
port.postMessage({topic: "test-close-service-window"});
break;
case "got-service-window-closed-message":
ok(true, "got service window closed message");
next();
break;
}
}
}
}

View File

@ -2,7 +2,7 @@
<head>
<meta charset="utf-8">
<script>
var win;
var testwindow;
function pingWorker() {
var port = navigator.mozSocial.getWorker().port;
port.onmessage = function(e) {
@ -12,32 +12,15 @@
navigator.mozSocial.openPanel("social_flyout.html");
break;
case "test-chatbox-open":
navigator.mozSocial.openChatWindow("social_chat.html", function(chatwin) {
port.postMessage({topic: "chatbox-opened", result: chatwin ? "ok" : "failed"});
});
break;
case "test-service-window":
win = navigator.mozSocial.openServiceWindow("social_window.html", "test-service-window", "width=300,height=300");
break;
case "test-service-window-twice":
win = navigator.mozSocial.openServiceWindow("social_window.html", "test-service-window", "width=300,height=300");
var win2 = navigator.mozSocial.openServiceWindow("social_window.html", "test-service-window", "");
var result;
if (win == win2)
result = "ok";
else
result = "not ok: " + win2 + " != " + win;
port.postMessage({topic: "test-service-window-twice-result", result: result});
break;
case "test-close-service-window":
win.addEventListener("unload", function watchClose() {
win.removeEventListener("unload", watchClose);
port.postMessage({topic: "service-window-closed-message", result: "ok"});
}, false)
win.close();
navigator.mozSocial.openChatWindow("social_chat.html",
function(chatwin) {
port.postMessage({topic: "chatbox-opened",
result: chatwin ? "ok" : "failed"});
});
break;
case "test-isVisible":
port.postMessage({topic: "test-isVisible-response", result: navigator.mozSocial.isVisible});
port.postMessage({topic: "test-isVisible-response",
result: navigator.mozSocial.isVisible});
break;
}
}

View File

@ -9,7 +9,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SocialService", "resource://gre/modules/SocialService.jsm");
const EXPORTED_SYMBOLS = ["MozSocialAPI"];
const EXPORTED_SYMBOLS = ["MozSocialAPI", "openChatWindow"];
var MozSocialAPI = {
_enabled: false,
@ -101,15 +101,6 @@ function attachToWindow(provider, targetWindow) {
return false;
}
},
openServiceWindow: {
enumerable: true,
configurable: true,
writable: true,
value: function(toURL, name, options) {
let url = targetWindow.document.documentURIObject.resolve(toURL);
return openServiceWindow(provider, targetWindow, url, name, options);
}
},
openChatWindow: {
enumerable: true,
configurable: true,
@ -195,12 +186,12 @@ function ensureProviderOrigin(provider, url) {
fullURL = Services.io.newURI(provider.origin, null, null).resolve(url);
uri = Services.io.newURI(fullURL, null, null);
} catch (ex) {
Cu.reportError("openServiceWindow: failed to resolve window URL: " + url + "; " + ex);
Cu.reportError("mozSocial: failed to resolve window URL: " + url + "; " + ex);
return null;
}
if (provider.origin != uri.prePath) {
Cu.reportError("openServiceWindow: unable to load new location, " +
Cu.reportError("mozSocial: unable to load new location, " +
provider.origin + " != " + uri.prePath);
return null;
}
@ -215,41 +206,3 @@ function openChatWindow(chromeWindow, provider, url, callback) {
return;
chromeWindow.SocialChatBar.newChat(provider, fullURL, callback);
}
function openServiceWindow(provider, contentWindow, url, name, options) {
// resolve partial URLs and check prePath matches
let fullURL = ensureProviderOrigin(provider, url);
if (!fullURL)
return null;
let windowName = provider.origin + name;
let chromeWindow = Services.ww.getWindowByName(windowName, null);
let tabbrowser = chromeWindow && chromeWindow.gBrowser;
if (tabbrowser &&
tabbrowser.selectedBrowser.getAttribute("origin") == provider.origin) {
return tabbrowser.contentWindow;
}
let serviceWindow = contentWindow.openDialog(fullURL, windowName,
"chrome=no,dialog=no" + options);
// Get the newly opened window's containing XUL window
chromeWindow = getChromeWindow(serviceWindow);
// set the window's name and origin attribute on its browser, so that it can
// be found via getWindowByName
chromeWindow.name = windowName;
chromeWindow.gBrowser.selectedBrowser.setAttribute("origin", provider.origin);
// disable global history for the new window.
chromeWindow.gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = false;
// we dont want the default title the browser produces, we'll fixup whenever
// it changes.
serviceWindow.addEventListener("DOMTitleChanged", function() {
let sep = xulWindow.document.documentElement.getAttribute("titlemenuseparator");
xulWindow.document.title = provider.name + sep + serviceWindow.document.title;
});
return serviceWindow;
}

View File

@ -70,34 +70,52 @@ WorkerAPI.prototype = {
this._port.postMessage({topic: "social.cookies-get-response",
data: results});
},
// XXX backwards compat for existing providers, remove these eventually
"social.ambient-notification-area": function (data) {
// replaced with social.user-profile
// handle the provider icon and user profile for the primary provider menu
if (data.background) {
// backwards compat
try {
data.iconURL = /url\((['"]?)(.*)(\1)\)/.exec(data.background)[2];
} catch(e) {
data.iconURL = data.background;
'social.notification-create': function(data) {
let port = this._port;
let provider = this._provider;
let {id, type, icon, body, action, actionArgs} = data;
let alertsService = Cc["@mozilla.org/alerts-service;1"]
.getService(Ci.nsIAlertsService);
function listener(subject, topic, data) {
if (topic === "alertclickcallback") {
// we always post back the click
port.postMessage({topic: "social.notification-action",
data: {id: id,
action: action,
actionArgs: actionArgs}});
switch (action) {
case "link":
// if there is a url, make it open a tab
if (actionArgs.toURL) {
try {
let pUri = Services.io.newURI(provider.origin, null, null);
let nUri = Services.io.newURI(pUri.resolve(actionArgs.toURL),
null, null);
// fixup
if (nUri.scheme != pUri.scheme)
nUri.scheme = pUri.scheme;
if (nUri.prePath == provider.origin) {
let xulWindow = Services.wm.getMostRecentWindow("navigator:browser");
xulWindow.openUILink(nUri.spec);
}
} catch(e) {
Cu.reportError("social.notification-create error: "+e);
}
}
break;
default:
break;
}
}
}
this._provider.updateUserProfile(data);
alertsService.showAlertNotification(icon,
this._provider.name, // title
body,
!!action, // text clickable if an
// action was provided.
null,
listener,
type);
},
"social.ambient-notification-update": function (data) {
// replaced with social.ambient-notification
// handle the provider icon and user profile for the primary provider menu
if (data.background) {
// backwards compat
try {
data.iconURL = /url\((['"]?)(.*)(\1)\)/.exec(data.background)[2];
} catch(e) {
data.iconURL = data.background;
}
}
this._provider.setAmbientNotification(data);
}
}
}

View File

@ -22,6 +22,7 @@ MOCHITEST_BROWSER_FILES = \
browser_workerAPI.js \
worker_social.js \
browser_SocialProvider.js \
browser_notifications.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,128 @@
/* 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/. */
const TEST_PROVIDER_ORIGIN = 'http://example.com';
Cu.import("resource://gre/modules/Services.jsm");
function ensureProvider(workerFunction, cb) {
let manifest = {
origin: TEST_PROVIDER_ORIGIN,
name: "Example Provider",
workerURL: "data:application/javascript," + encodeURI("let run=" + workerFunction.toSource()) + ";run();"
};
ensureSocialEnabled();
SocialService.addProvider(manifest, function (p) {
cb(p);
});
}
function test() {
waitForExplicitFinish();
let cbPostTest = function(cb) {
SocialService.removeProvider(TEST_PROVIDER_ORIGIN, function() {cb()});
};
runTests(tests, undefined, cbPostTest);
}
let tests = {
testNotificationCallback: function(cbnext) {
let run = function() {
let testPort, apiPort;
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "social.initialize") { // this is the api port.
apiPort = port;
} else if (e.data.topic == "test.initialize") { // test suite port.
testPort = port;
apiPort.postMessage({topic: 'social.notification-create',
data: {
id: "the id",
body: 'test notification',
action: 'callback',
actionArgs: { data: "something" }
}
});
} else if (e.data.topic == "social.notification-action") {
let data = e.data.data;
let ok = data && data.action == "callback" &&
data.actionArgs && e.data.data.actionArgs.data == "something";
testPort.postMessage({topic: "test.done", data: ok});
}
}
}
}
ensureProvider(run, function(provider) {
if ('@mozilla.org/system-alerts-service;1' in Cc) {
// This is a platform that has a system-alerts-service so the "toast"
// notifications aren't implemented by XUL making it very tricky to test.
// So just punt.
info("this platform has a system alerts service - test skipped");
cbnext();
return;
}
if (!("@mozilla.org/alerts-service;1" in Cc)) {
info("Alerts service does not exist in this application");
cbnext();
return;
}
var notifier;
try {
notifier = Cc["@mozilla.org/alerts-service;1"].
getService(Ci.nsIAlertsService);
} catch (ex) {
info("Alerts service is not available. (Mac OS X without Growl?)", ex);
cbnext();
return;
}
provider.port.onmessage = function(e) {
if (e.data.topic == "test.done") {
ok(e.data.data, "check the test worked");
cbnext();
}
}
provider.port.postMessage({topic: "test.initialize"});
let count = 0;
// this relies on the implementation of the alerts service.
const ALERT_CHROME_URL = "chrome://global/content/alerts/alert.xul";
const ALERT_TEXT_LABEL_ID = "alertTextLabel";
const ALERT_TITLE_LABEL_ID = "alertTitleLabel";
let findPopup = function() {
let wenum = Services.ww.getWindowEnumerator();
while (wenum.hasMoreElements()) {
let win = wenum.getNext();
if (win.location.href == ALERT_CHROME_URL) {
let doc = win.document;
// We found the window - wait until the ID we care about also exists.
if (doc.getElementById(ALERT_TEXT_LABEL_ID) &&
doc.getElementById(ALERT_TEXT_LABEL_ID).getAttribute("value")) {
// some sanity checking of the content.
is(doc.getElementById(ALERT_TEXT_LABEL_ID).getAttribute("value"),
"test notification",
"check the alert label is correct");
is(doc.getElementById(ALERT_TITLE_LABEL_ID).getAttribute("value"),
"Example Provider",
"check the alert title is correct");
// now click on the text - this should trigger a "callback" into
// our worker which will then send back a "test.done" message.
EventUtils.sendMouseEvent({type: "click"}, ALERT_TEXT_LABEL_ID, win);
return;
}
}
}
if (count++ > 50) {
ok(false, "failed to find the notification popup");
cbnext();
return;
}
executeSoon(findPopup);
}
executeSoon(findPopup);
});
}
};