Bug 898706 remove frameworker and all associated code and tests, r=markh

This commit is contained in:
Shane Caraveo 2016-03-17 16:46:41 -07:00
parent b582bcefb4
commit c3a25096a7
59 changed files with 371 additions and 4099 deletions

View File

@ -69,9 +69,6 @@
config.tryAgainCallback = loadQueryURL;
config.queryURL = url;
break;
case "workerFailure":
config.tryAgainCallback = reloadProvider;
break;
default:
break;
}
@ -104,20 +101,8 @@
}
function reloadProvider() {
// Just incase the current provider *isn't* in a frameworker-error
// state, reload the current one.
let provider = Social._getProviderFromOrigin(config.origin);
provider.reload();
// If the problem is a frameworker-error, it may be that the child
// process crashed - and if that happened, then *all* providers in that
// process will have crashed. However, only the current provider is
// likely to have the error surfaced in the UI - so we reload *all*
// providers that are in a frameworker-error state.
for (let provider of Social.providers) {
if (provider.enabled && provider.errorState == "frameworker-error") {
provider.reload();
}
}
}
parseQueryString();

View File

@ -65,8 +65,6 @@ SocialUI = {
mm.loadFrameScript("chrome://browser/content/social-content.js", true);
Services.obs.addObserver(this, "social:ambient-notification-changed", false);
Services.obs.addObserver(this, "social:profile-changed", false);
Services.obs.addObserver(this, "social:frameworker-error", false);
Services.obs.addObserver(this, "social:providers-changed", false);
Services.obs.addObserver(this, "social:provider-reload", false);
Services.obs.addObserver(this, "social:provider-enabled", false);
@ -76,6 +74,7 @@ SocialUI = {
CustomizableUI.addListener(this);
SocialActivationListener.init();
messageManager.addMessageListener("Social:Notification", this);
// menupopups that list social providers. we only populate them when shown,
// and if it has not been done already.
@ -100,8 +99,6 @@ SocialUI = {
SocialSidebar.saveWindowState();
Services.obs.removeObserver(this, "social:ambient-notification-changed");
Services.obs.removeObserver(this, "social:profile-changed");
Services.obs.removeObserver(this, "social:frameworker-error");
Services.obs.removeObserver(this, "social:providers-changed");
Services.obs.removeObserver(this, "social:provider-reload");
Services.obs.removeObserver(this, "social:provider-enabled");
@ -110,6 +107,7 @@ SocialUI = {
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
CustomizableUI.removeListener(this);
SocialActivationListener.uninit();
messageManager.removeMessageListener("Social:Notification", this);
document.getElementById("viewSidebarMenu").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
document.getElementById("social-statusarea-popup").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
@ -117,6 +115,15 @@ SocialUI = {
this._initialized = false;
},
receiveMessage: function(aMessage) {
if (aMessage.name == "Social:Notification") {
let provider = Social._getProviderFromOrigin(aMessage.data.origin);
if (provider) {
provider.setAmbientNotification(aMessage.data.detail);
}
}
},
observe: function SocialUI_observe(subject, topic, data) {
switch (topic) {
case "social:provider-enabled":
@ -149,17 +156,6 @@ SocialUI = {
case "social:ambient-notification-changed":
SocialStatus.updateButton(data);
break;
case "social:profile-changed":
// make sure anything that happens here only affects the provider for
// which the profile is changing, and that anything we call actually
// needs to change based on profile data.
SocialStatus.updateButton(data);
break;
case "social:frameworker-error":
if (this.enabled && SocialSidebar.provider && SocialSidebar.provider.origin == data) {
SocialSidebar.loadFrameworkerFailure();
}
break;
case "nsPref:changed":
if (data == "social.toast-notifications.enabled") {
SocialSidebar.updateToggleNotifications();
@ -218,17 +214,12 @@ SocialUI = {
get enabled() {
// Returns whether social is enabled *for this window*.
if (this._chromeless || PrivateBrowsingUtils.isWindowPrivate(window))
if (this._chromeless)
return false;
return Social.providers.length > 0;
},
canShareOrMarkPage: function(aURI) {
// Bug 898706 we do not enable social in private sessions since frameworker
// would be shared between private and non-private windows
if (PrivateBrowsingUtils.isWindowPrivate(window))
return false;
return (aURI && (aURI.schemeIs('http') || aURI.schemeIs('https')));
},
@ -287,10 +278,6 @@ SocialActivationListener = {
options = { bypassContentCheck: true, bypassInstallPanel: true };
}
// If we are in PB mode, we silently do nothing (bug 829404 exists to
// do something sensible here...)
if (PrivateBrowsingUtils.isWindowPrivate(window))
return;
Social.installProvider(data, function(manifest) {
Social.activateFromOrigin(manifest.origin, function(provider) {
if (provider.sidebarURL) {
@ -950,16 +937,6 @@ SocialSidebar = {
_unloadTimeoutId: 0,
loadFrameworkerFailure: function() {
if (this.provider && this.provider.errorState == "frameworker-error") {
// we have to explicitly load this error page since it is not being
// handled via the normal error page paths.
let sbrowser = document.getElementById("social-sidebar-browser");
sbrowser.setAttribute("src", "about:socialerror?mode=workerFailure&origin=" +
encodeURIComponent(this.provider.origin));
}
},
_provider: null,
ensureProvider: function() {
if (this._provider)
@ -1241,8 +1218,8 @@ SocialStatus = {
let iconNames = Object.keys(icons);
let notif = icons[iconNames[0]];
// The image and tooltip need to be updated for both
// ambient notification and profile changes.
// The image and tooltip need to be updated for
// ambient notification changes.
let iconURL = provider.icon32URL || provider.iconURL;
let tooltiptext;
if (!notif || !widget.areaType) {

View File

@ -35,6 +35,15 @@ addEventListener("DOMTitleChanged", function(e) {
});
var gHookedWindowCloseForPanelClose = false;
addEventListener("Social:Notification", function(event) {
let frame = docShell.chromeEventHandler;
let origin = frame.getAttribute("origin");
sendAsyncMessage("Social:Notification", {
"origin": origin,
"detail": JSON.parse(event.detail)
});
});
// Error handling class used to listen for network errors in the social frames
// and replace them with a social-specific error page
SocialErrorListener = {
@ -209,6 +218,8 @@ SocialErrorListener = {
onStateChange(aWebProgress, aRequest, aState, aStatus) {
let failure = false;
if ((aState & Ci.nsIWebProgressListener.STATE_IS_REQUEST))
return;
if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
if (aRequest instanceof Ci.nsIHttpChannel) {
try {

View File

@ -246,7 +246,7 @@
<body><![CDATA[
// we always set the src on click if it has not been set for this tab,
// but we only want to open the panel if it was previously annotated.
let openPanel = this.isMarked || aOpenPanel || !this.provider.haveLoggedInUser();
let openPanel = this.isMarked || aOpenPanel;
let src = this.content.getAttribute("src");
if (!src || src == "about:blank") {
this.loadPanel();

View File

@ -23,8 +23,6 @@ support-files =
social_postActivation.html
social_sidebar.html
social_sidebar_empty.html
social_window.html
social_worker.js
unchecked.jpg
[browser_aboutHome_activation.js]
@ -44,12 +42,6 @@ skip-if = (os == 'linux' && e10s) # Bug 1072669 context menu relies on target el
[browser_social_marks.js]
[browser_social_marks_context.js]
[browser_social_multiprovider.js]
[browser_social_multiworker.js]
[browser_social_perwindowPB.js]
[browser_social_sidebar.js]
[browser_social_status.js]
skip-if = true # Bug 1245800 'onoffline' and 'ononline' not defined JS errors
[browser_social_window.js]
[browser_social_workercrash.js]
#skip-if = !crashreporter
skip-if = true # Bug 1060813 - frequent leaks on all platforms

View File

@ -18,7 +18,6 @@ var manifestUpgrade = { // used for testing install
name: "provider 3",
origin: "https://test2.example.com",
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://test2.example.com/browser/browser/base/content/test/general/moz.png",
version: "1.0"
};
@ -211,72 +210,5 @@ var tests = {
Social.uninstallProvider(addonManifest.origin);
});
});
},
testUpgradeProviderFromWorker: function(next) {
// add the provider, change the pref, add it again. The provider at that
// point should be upgraded
let activationURL = manifestUpgrade.origin + "/browser/browser/base/content/test/social/social_activate.html"
ensureEventFired(PopupNotifications.panel, "popupshown").then(() => {
let panel = document.getElementById("servicesInstall-notification");
info("servicesInstall-notification panel opened");
panel.button.click();
});
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
let installFrom = doc.nodePrincipal.origin;
Services.prefs.setCharPref("social.whitelist", installFrom);
let data = {
origin: installFrom,
url: doc.location.href,
manifest: manifestUpgrade,
window: window
}
Social.installProvider(data, function(addonManifest) {
SocialService.enableProvider(addonManifest.origin, function(provider) {
is(provider.manifest.version, 1, "manifest version is 1");
// watch for the provider-update and test the new version
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
if (topic != "provider-update")
return;
// The worker will have reloaded and the current provider instance
// disabled, removed from the provider list. We have a reference
// here, check it is is disabled.
is(provider.enabled, false, "old provider instance is disabled")
is(origin, addonManifest.origin, "provider manifest updated")
SocialService.unregisterProviderListener(providerListener);
// Get the new provider instance, fetch the manifest via workerapi
// and validate that data as well.
let p = Social._getProviderFromOrigin(origin);
is(p.manifest.version, 2, "manifest version is 2");
let port = p.getWorkerPort();
ok(port, "got a new port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "social.manifest":
let manifest = e.data.data;
is(manifest.version, 2, "manifest version is 2");
port.close();
Social.uninstallProvider(origin, function() {
Services.prefs.clearUserPref("social.whitelist");
ensureBrowserTabClosed(tab).then(next);
});
break;
}
}
port.postMessage({topic: "test-init"});
port.postMessage({topic: "manifest-get"});
});
let port = provider.getWorkerPort();
port.postMessage({topic: "worker.update", data: true});
});
});
});
}
}

View File

@ -13,14 +13,12 @@ var manifest = { // normal provider
name: "provider ok",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
var manifest_bad = { // normal provider
name: "provider blocked",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png"
};

View File

@ -6,7 +6,6 @@ var baseURL = "https://example.com/browser/browser/base/content/test/social/";
var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
};
@ -24,11 +23,11 @@ function sendActivationEvent(subframe) {
function promiseShareFrameEvent(iframe, eventName) {
let deferred = Promise.defer();
iframe.addEventListener(eventName, function load() {
iframe.addEventListener(eventName, function load(event) {
info("page load is " + iframe.contentDocument.location.href);
if (iframe.contentDocument.location.href != "data:text/plain;charset=utf8,") {
iframe.removeEventListener(eventName, load, true);
deferred.resolve();
deferred.resolve(event);
}
}, true);
return deferred.promise;
@ -37,7 +36,17 @@ function promiseShareFrameEvent(iframe, eventName) {
function test() {
waitForExplicitFinish();
Services.prefs.setCharPref("social.shareDirectory", activationPage);
let frameScript = "data:,(" + function frame_script() {
addEventListener("OpenGraphData", function (aEvent) {
sendAsyncMessage("sharedata", aEvent.detail);
}, true, true);
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
registerCleanupFunction(function () {
mm.removeDelayedFrameScript(frameScript);
Services.prefs.clearUserPref("social.directories");
Services.prefs.clearUserPref("social.shareDirectory");
Services.prefs.clearUserPref("social.share.activationPanelEnabled");
@ -174,40 +183,34 @@ var tests = {
},
testSharePage: function(next) {
let provider = Social._getProviderFromOrigin(manifest.origin);
let port = provider.getWorkerPort();
ok(port, "provider has a port");
let testTab;
let testIndex = 0;
let testData = corpus[testIndex++];
let mm = getGroupMessageManager("social");
mm.addMessageListener("sharedata", function handler(msg) {
gBrowser.removeTab(testTab);
hasoptions(testData.options, JSON.parse(msg.data));
testData = corpus[testIndex++];
if (testData) {
executeSoon(runOneTest);
} else {
mm.removeMessageListener("sharedata", handler);
SocialService.disableProvider(manifest.origin, next);
}
});
function runOneTest() {
addTab(testData.url, function(tab) {
testTab = tab;
SocialShare.sharePage(manifest.origin);
});
}
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-share-data-message":
gBrowser.removeTab(testTab);
hasoptions(testData.options, e.data.result);
testData = corpus[testIndex++];
if (testData) {
executeSoon(runOneTest);
} else {
SocialService.disableProvider(manifest.origin, next);
}
break;
}
}
port.postMessage({topic: "test-init"});
executeSoon(runOneTest);
},
testShareMicroformats: function(next) {
SocialService.addProvider(manifest, function(provider) {
let port = provider.getWorkerPort();
let target, testTab;
let expecting = JSON.stringify({
@ -226,7 +229,7 @@ var tests = {
}
],
"url": ["https://example.com/"],
"price": ["£29.95"],
"price": ["29.95"],
"review": [{
"value": "4.5 out of 5",
"type": ["h-review"],
@ -255,17 +258,13 @@ var tests = {
}
});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-share-data-message":
is(JSON.stringify(e.data.result), expecting, "microformats data ok");
gBrowser.removeTab(testTab);
SocialService.disableProvider(manifest.origin, next);
break;
}
}
port.postMessage({topic: "test-init"});
let mm = getGroupMessageManager("social");
mm.addMessageListener("sharedata", function handler(msg) {
is(msg.data, expecting, "microformats data ok");
gBrowser.removeTab(testTab);
mm.removeMessageListener("sharedata", handler);
SocialService.disableProvider(manifest.origin, next);
});
let url = "https://example.com/browser/browser/base/content/test/social/microformats.html"
addTab(url, function(tab) {
@ -296,20 +295,13 @@ var tests = {
}, () => {
is(subframe.contentDocument.location.href, activationPage, "activation page loaded");
promiseObserverNotified("social:provider-enabled").then(() => {
let provider = Social._getProviderFromOrigin(manifest.origin);
let port = provider.getWorkerPort();
ok(!!port, "got port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-share-data-message":
ok(true, "share completed");
gBrowser.removeTab(testTab);
SocialService.uninstallProvider(manifest.origin, next);
break;
}
}
port.postMessage({topic: "test-init"});
let mm = getGroupMessageManager("social");
mm.addMessageListener("sharedata", function handler(msg) {
ok(true, "share completed");
gBrowser.removeTab(testTab);
mm.removeMessageListener("sharedata", handler);
SocialService.uninstallProvider(manifest.origin, next);
});
});
sendActivationEvent(subframe);
}, "share panel did not open and load share page");

View File

@ -9,46 +9,30 @@ var manifests = [
name: "provider@example.com",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?example.com",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider@test1",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?test1",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider@test2",
origin: "https://test2.example.com",
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?test2",
workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
var ports = [];
function getProviderPort(provider) {
let port = provider.getWorkerPort();
ok(port, "provider has a port");
ports.push(port);
return port;
}
var chatId = 0;
function openChat(provider, callback) {
function openChat(provider) {
let deferred = Promise.defer();
SocialSidebar.provider = provider;
let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let port = getProviderPort(provider);
port.onmessage = function(e) {
if (e.data.topic == "got-chatbox-message") {
callback();
}
}
let url = chatUrl + "?" + (chatId++);
port.postMessage({topic: "test-init"});
port.postMessage({topic: "test-worker-chat", data: url});
gURLsNotRemembered.push(url);
return port;
let url = chatUrl + "?id=" + (chatId++);
makeChat("normal", "chat " + chatId, (cb) => { deferred.resolve(cb); });
return deferred.promise;
}
function windowHasChats(win) {
@ -59,30 +43,32 @@ function test() {
requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish();
let frameScript = "data:,(" + function frame_script() {
addMessageListener("socialTest-CloseSelf", function(e) {
content.close();
});
addEventListener("visibilitychange", function() {
sendAsyncMessage("chatbox-visibility", content.document.hidden ? "hidden" : "shown");
});
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
let oldwidth = window.outerWidth; // we futz with these, so we restore them
let oldleft = window.screenX;
window.moveTo(0, window.screenY)
let postSubTest = function(cb) {
// ensure ports are closed
for (let port of ports) {
port.close()
ok(port._closed, "port closed");
}
ports = [];
let chats = document.getElementById("pinnedchats");
ok(chats.children.length == 0, "no chatty children left behind");
cb();
};
runSocialTestWithProvider(manifests, function (finishcb) {
ok(Social.enabled, "Social is enabled");
ok(getProviderPort(Social.providers[0]), "provider 0 has port");
ok(getProviderPort(Social.providers[1]), "provider 1 has port");
ok(getProviderPort(Social.providers[2]), "provider 2 has port");
SocialSidebar.show();
runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight);
mm.removeDelayedFrameScript(frameScript);
finishcb();
});
});
@ -90,85 +76,29 @@ function test() {
var tests = {
testOpenCloseChat: function(next) {
let chats = document.getElementById("pinnedchats");
let port = getProviderPort(SocialSidebar.provider);
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
openChatViaUser();
break;
case "got-chatbox-visibility":
let selectedChat = chats.selectedChat || chats.lastElementChild;
if (e.data.result == "hidden") {
ok(true, "chatbox got minimized");
selectedChat.toggle();
} else if (e.data.result == "shown") {
ok(true, "chatbox got shown");
// close it now
let content = selectedChat.content;
content.addEventListener("unload", function chatUnload() {
content.removeEventListener("unload", chatUnload, true);
ok(true, "got chatbox unload on close");
next();
}, true);
selectedChat.close();
}
break;
case "got-chatbox-message":
ok(true, "got chatbox message");
ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
selectedChat.toggle();
break;
}
}
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testWorkerChatWindow: function(next) {
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let chats = document.getElementById("pinnedchats");
let port = getProviderPort(SocialSidebar.provider);
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-chatbox-message":
ok(true, "got a chat window opened");
ok(chats.selectedChat, "chatbox from worker opened");
while (chats.selectedChat) {
chats.selectedChat.close();
}
ok(!chats.selectedChat, "chats are all closed");
gURLsNotRemembered.push(chatUrl);
next();
break;
}
}
ok(!chats.selectedChat, "chats are all closed");
port.postMessage({topic: "test-worker-chat", data: chatUrl});
},
testCloseSelf: function(next) {
let chats = document.getElementById("pinnedchats");
let port = getProviderPort(SocialSidebar.provider);
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
openChatViaUser();
break;
case "got-chatbox-visibility":
is(e.data.result, "shown", "chatbox shown");
port.close(); // don't want any more visibility messages.
let chat = chats.selectedChat;
ok(chat.parentNode, "chat has a parent node before it is closed");
// ask it to close itself.
let mm = chat.content.messageManager;
mm.sendAsyncMessage("Social:CustomEvent", { name: "socialTest-CloseSelf" });
waitForCondition(() => !chat.parentNode, next, "chat should close");
break;
}
}
port.postMessage({topic: "test-init", data: { id: 1 }});
openChat(SocialSidebar.provider).then((cb) => {
let smm = getGroupMessageManager("social");
// Sometimes we'll get the initial shown visibility from opening the chat
// box, so we ensure that we get hidden first.
let minimized = false;
smm.addMessageListener("chatbox-visibility", function handler(msg) {
if (minimized && msg.data == "shown") {
ok(true, "chatbox got shown");
smm.removeMessageListener("chatbox-visibility", handler);
// test the chatbox content closing itself
promiseNodeRemoved(cb).then(next);
let mm = cb.content.messageManager;
mm.sendAsyncMessage("socialTest-CloseSelf", {});
} else if (!minimized && msg.data == "hidden") {
minimized = true;
ok(true, "chatbox got minimized");
// toggle to maximize chat
cb.toggle();
}
});
// toggle to minimize chat
cb.toggle();
});
},
// Check what happens when you close the only visible chat.
@ -178,8 +108,6 @@ var tests = {
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
let port = getProviderPort(SocialSidebar.provider);
port.postMessage({topic: "test-init"});
makeChat("normal", "first chat", function() {
// got the first one.
@ -209,8 +137,6 @@ var tests = {
},
testShowWhenCollapsed: function(next) {
let port = getProviderPort(SocialSidebar.provider);
port.postMessage({topic: "test-init"});
get3ChatsForCollapsing("normal", function(first, second, third) {
let chatbar = getChatBar();
chatbar.showChat(first);
@ -218,91 +144,5 @@ var tests = {
is(second.collapsed || third.collapsed, true, "one of the others should be collapsed");
Task.spawn(closeAllChats).then(next);
});
},
testOnlyOneCallback: function(next) {
let chats = document.getElementById("pinnedchats");
let port = getProviderPort(SocialSidebar.provider);
let numOpened = 0;
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
port.postMessage({topic: "test-chatbox-open"});
break;
case "chatbox-opened":
numOpened += 1;
port.postMessage({topic: "ping"});
break;
case "pong":
executeSoon(function() {
is(numOpened, 1, "only got one open message");
chats.selectedChat.close();
next();
});
}
}
port.postMessage({topic: "test-init", data: { id: 1 }});
},
testMultipleProviderChat: function(next) {
// test incomming chats from all providers
let port0 = openChat(Social.providers[0], function() {
let port1 = openChat(Social.providers[1], function() {
let port2 = openChat(Social.providers[2], function() {
let chats = document.getElementById("pinnedchats");
waitForCondition(() => chats.children.length == Social.providers.length,
function() {
ok(true, "one chat window per provider opened");
// test logout of a single provider
port2.postMessage({topic: "test-logout"});
waitForCondition(() => chats.children.length == Social.providers.length - 1,
function() {
Task.spawn(closeAllChats).then(next);
},
"chat window didn't close");
}, "chat windows did not open");
});
});
});
},
// XXX - note this must be the last test until we restore the login state
// between tests...
testCloseOnLogout: function(next) {
const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let port = SocialSidebar.provider.getWorkerPort();
ports.push(port);
ok(port, "provider has a port");
let opened = false;
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
info("open first chat window");
port.postMessage({topic: "test-worker-chat", data: chatUrl});
break;
case "got-chatbox-message":
ok(true, "got a chat window opened");
if (opened) {
port.postMessage({topic: "test-logout"});
waitForCondition(() => document.getElementById("pinnedchats").firstChild == null,
function() {
next();
},
"chat windows didn't close");
} else {
// open a second chat window
opened = true;
port.postMessage({topic: "test-worker-chat", data: chatUrl+"?id=1"});
}
break;
}
}
// make sure a user profile is set for this provider as chat windows are
// only closed on *change* of the profile data rather than merely setting
// profile data.
port.postMessage({topic: "test-set-profile"});
port.postMessage({topic: "test-init"});
}
}

View File

@ -6,71 +6,10 @@ function isChatFocused(chat) {
return getChatBar()._isChatFocused(chat);
}
function openChatViaSidebarMessage(port, data, callback) {
port.onmessage = function (e) {
if (e.data.topic == "chatbox-opened")
callback();
}
port.postMessage({topic: "test-chatbox-open", data: data});
}
function openChatViaWorkerMessage(port, data, callback) {
// sadly there is no message coming back to tell us when the chat has
// been opened, so we wait until one appears.
let chatbar = getChatBar();
let numExpected = chatbar.childElementCount + 1;
port.postMessage({topic: "test-worker-chat", data: data});
waitForCondition(() => chatbar.childElementCount == numExpected,
function() {
// so the child has been added, but we don't know if it
// has been intialized - re-request it and the callback
// means it's done. Minimized, same as the worker.
chatbar.openChat({
origin: SocialSidebar.provider.origin,
title: SocialSidebar.provider.name,
url: data,
mode: "minimized"
}, function() { callback(); });
},
"No new chat appeared");
}
var isSidebarLoaded = false;
function startTestAndWaitForSidebar(callback) {
let doneCallback;
let port = SocialSidebar.provider.getWorkerPort();
function maybeCallback() {
if (!doneCallback)
callback(port);
doneCallback = true;
}
port.onmessage = function(e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
// if sidebar loaded too fast, we need a backup ping
case "got-isVisible-response":
isSidebarLoaded = true;
maybeCallback();
break;
case "test-init-done":
if (isSidebarLoaded)
maybeCallback();
else
port.postMessage({topic: "test-isVisible"});
break;
}
}
port.postMessage({topic: "test-init"});
}
var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
@ -103,58 +42,17 @@ function test() {
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
runSocialTests(tests, preSubTest, postSubTest, function () {
finishcb();
ensureBrowserTabClosed(tab).then(finishcb);
});
});
}, true);
registerCleanupFunction(function() {
gBrowser.removeTab(tab);
});
}
var tests = {
// In this test the worker asks the sidebar to open a chat. As that means
// we aren't handling user-input we will not focus the chatbar.
// Then we do it again - should still not be focused.
// Then we perform a user-initiated request - it should get focus.
testNoFocusWhenViaWorker: function(next) {
let chatbar = getChatBar();
startTestAndWaitForSidebar(function(port) {
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
ok(true, "got chatbox message");
is(chatbar.childElementCount, 1, "exactly 1 chat open");
let browser = gBrowser.selectedTab.linkedBrowser;
ContentTask.spawn(browser, null, function* () {
is(Services.focus.focusedWindow, content, "tab should still be focused");
}).then(() => {
// re-request the same chat via a message.
openChatViaSidebarMessage(port, {stealFocus: 1}, function() {
is(chatbar.childElementCount, 1, "still exactly 1 chat open");
ContentTask.spawn(browser, null, function* () {
is(Services.focus.focusedWindow, content, "tab should still be focused");
}).then(() => {
// re-request the same chat via user event.
openChatViaUser();
waitForCondition(() => isChatFocused(chatbar.selectedChat), function() {
is(chatbar.childElementCount, 1, "still exactly 1 chat open");
is(chatbar.selectedChat, chatbar.firstElementChild,
"chat should be selected");
next();
}, "chat should be focused");
});
});
});
});
});
},
// In this test we arrange for the sidebar to open the chat via a simulated
// click. This should cause the new chat to be opened and focused.
testFocusWhenViaUser: function(next) {
startTestAndWaitForSidebar(function(port) {
ensureFrameLoaded(document.getElementById("social-sidebar-browser")).then(() => {
let chatbar = getChatBar();
openChatViaUser();
ok(chatbar.firstElementChild, "chat opened");

View File

@ -7,7 +7,6 @@ var SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).So
var manifest = { // used for testing install
name: "provider test1",
origin: "https://test1.example.com",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
markURL: "https://test1.example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
markedIcon: "https://test1.example.com/browser/browser/base/content/test/social/unchecked.jpg",
unmarkedIcon: "https://test1.example.com/browser/browser/base/content/test/social/checked.jpg",
@ -18,9 +17,17 @@ var manifest = { // used for testing install
function test() {
waitForExplicitFinish();
let frameScript = "data:,(" + function frame_script() {
addEventListener("OpenGraphData", function (aEvent) {
sendAsyncMessage("sharedata", aEvent.detail);
}, true, true);
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTests(tests, undefined, undefined, function () {
mm.removeDelayedFrameScript(frameScript);
finishcb();
});
});
@ -30,7 +37,6 @@ var tests = {
testMarkMicroformats: function(next) {
// emulates context menu action using target element, calling SocialMarks.markLink
let provider = Social._getProviderFromOrigin(manifest.origin);
let port = provider.getWorkerPort();
let target, testTab;
// browser_share tests microformats on the full page, this is testing a
@ -50,18 +56,13 @@ var tests = {
}
});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-share-data-message":
is(JSON.stringify(e.data.result), expecting, "microformats data ok");
gBrowser.removeTab(testTab);
port.close();
next();
break;
}
}
port.postMessage({topic: "test-init"});
let mm = getGroupMessageManager("social");
mm.addMessageListener("sharedata", function handler(msg) {
gBrowser.removeTab(testTab);
is(msg.data, expecting, "microformats data ok");
mm.removeMessageListener("sharedata", handler);
next();
});
let url = "https://example.com/browser/browser/base/content/test/social/microformats.html"
addTab(url, function(tab) {

View File

@ -4,23 +4,45 @@
function test() {
waitForExplicitFinish();
let frameScript = "data:,(" + function frame_script() {
addMessageListener("socialTest-CloseSelf", function(e) {
content.close();
});
addEventListener("socialFrameShow", function(e) {
sendAsyncMessage("social-visibility", "shown");
}, false);
addEventListener("socialFrameHide", function(e) {
sendAsyncMessage("social-visibility", "hidden");
}, false);
addMessageListener("socialTest-sendEvent", function(msg) {
let data = msg.data;
let evt = content.document.createEvent("CustomEvent");
evt.initCustomEvent(data.name, true, true, JSON.stringify(data.data));
content.document.documentElement.dispatchEvent(evt);
});
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
// disable transitions for the test
let panel = document.getElementById("social-flyout-panel");
registerCleanupFunction(function () {
panel.removeAttribute("animate");
ensureFrameLoaded(document.getElementById("social-sidebar-browser")).then(() => {
// disable transitions for the test
let panel = document.getElementById("social-flyout-panel");
registerCleanupFunction(function () {
panel.removeAttribute("animate");
});
panel.setAttribute("animate", "false");
runSocialTests(tests, undefined, undefined, finishcb);
});
panel.setAttribute("animate", "false");
runSocialTests(tests, undefined, undefined, finishcb);
});
}
@ -30,109 +52,61 @@ var tests = {
ensureEventFired(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
});
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
port.postMessage({topic: "test-flyout-open"});
break;
case "got-flyout-visibility":
if (e.data.result == "hidden") {
ok(true, "flyout visibility is 'hidden'");
is(panel.state, "closed", "panel really is closed");
port.close();
next();
} else if (e.data.result == "shown") {
ok(true, "flyout visibility is 'shown");
port.postMessage({topic: "test-flyout-close"});
}
break;
case "got-flyout-message":
ok(e.data.result == "ok", "got flyout message");
break;
let sidebar = document.getElementById("social-sidebar-browser")
let mm = getGroupMessageManager("social");
mm.addMessageListener("social-visibility", function handler(msg) {
if (msg.data == "shown") {
sidebar.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-close", data: {} });
} else if (msg.data == "hidden") {
mm.removeMessageListener("social-visibility", handler);
next();
}
}
port.postMessage({topic: "test-init"});
});
sidebar.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
},
testResizeFlyout: function(next) {
let panel = document.getElementById("social-flyout-panel");
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
port.postMessage({topic: "test-flyout-open"});
break;
case "got-flyout-visibility":
if (e.data.result != "shown")
return;
// The width of the flyout should be 400px initially
let iframe = panel.firstChild;
let body = iframe.contentDocument.body;
let cs = iframe.contentWindow.getComputedStyle(body);
is(cs.width, "400px", "should be 400px wide");
is(iframe.boxObject.width, 400, "iframe should now be 400px wide");
is(cs.height, "400px", "should be 400px high");
is(iframe.boxObject.height, 400, "iframe should now be 400px high");
ensureEventFired(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
// The width of the flyout should be 400px initially
let iframe = panel.firstChild;
let body = iframe.contentDocument.body;
let cs = iframe.contentWindow.getComputedStyle(body);
ensureEventFired(iframe.contentWindow, "resize").then(() => {
cs = iframe.contentWindow.getComputedStyle(body);
is(cs.width, "400px", "should be 400px wide");
is(iframe.boxObject.width, 400, "iframe should now be 400px wide");
is(cs.height, "400px", "should be 400px high");
is(iframe.boxObject.height, 400, "iframe should now be 400px high");
is(cs.width, "500px", "should now be 500px wide");
is(iframe.boxObject.width, 500, "iframe should now be 500px wide");
is(cs.height, "500px", "should now be 500px high");
is(iframe.boxObject.height, 500, "iframe should now be 500px high");
panel.hidePopup();
port.close();
next();
});
SocialFlyout.dispatchPanelEvent("socialTest-MakeWider");
break;
}
}
port.postMessage({topic: "test-init"});
ensureEventFired(iframe.contentWindow, "resize").then(() => {
cs = iframe.contentWindow.getComputedStyle(body);
is(cs.width, "500px", "should now be 500px wide");
is(iframe.boxObject.width, 500, "iframe should now be 500px wide");
is(cs.height, "500px", "should now be 500px high");
is(iframe.boxObject.height, 500, "iframe should now be 500px high");
ensureEventFired(panel, "popuphidden").then(next);
panel.hidePopup();
});
SocialFlyout.dispatchPanelEvent("socialTest-MakeWider");
});
let sidebar = document.getElementById("social-sidebar-browser");
sidebar.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
},
testCloseSelf: function(next) {
// window.close is affected by the pref dom.allow_scripts_to_close_windows,
// which defaults to false, but is set to true by the test harness.
// so temporarily set it back.
const ALLOW_SCRIPTS_TO_CLOSE_PREF = "dom.allow_scripts_to_close_windows";
// note clearUserPref doesn't do what we expect, as the test harness itself
// changes the pref value - so clearUserPref resets it to false rather than
// the true setup by the test harness.
let oldAllowScriptsToClose = Services.prefs.getBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF);
Services.prefs.setBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF, false);
let panel = document.getElementById("social-flyout-panel");
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
port.postMessage({topic: "test-flyout-open"});
break;
case "got-flyout-visibility":
if (e.data.result != "shown")
return;
let iframe = panel.firstChild;
ensureEventFired(iframe.contentDocument, "SocialTest-DoneCloseSelf").then(() => {
port.close();
is(panel.state, "closed", "flyout should have closed itself");
Services.prefs.setBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF, oldAllowScriptsToClose);
next();
});
is(panel.state, "open", "flyout should be open");
SocialFlyout.dispatchPanelEvent("socialTest-CloseSelf");
break;
}
}
port.postMessage({topic: "test-init"});
ensureEventFired(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
ensureEventFired(panel, "popuphidden").then(next);
let mm = panel.firstChild.messageManager;
mm.sendAsyncMessage("socialTest-CloseSelf", {});
});
let sidebar = document.getElementById("social-sidebar-browser");
sidebar.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
},
testCloseOnLinkTraversal: function(next) {
@ -146,25 +120,14 @@ var tests = {
}
let panel = document.getElementById("social-flyout-panel");
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
port.postMessage({topic: "test-flyout-open"});
break;
case "got-flyout-visibility":
if (e.data.result == "shown") {
// click on our test link
is(panel.state, "open", "flyout should be open");
gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true);
let iframe = panel.firstChild;
iframe.contentDocument.getElementById('traversal').click();
}
break;
}
}
port.postMessage({topic: "test-init"});
ensureEventFired(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
is(panel.state, "open", "flyout should be open");
gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true);
let iframe = panel.firstChild;
iframe.contentDocument.getElementById('traversal').click();
});
let sidebar = document.getElementById("social-sidebar-browser");
sidebar.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
}
}

View File

@ -9,59 +9,46 @@ function test() {
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
let frameScript = "data:,(" + function frame_script() {
addEventListener("socialFrameShow", function(e) {
sendAsyncMessage("visibility", "shown");
}, false);
addEventListener("socialFrameHide", function(e) {
sendAsyncMessage("visibility", "hidden");
}, false);
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
registerCleanupFunction(function () {
mm.removeDelayedFrameScript(frameScript);
});
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
runSocialTests(tests, undefined, undefined, finishcb);
});
}
var tests = {
testSidebarMessage: function(next) {
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
// The sidebar message will always come first, since it loads by default
ok(true, "got sidebar message");
port.close();
next();
break;
}
};
},
testIsVisible: function(next) {
let port = SocialSidebar.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-isVisible-response":
is(e.data.result, true, "Sidebar should be visible by default");
SocialSidebar.toggleSidebar();
port.close();
next();
}
};
port.postMessage({topic: "test-isVisible"});
let mm = getGroupMessageManager("social");
mm.addMessageListener("visibility", function handler(msg) {
mm.removeMessageListener("visibility", handler);
is(msg.data, "shown", "sidebar is visible");
next();
});
SocialSidebar.show();
},
testIsNotVisible: function(next) {
let port = SocialSidebar.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-isVisible-response":
is(e.data.result, false, "Sidebar should be hidden");
port.close();
next();
}
};
port.postMessage({topic: "test-isVisible"});
let mm = getGroupMessageManager("social");
mm.addMessageListener("visibility", function handler(msg) {
mm.removeMessageListener("visibility", handler);
is(msg.data, "hidden", "sidebar is hidden");
next();
});
SocialSidebar.hide();
}
}

View File

@ -7,7 +7,6 @@ var SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).So
var manifest2 = { // used for testing install
name: "provider test1",
origin: "https://test1.example.com",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
markURL: "https://test1.example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
markedIcon: "https://test1.example.com/browser/browser/base/content/test/social/unchecked.jpg",
unmarkedIcon: "https://test1.example.com/browser/browser/base/content/test/social/checked.jpg",
@ -26,6 +25,21 @@ var manifest3 = { // used for testing install
function test() {
waitForExplicitFinish();
let frameScript = "data:,(" + function frame_script() {
addEventListener("socialFrameShow", function(e) {
sendAsyncMessage("visibility", "shown");
}, false);
addEventListener("socialFrameHide", function(e) {
sendAsyncMessage("visibility", "hidden");
}, false);
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
registerCleanupFunction(function () {
mm.removeDelayedFrameScript(frameScript);
});
runSocialTests(tests, undefined, undefined, finish);
}
@ -134,54 +148,37 @@ var tests = {
let widget = CustomizableUI.getWidget(id);
let btn = widget.forWindow(window).node;
ok(btn, "got a mark button");
let port = provider.getWorkerPort();
ok(port, "got a port");
let ourTab;
ensureEventFired(btn.panel, "popupshown").then(() => {
info("marks panel shown");
let doc = btn.contentDocument;
let unmarkBtn = doc.getElementById("unmark");
ok(unmarkBtn, "testMarkPanel - got the panel unmark button");
EventUtils.sendMouseEvent({type: "click"}, unmarkBtn, btn.contentWindow);
});
ensureEventFired(btn.panel, "popuphidden").then(() => {
ensureBrowserTabClosed(ourTab).then(() => {
ok(btn.disabled, "button is disabled");
next();
});
});
// verify markbutton is disabled when there is no browser url
ok(btn.disabled, "button is disabled");
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
ourTab = tab;
ok(!btn.disabled, "button is enabled");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
ok(true, "test-init-done received");
ok(provider.profile.userName, "profile was set by test worker");
// first click marks the page, second click opens the page. We have to
// synthesize so the command event happens
EventUtils.synthesizeMouseAtCenter(btn, {});
// wait for the button to be marked, click to open panel
is(btn.panel.state, "closed", "panel should not be visible yet");
waitForCondition(() => btn.isMarked, function() {
EventUtils.synthesizeMouseAtCenter(btn, {});
}, "button is marked");
break;
case "got-social-panel-visibility":
ok(true, "got the panel message " + e.data.result);
if (e.data.result == "shown") {
// unmark the page via the button in the page
ensureFrameLoaded(btn.content).then(() => {
let doc = btn.contentDocument;
let unmarkBtn = doc.getElementById("unmark");
ok(unmarkBtn, "testMarkPanel - got the panel unmark button");
EventUtils.sendMouseEvent({type: "click"}, unmarkBtn, btn.contentWindow);
});
} else {
// page should no longer be marked
port.close();
waitForCondition(() => !btn.isMarked, function() {
// cleanup after the page has been unmarked
ensureBrowserTabClosed(tab).then(() => {
ok(btn.disabled, "button is disabled");
next();
});
}, "button unmarked");
}
break;
}
};
port.postMessage({topic: "test-init"});
// first click marks the page, second click opens the page. We have to
// synthesize so the command event happens
EventUtils.synthesizeMouseAtCenter(btn, {});
// wait for the button to be marked, click to open panel
is(btn.panel.state, "closed", "panel should not be visible yet");
waitForCondition(() => btn.isMarked, function() {
EventUtils.synthesizeMouseAtCenter(btn, {});
}, "button is marked");
});
},
@ -218,67 +215,6 @@ var tests = {
});
},
testMarkPanelLoggedOut: function(next) {
// click on panel to open and wait for visibility
let provider = Social._getProviderFromOrigin(manifest2.origin);
ok(provider.enabled, "provider is enabled");
let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
let widget = CustomizableUI.getWidget(id);
let btn = widget.forWindow(window).node;
ok(btn, "got a mark button");
let port = provider.getWorkerPort();
ok(port, "got a port");
// verify markbutton is disabled when there is no browser url
ok(btn.disabled, "button is disabled");
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
ok(!btn.disabled, "button is enabled");
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
ok(true, "test-init-done received");
ok(provider.profile.userName, "profile was set by test worker");
port.postMessage({topic: "test-logout"});
waitForCondition(() => !provider.profile.userName,
function() {
// when the provider has not indicated to us that a user is
// logged in, the first click opens the page.
EventUtils.synthesizeMouseAtCenter(btn, {});
},
"profile was unset by test worker");
break;
case "got-social-panel-visibility":
ok(true, "got the panel message " + e.data.result);
if (e.data.result == "shown") {
// our test marks the page during the load event (see
// social_mark.html) regardless of login state, unmark the page
// via the button in the page
ensureFrameLoaded(btn.content).then(() => {
let doc = btn.contentDocument;
let unmarkBtn = doc.getElementById("unmark");
ok(unmarkBtn, "testMarkPanelLoggedOut - got the panel unmark button");
EventUtils.sendMouseEvent({type: "click"}, unmarkBtn, btn.contentWindow);
});
} else {
// page should no longer be marked
port.close();
waitForCondition(() => !btn.isMarked, function() {
// cleanup after the page has been unmarked
ensureBrowserTabClosed(tab).then(() => {
ok(btn.disabled, "button is disabled");
next();
});
}, "button unmarked");
}
break;
}
};
port.postMessage({topic: "test-init"});
});
},
testButtonOnDisable: function(next) {
// enable the provider now
let provider = Social._getProviderFromOrigin(manifest2.origin);

View File

@ -17,20 +17,19 @@ var gProviders = [
name: "provider 1",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider 2",
origin: "https://test2.example.com",
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
var tests = {
testProviderSwitch: function(next) {
let sbrowser = document.getElementById("social-sidebar-browser");
let menu = document.getElementById("social-statusarea-popup");
let button = document.getElementById("social-sidebar-button");
function checkProviderMenu(selectedProvider) {
@ -43,36 +42,32 @@ var tests = {
}
// the menu is not populated until onpopupshowing, so wait for popupshown
function theTest() {
menu.removeEventListener("popupshown", theTest, true);
ensureEventFired(menu, "popupshown").then(()=>{
menu.hidePopup(); // doesn't need visibility
// first provider should already be visible in the sidebar
is(Social.providers[0].origin, SocialSidebar.provider.origin, "selected provider in sidebar");
checkProviderMenu(Social.providers[0]);
// Now activate "provider 2"
onSidebarLoad(function() {
ensureEventFired(sbrowser, "load").then(()=>{
checkUIStateMatchesProvider(Social.providers[1]);
onSidebarLoad(function() {
ensureEventFired(sbrowser, "load").then(()=>{
checkUIStateMatchesProvider(Social.providers[0]);
next();
});
// show the menu again so the menu is updated with the correct commands
function doClick() {
ensureEventFired(menu, "popupshown").then(()=>{
// click on the provider menuitem to switch providers
let el = menu.getElementsByAttribute("origin", Social.providers[0].origin);
is(el.length, 1, "selected provider menu item exists");
EventUtils.synthesizeMouseAtCenter(el[0], {});
}
menu.addEventListener("popupshown", doClick, true);
});
EventUtils.synthesizeMouseAtCenter(button, {});
});
SocialSidebar.provider = Social.providers[1];
};
menu.addEventListener("popupshown", theTest, true);
});
EventUtils.synthesizeMouseAtCenter(button, {});
}
}
@ -81,16 +76,3 @@ function checkUIStateMatchesProvider(provider) {
// Sidebar
is(document.getElementById("social-sidebar-browser").getAttribute("src"), provider.sidebarURL, "side bar URL is set");
}
function onSidebarLoad(callback) {
let sbrowser = document.getElementById("social-sidebar-browser");
sbrowser.addEventListener("load", function load(evt) {
if (evt.target != sbrowser.contentDocument) {
return;
}
sbrowser.removeEventListener("load", load, true);
// give the load a chance to finish before pulling the rug (ie. calling
// next)
executeSoon(callback);
}, true);
}

View File

@ -1,70 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
waitForExplicitFinish();
runSocialTestWithProvider(gProviders, function (finishcb) {
runSocialTests(tests, undefined, undefined, function() {
finishcb();
});
});
}
var gProviders = [
{
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider 2",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
var tests = {
testWorkersAlive: function(next) {
// verify we can get a message from all providers that are enabled
let messageReceived = 0;
function oneWorkerTest(provider) {
let port = provider.getWorkerPort();
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
ok(true, "got message from provider " + provider.name);
port.close();
messageReceived++;
break;
}
};
port.postMessage({topic: "test-init"});
}
for (let p of Social.providers) {
oneWorkerTest(p);
}
waitForCondition(() => messageReceived == Social.providers.length,
next, "received messages from all workers",
/* increase timeout because shutting down a child process is slow */ 60);
},
testMultipleWorkerEnabling: function(next) {
// test that all workers are enabled when we allow multiple workers
for (let p of Social.providers) {
ok(p.enabled, "provider enabled");
let port = p.getWorkerPort();
ok(port, "worker enabled");
port.close();
}
next();
}
}

View File

@ -1,100 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function openTab(win, url, callback) {
let newTab = win.gBrowser.addTab(url);
let tabBrowser = win.gBrowser.getBrowserForTab(newTab);
tabBrowser.addEventListener("load", function tabLoadListener() {
tabBrowser.removeEventListener("load", tabLoadListener, true);
win.gBrowser.selectedTab = newTab;
callback(newTab);
}, true)
}
// Tests for per-window private browsing.
function openPBWindow(callback) {
let w = OpenBrowserWindow({private: true});
w.addEventListener("load", function loadListener() {
w.removeEventListener("load", loadListener);
openTab(w, "http://example.com", function() {
callback(w);
});
});
}
function postAndReceive(port, postTopic, receiveTopic, callback) {
port.onmessage = function(e) {
if (e.data.topic == receiveTopic)
callback();
}
port.postMessage({topic: postTopic});
}
function test() {
waitForExplicitFinish();
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
statusURL: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
markURL: "https://example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
runSocialTestWithProvider(manifest, function (finishcb) {
openTab(window, "http://example.com", function(newTab) {
runSocialTests(tests, undefined, undefined, function() {
window.gBrowser.removeTab(newTab);
finishcb();
});
});
});
}
var tests = {
testPrivateBrowsing: function(next) {
let port = SocialSidebar.provider.getWorkerPort();
ok(port, "provider has a port");
postAndReceive(port, "test-init", "test-init-done", function() {
// social features should all be enabled in the existing window.
info("checking main window ui");
ok(window.SocialUI.enabled, "social is enabled in normal window");
checkSocialUI(window);
// open a new private-window
openPBWindow(function(pbwin) {
// The provider should remain alive.
postAndReceive(port, "ping", "pong", function() {
// the new window should have no social features at all.
info("checking private window ui");
ok(!pbwin.SocialUI.enabled, "social is disabled in a PB window");
checkSocialUI(pbwin);
// but they should all remain enabled in the initial window
info("checking main window ui");
ok(window.SocialUI.enabled, "social is still enabled in normal window");
checkSocialUI(window);
// Check that the status button is disabled on the private
// browsing window and not on the normal window.
let id = SocialStatus._toolbarHelper.idFromOrigin("https://example.com");
let widget = CustomizableUI.getWidget(id);
ok(widget.forWindow(pbwin).node.disabled, "status button disabled on private window");
ok(!widget.forWindow(window).node.disabled, "status button enabled on normal window");
// Check that the mark button is disabled on the private
// browsing window and not on the normal window.
id = SocialMarks._toolbarHelper.idFromOrigin("https://example.com");
widget = CustomizableUI.getWidget(id);
ok(widget.forWindow(pbwin).node.disabled, "mark button disabled on private window");
ok(!widget.forWindow(window).node.disabled, "mark button enabled on normal window");
// that's all folks...
pbwin.close();
next();
})
});
});
},
}

View File

@ -8,7 +8,6 @@ var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
@ -24,7 +23,6 @@ function test() {
function doTest() {
ok(SocialSidebar.canShow, "social sidebar should be able to be shown");
ok(!SocialSidebar.opened, "social sidebar should not be open by default");
SocialSidebar.show();
let command = document.getElementById("Social:ToggleSidebar");
let sidebar = document.getElementById("social-sidebar-box");
@ -57,17 +55,14 @@ function doTest() {
// First check the the sidebar is initially visible, and loaded
ok(!command.hidden, "toggle command should be visible");
checkShown(true);
browser.addEventListener("socialFrameHide", function sidebarhide() {
browser.removeEventListener("socialFrameHide", sidebarhide);
ensureEventFired(browser, "socialFrameShow").then(function sidebarhide() {
checkShown(false);
checkShown(true);
browser.addEventListener("socialFrameShow", function sidebarshow() {
browser.removeEventListener("socialFrameShow", sidebarshow);
ensureEventFired(browser, "socialFrameHide").then(function sidebarshow() {
checkShown(true);
checkShown(false);
// disable social.
SocialService.disableProvider(SocialSidebar.provider.origin, function() {
@ -83,17 +78,5 @@ function doTest() {
info("Toggling sidebar back on");
SocialSidebar.toggleSidebar();
});
// use port messaging to ensure that the sidebar is both loaded and
// ready before we run other tests
let port = SocialSidebar.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-sidebar-message":
ok(true, "sidebar is loaded and ready");
SocialSidebar.toggleSidebar();
}
};
SocialSidebar.show();
}

View File

@ -8,13 +8,11 @@ var manifest = { // builtin provider
name: "provider example.com",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
var manifest2 = { // used for testing install
name: "provider test1",
origin: "https://test1.example.com",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
statusURL: "https://test1.example.com/browser/browser/base/content/test/social/social_panel.html",
iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
version: "1.0"
@ -40,6 +38,27 @@ function openWindowAndWaitForInit(callback) {
function test() {
waitForExplicitFinish();
let frameScript = "data:,(" + function frame_script() {
addEventListener("socialFrameShow", function(e) {
sendAsyncMessage("visibility", "shown");
}, false);
addEventListener("socialFrameHide", function(e) {
sendAsyncMessage("visibility", "hidden");
}, false);
addMessageListener("socialTest-sendEvent", function(msg) {
let data = msg.data;
let evt = content.document.createEvent("CustomEvent");
evt.initCustomEvent(data.name, true, true, JSON.stringify(data.data));
content.document.documentElement.dispatchEvent(evt);
});
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
registerCleanupFunction(function () {
mm.removeDelayedFrameScript(frameScript);
});
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTests(tests, undefined, undefined, function () {
Services.prefs.clearUserPref("social.remote-install.enabled");
@ -123,44 +142,31 @@ var tests = {
counter: 1
};
// Disable the transition
let panel = document.getElementById("social-notification-panel");
panel.setAttribute("animate", "false");
// click on panel to open and wait for visibility
let provider = Social._getProviderFromOrigin(manifest2.origin);
let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
let widget = CustomizableUI.getWidget(id);
let btn = widget.forWindow(window).node;
ok(btn, "got a status button");
let port = provider.getWorkerPort();
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
ok(true, "test-init-done received");
ok(provider.profile.userName, "profile was set by test worker");
btn.click();
break;
case "got-social-panel-visibility":
ok(true, "got the panel message " + e.data.result);
if (e.data.result == "shown") {
panel.hidePopup();
panel.removeAttribute("animate");
} else {
port.postMessage({topic: "test-ambient-notification", data: icon});
port.close();
waitForCondition(function() { return btn.getAttribute("badge"); },
function() {
is(btn.style.listStyleImage, "url(\"" + icon.iconURL + "\")", "notification icon updated");
next();
}, "button updated by notification");
}
break;
}
};
port.postMessage({topic: "test-init"});
// Disable the transition
let panel = document.getElementById("social-notification-panel");
panel.setAttribute("animate", "false");
ensureEventFired(panel, "popupshown").then(() => {
ensureFrameLoaded(panel.firstChild).then(() => {
let mm = panel.firstChild.messageManager;
mm.sendAsyncMessage("socialTest-sendEvent", { name: "Social:Notification", data: icon });
waitForCondition(function() { return btn.getAttribute("badge"); },
function() {
is(btn.style.listStyleImage, "url(\"" + icon.iconURL + "\")", "notification icon updated");
panel.hidePopup();
}, "button updated by notification");
});
});
ensureEventFired(panel, "popuphidden").then(() => {
panel.removeAttribute("animate");
next();
});
btn.click(); // open the panel
},
testPanelOffline: function(next) {
@ -173,8 +179,6 @@ var tests = {
ok(btn, "got a status button");
let frameId = btn.getAttribute("notificationFrameId");
let frame = document.getElementById(frameId);
let port = provider.getWorkerPort();
port.postMessage({topic: "test-init"});
goOffline().then(function() {
info("testing offline error page");
@ -183,8 +187,16 @@ var tests = {
ensureEventFired(panel, "popupshown").then(() => {
ensureFrameLoaded(frame).then(() => {
is(frame.contentDocument.documentURI.indexOf("about:socialerror?mode=tryAgainOnly"), 0, "social error page is showing "+frame.contentDocument.documentURI);
panel.hidePopup();
goOnline().then(next);
// We got our error page, reset to avoid test leak.
ensureEventFired(frame, "load").then(() => {
is(frame.contentDocument.documentURI, "about:blank", "closing error panel");
ensureEventFired(panel, "popuphidden").then(next);
panel.hidePopup();
});
goOnline().then(() => {
info("resetting error panel");
frame.setAttribute("src", "about:blank");
});
});
});
// reload after going offline, wait for unload to open panel

View File

@ -55,13 +55,11 @@ var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
var manifest2 = { // used for testing install
name: "provider test1",
origin: "https://test1.example.com",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html",
iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
};

View File

@ -1,157 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This tests our recovery if a child content process hosting providers
// crashes.
// A content script we inject into one of our browsers
const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/content/test/social/social_crash_content_helper.js";
var {getFrameWorkerHandle} = Cu.import("resource://gre/modules/FrameWorker.jsm", {});
var {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
function test() {
waitForExplicitFinish();
// We need to ensure all our workers are in the same content process.
Services.prefs.setIntPref("dom.ipc.processCount", 1);
// This test generates many uncaught promises that should not cause failures.
Promise.Debugging.clearUncaughtErrorObservers();
runSocialTestWithProvider(gProviders, function (finishcb) {
runSocialTests(tests, undefined, undefined, function() {
Services.prefs.clearUserPref("dom.ipc.processCount");
finishcb();
});
});
}
var gProviders = [
{
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider 2",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
var tests = {
testCrash: function(next) {
// open the sidebar, then crash the child.
let sbrowser = document.getElementById("social-sidebar-browser");
onSidebarLoad(function() {
// get the browser element for our provider.
let fw = getFrameWorkerHandle(gProviders[0].workerURL);
fw.port.close();
fw._worker.browserPromise.then(browser => {
let mm = browser.messageManager;
mm.loadFrameScript(TEST_CONTENT_HELPER, false);
// add an observer for the crash - after it sees the crash we attempt
// a reload.
let observer = new crashObserver(function() {
info("Saw the process crash.")
Services.obs.removeObserver(observer, 'ipc:content-shutdown');
// Add another sidebar load listener - it should be the error page.
onSidebarLoad(function() {
ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?mode=workerFailure")==0, "is on social error page");
// after reloading, the sidebar should reload
onSidebarLoad(function() {
// now ping both workers - they should both be alive.
ensureWorkerLoaded(gProviders[0], function() {
ensureWorkerLoaded(gProviders[1], function() {
// and we are done!
next();
});
});
});
// click the try-again button.
sbrowser.contentDocument.getElementById("btnTryAgain").click();
});
});
Services.obs.addObserver(observer, 'ipc:content-shutdown', false);
// and cause the crash.
mm.sendAsyncMessage("social-test:crash");
});
})
SocialSidebar.show();
},
}
function onSidebarLoad(callback) {
let sbrowser = document.getElementById("social-sidebar-browser");
sbrowser.addEventListener("load", function load() {
sbrowser.removeEventListener("load", load, true);
callback();
}, true);
}
function ensureWorkerLoaded(manifest, callback) {
let fw = getFrameWorkerHandle(manifest.workerURL);
// once the worker responds to a ping we know it must be up.
let port = fw.port;
port.onmessage = function(msg) {
if (msg.data.topic == "pong") {
port.close();
callback();
}
}
port.postMessage({topic: "ping"})
}
// More duplicated code from browser_thumbnails_brackground_crash.
// Bug 915518 exists to unify these.
// This observer is needed so we can clean up all evidence of the crash so
// the testrunner thinks things are peachy.
var crashObserver = function(callback) {
this.callback = callback;
}
crashObserver.prototype = {
observe: function(subject, topic, data) {
is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
ok(subject instanceof Components.interfaces.nsIPropertyBag2,
'Subject implements nsIPropertyBag2.');
// we might see this called as the process terminates due to previous tests.
// We are only looking for "abnormal" exits...
if (!subject.hasKey("abnormal")) {
info("This is a normal termination and isn't the one we are looking for...");
return;
}
var dumpID;
if ('nsICrashReporter' in Components.interfaces) {
dumpID = subject.getPropertyAsAString('dumpID');
ok(dumpID, "dumpID is present and not an empty string");
}
if (dumpID) {
var minidumpDirectory = getMinidumpDirectory();
removeFile(minidumpDirectory, dumpID + '.dmp');
removeFile(minidumpDirectory, dumpID + '.extra');
}
this.callback();
}
}
function getMinidumpDirectory() {
var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
dir.append("minidumps");
return dir;
}
function removeFile(directory, filename) {
var file = directory.clone();
file.append(filename);
if (file.exists()) {
file.remove(false);
}
}

View File

@ -89,7 +89,7 @@ function runSocialTestWithProvider(manifest, callback, finishcallback) {
SessionStore.setWindowValue(window, "socialSidebar", "");
for (let i = 0; i < manifests.length; i++) {
let m = manifests[i];
for (let what of ['sidebarURL', 'workerURL', 'iconURL', 'shareURL', 'markURL']) {
for (let what of ['sidebarURL', 'iconURL', 'shareURL', 'markURL']) {
if (m[what]) {
yield promiseSocialUrlNotRemembered(m[what]);
}
@ -194,8 +194,7 @@ function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
return;
}
let [name, func] = result.value;
// We run on a timeout as the frameworker also makes use of timeouts, so
// this helps keep the debug messages sane.
// We run on a timeout to help keep the debug messages sane.
executeSoon(function() {
function cleanupAndRunNextTest() {
info("sub-test " + name + " complete");
@ -441,9 +440,6 @@ function get3ChatsForCollapsing(mode, cb) {
// ensure a second can be created fully visible but a third can not - then
// create the other 2. first will will be collapsed, second fully visible
// and the third also visible and the "selected" one.
// To make our life easier we don't go via the worker and ports so we get
// more control over creation *and* to make the code much simpler. We
// assume the worker/port stuff is individually tested above.
let chatbar = getChatBar();
let chatWidth = undefined;
let num = 0;
@ -508,7 +504,7 @@ function makeChat(mode, uniqueid, cb) {
chatbox.content.messageManager.sendAsyncMessage("Social:SetDocumentTitle", {
title: uniqueid
});
cb();
cb(chatbox);
});
}
@ -630,15 +626,15 @@ function getPopupWidth() {
return popup.parentNode.getBoundingClientRect().width + margins;
}
function promiseCloseChat(chat) {
function promiseNodeRemoved(aNode) {
let deferred = Promise.defer();
let parent = chat.parentNode;
let parent = aNode.parentNode;
let observer = new MutationObserver(function onMutatations(mutations) {
for (let mutation of mutations) {
for (let i = 0; i < mutation.removedNodes.length; i++) {
let node = mutation.removedNodes.item(i);
if (node != chat) {
if (node != aNode) {
continue;
}
observer.disconnect();
@ -647,10 +643,15 @@ function promiseCloseChat(chat) {
}
});
observer.observe(parent, {childList: true});
chat.close();
return deferred.promise;
}
function promiseCloseChat(chat) {
let promise = promiseNodeRemoved(chat);
chat.close();
return promise;
}
function closeAllChats() {
let chatbar = getChatBar();
while (chatbar.selectedChat) {

View File

@ -7,7 +7,7 @@
<img class="photo" src="https://example.com/someimage.jpg" />
<p class="description">The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It's a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.</p>
<a class="url" href="https://example.com/">More info about the Raspberry Pi</a>
<p class="price">£29.95</p>
<p class="price">29.95</p>
<p class="review hreview"><span id="test-review" class="rating">4.5</span> out of 5</p>
<p>Categories:
<a rel="tag" href="https://example.com/wiki/computer" class="category">Computer</a>,

View File

@ -2,11 +2,8 @@
<head>
<meta charset="utf-8">
<script>
var shareData;
addEventListener("OpenGraphData", function(e) {
shareData = JSON.parse(e.detail);
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "share-data-message", result: shareData});
// frame scripts handle test data
// share windows self-close
history.back(); // bug 1042991, ensure history is available
window.close();

View File

@ -15,7 +15,6 @@ var data = {
"iconURL": "chrome://branding/content/icon16.png",
"icon32URL": "chrome://branding/content/favicon32.png",
"icon64URL": "chrome://branding/content/icon64.png",
"workerURL": "/browser/browser/base/content/test/social/social_worker.js",
"shareURL": "/browser/browser/base/content/test/social/share.html"
}

View File

@ -13,7 +13,6 @@ var data = {
// at least one of these must be defined
"sidebarURL": "/browser/browser/base/content/test/social/social_sidebar.html",
"workerURL": "/browser/browser/base/content/test/social/social_worker.js",
"statusURL": "/browser/browser/base/content/test/social/social_panel.html",
"postActivationURL": "/browser/browser/base/content/test/social/social_postActivation.html",

View File

@ -1,26 +1,9 @@
<html>
<head>
<meta charset="utf-8">
<script>
function pingWorker() {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "chatbox-message", result: "ok"});
}
window.addEventListener("socialFrameShow", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "chatbox-visibility", result: "shown"});
}, false);
window.addEventListener("socialFrameHide", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "chatbox-visibility", result: "hidden"});
}, false);
window.addEventListener("socialTest-CloseSelf", function(e) {
window.close();
}, false);
</script>
<title>test chat window</title>
</head>
<body onload="pingWorker();">
<body>
<p>This is a test social chat window.</p>
<!-- a couple of input fields to help with focus testing -->
<input id="input1"/>

View File

@ -2,18 +2,6 @@
<head>
<meta charset="utf-8">
<script>
function pingWorker() {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "flyout-message", result: "ok"});
}
window.addEventListener("socialFrameShow", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "flyout-visibility", result: "shown"});
}, false);
window.addEventListener("socialFrameHide", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "flyout-visibility", result: "hidden"});
}, false);
window.addEventListener("socialTest-MakeWider", function(e) {
document.body.setAttribute("style", "width: 500px; height: 500px; margin: 0; overflow: hidden;");
document.body.offsetWidth; // force a layout flush
@ -29,7 +17,7 @@
}, false);
</script>
</head>
<body style="width: 400px; height: 400px; margin: 0; overflow: hidden;" onload="pingWorker();">
<body style="width: 400px; height: 400px; margin: 0; overflow: hidden;">
<p>This is a test social flyout panel.</p>
<a id="traversal" href="https://test.example.com">test link</a>
</body>

View File

@ -4,14 +4,6 @@
<link id="siteicon" rel="icon" href="./icon.png"/>
<title>Demo Mark Window</title>
<script type="text/javascript">
window.addEventListener("socialFrameShow", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "status-panel-visibility", result: "shown"});
}, false);
window.addEventListener("socialFrameHide", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "status-panel-visibility", result: "hidden"});
}, false);
function updateTextNode(parent, text) {
var textNode = parent.childNodes[0];
@ -34,8 +26,6 @@
shareData = JSON.parse(e.detail);
updateTextNode(document.getElementById("shared"), shareData.url);
socialMarkUpdate(true);
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "share-data-message", result: shareData});
});
</script>
</head>

View File

@ -1,24 +1,8 @@
<html>
<head>
<meta charset="utf-8">
<script>
function pingWorker() {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "panel-message",
result: "ok",
location: window.location.href});
}
window.addEventListener("socialFrameShow", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "status-panel-visibility", result: "shown"});
}, false);
window.addEventListener("socialFrameHide", function(e) {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "status-panel-visibility", result: "hidden"});
}, false);
</script>
</head>
<body onload="pingWorker();">
<body>
<p>This is a test social panel.</p>
</body>
</html>

View File

@ -2,40 +2,15 @@
<head>
<meta charset="utf-8">
<script>
var testwindow;
function pingWorker() {
var port = navigator.mozSocial.getWorker().port;
port.onmessage = function(e) {
var topic = e.data.topic;
switch (topic) {
case "test-flyout-open":
navigator.mozSocial.openPanel("social_flyout.html");
break;
case "test-flyout-close":
navigator.mozSocial.closePanel();
break;
case "test-chatbox-open":
var url = "social_chat.html";
var data = e.data.data;
if (data && data.id) {
url = url + "?id="+data.id;
}
navigator.mozSocial.openChatWindow(url, function(success) {
port.postMessage({topic: "chatbox-opened",
result: success ? "ok" : "failed"});
});
break;
case "test-isVisible":
port.postMessage({topic: "test-isVisible-response",
result: navigator.mozSocial.isVisible});
break;
}
}
port.postMessage({topic: "sidebar-message", result: "ok"});
}
addEventListener("test-flyout-open", function(e) {
navigator.mozSocial.openPanel("social_flyout.html");
}, false);
addEventListener("test-flyout-close", function(e) {
navigator.mozSocial.closePanel();
}, false);
</script>
</head>
<body onload="pingWorker();">
<body>
<p>This is a test social sidebar.</p>
<button id="chat-opener" onclick="navigator.mozSocial.openChatWindow('./social_chat.html');"/>
</body>

View File

@ -1,17 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<script>
function pingWorker() {
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "service-window-message",
location: window.location.href,
result: "ok"
});
}
</script>
</head>
<body onload="pingWorker();">
<p>This is a test social service window.</p>
</body>
</html>

View File

@ -1,137 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var testPort, sidebarPort, apiPort, updatingManifest=false;
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function onMessage(event) {
let topic = event.data.topic;
switch (topic) {
case "test-init":
testPort = port;
port.postMessage({topic: "test-init-done"});
break;
case "ping":
port.postMessage({topic: "pong"});
break;
case "test-logout":
apiPort.postMessage({topic: "social.user-profile", data: {}});
break;
case "sidebar-message":
sidebarPort = port;
if (testPort && event.data.result == "ok")
testPort.postMessage({topic:"got-sidebar-message"});
break;
case "service-window-message":
testPort.postMessage({topic:"got-service-window-message",
location: event.data.location});
break;
case "service-window-closed-message":
testPort.postMessage({topic:"got-service-window-closed-message"});
break;
case "test-service-window":
sidebarPort.postMessage({topic:"test-service-window"});
break;
case "test-service-window-twice":
sidebarPort.postMessage({topic:"test-service-window-twice"});
break;
case "test-service-window-twice-result":
testPort.postMessage({topic: "test-service-window-twice-result", result: event.data.result })
break;
case "test-close-service-window":
sidebarPort.postMessage({topic:"test-close-service-window"});
break;
case "panel-message":
if (testPort && event.data.result == "ok")
testPort.postMessage({topic:"got-panel-message",
location: event.data.location
});
break;
case "status-panel-visibility":
testPort.postMessage({topic:"got-social-panel-visibility", result: event.data.result });
break;
case "test-chatbox-open":
sidebarPort.postMessage(event.data);
break;
case "chatbox-opened":
testPort.postMessage(event.data);
break;
case "chatbox-message":
testPort.postMessage({topic:"got-chatbox-message", result: event.data.result});
break;
case "chatbox-visibility":
testPort.postMessage({topic:"got-chatbox-visibility", result: event.data.result});
break;
case "test-flyout-open":
sidebarPort.postMessage({topic:"test-flyout-open"});
break;
case "flyout-message":
testPort.postMessage({topic:"got-flyout-message", result: event.data.result});
break;
case "flyout-visibility":
testPort.postMessage({topic:"got-flyout-visibility", result: event.data.result});
break;
case "test-flyout-close":
sidebarPort.postMessage({topic:"test-flyout-close"});
break;
case "test-worker-chat":
apiPort.postMessage({topic: "social.request-chat", data: event.data.data });
break;
case "social.initialize":
// This is the workerAPI port, respond and set up a notification icon.
// For multiprovider tests, we support acting like different providers
// based on the domain we load from.
apiPort = port;
// purposely fall through and set the profile on initialization
case "test-set-profile":
let profile;
if (location.href.indexOf("https://test1.example.com") == 0) {
profile = {
portrait: "https://test1.example.com/portrait.jpg",
userName: "tester",
displayName: "Test1 User",
};
} else {
profile = {
portrait: "https://example.com/portrait.jpg",
userName: "trickster",
displayName: "Kuma Lisa",
profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
};
}
apiPort.postMessage({topic: "social.user-profile", data: profile});
break;
case "test-ambient-notification":
apiPort.postMessage({topic: "social.ambient-notification", data: event.data.data});
break;
case "test-isVisible":
sidebarPort.postMessage({topic: "test-isVisible"});
break;
case "test-isVisible-response":
testPort.postMessage({topic: "got-isVisible-response", result: event.data.result});
break;
case "share-data-message":
if (testPort)
testPort.postMessage({topic:"got-share-data-message", result: event.data.result});
break;
case "manifest-get":
apiPort.postMessage({topic: 'social.manifest-get'});
break;
case "worker.update":
updatingManifest = true;
apiPort.postMessage({topic: 'social.manifest-get'});
break;
case "social.manifest":
if (updatingManifest) {
updatingManifest = false;
event.data.data.version = "2.0";
apiPort.postMessage({topic: 'social.manifest-set', data: event.data.data});
} else if (testPort) {
testPort.postMessage({topic:"social.manifest", data: event.data.data});
}
break;
}
}
}

View File

@ -28,8 +28,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
"resource://gre/modules/PageMetadata.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
@ -95,7 +93,7 @@ this.Social = {
// Retrieve the current set of providers, and set the current provider.
SocialService.getOrderedProviderList(function (providers) {
Social._updateProviderCache(providers);
Social._updateWorkerState(SocialService.enabled);
Social._updateEnabledState(SocialService.enabled);
deferred.resolve(false);
});
} else {
@ -113,15 +111,14 @@ this.Social = {
}
if (topic == "provider-enabled") {
Social._updateProviderCache(providers);
Social._updateWorkerState(true);
Social._updateEnabledState(true);
Services.obs.notifyObservers(null, "social:" + topic, origin);
return;
}
if (topic == "provider-disabled") {
// a provider was removed from the list of providers, that does not
// affect worker state for other providers
// a provider was removed from the list of providers, update states
Social._updateProviderCache(providers);
Social._updateWorkerState(providers.length > 0);
Social._updateEnabledState(providers.length > 0);
Services.obs.notifyObservers(null, "social:" + topic, origin);
return;
}
@ -136,7 +133,7 @@ this.Social = {
return deferred.promise;
},
_updateWorkerState: function(enable) {
_updateEnabledState: function(enable) {
for (let p of Social.providers) {
p.enabled = enable;
}
@ -275,9 +272,6 @@ function CreateSocialStatusWidget(aId, aProvider) {
node.setAttribute("oncommand", "SocialStatus.showPopup(this);");
node.setAttribute("constrain-size", "true");
if (PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView))
node.setAttribute("disabled", "true");
return node;
}
});

View File

@ -1,215 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=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/.
*/
/*
* This is an implementation of a "Shared Worker" using a remote browser
* in the hidden DOM window. This is the implementation that lives in the
* "chrome process". See FrameWorkerContent for code that lives in the
* "content" process and which sets up a sandbox for the worker.
*/
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/MessagePortBase.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
"resource://gre/modules/SocialService.jsm");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const HTML_NS = "http://www.w3.org/1999/xhtml";
this.EXPORTED_SYMBOLS = ["getFrameWorkerHandle"];
var workerCache = {}; // keyed by URL.
var _nextPortId = 1;
// Retrieves a reference to a WorkerHandle associated with a FrameWorker and a
// new ClientPort.
this.getFrameWorkerHandle =
function getFrameWorkerHandle(url, clientWindow, name, origin, exposeLocalStorage = false) {
// prevent data/about urls - see bug 891516
if (['http', 'https'].indexOf(Services.io.newURI(url, null, null).scheme) < 0)
throw new Error("getFrameWorkerHandle requires http/https urls");
// See if we already have a worker with this URL.
let existingWorker = workerCache[url];
if (!existingWorker) {
// create a remote browser and _Worker object - this will message the
// remote browser to do the content side of things.
let browserPromise = makeRemoteBrowser();
let options = { url: url, name: name, origin: origin,
exposeLocalStorage: exposeLocalStorage };
existingWorker = workerCache[url] = new _Worker(browserPromise, options);
}
// message the content so it can establish a new connection with the worker.
let portid = _nextPortId++;
existingWorker.browserPromise.then(browser => {
browser.messageManager.sendAsyncMessage("frameworker:connect",
{ portId: portid });
}).then(null, (ex) => {
Cu.reportError("Could not send frameworker:connect: " + ex);
});
// return the pseudo worker object.
let port = new ParentPort(portid, existingWorker.browserPromise, clientWindow);
existingWorker.ports.set(portid, port);
return new WorkerHandle(port, existingWorker);
};
// A "_Worker" is an internal representation of a worker. It's never returned
// directly to consumers.
function _Worker(browserPromise, options) {
this.browserPromise = browserPromise;
this.options = options;
this.ports = new Map();
browserPromise.then(browser => {
browser.addEventListener("oop-browser-crashed", () => {
Cu.reportError("FrameWorker remote process crashed");
notifyWorkerError(options.origin);
});
let mm = browser.messageManager;
// execute the content script and send the message to bootstrap the content
// side of the world.
mm.loadFrameScript("resource://gre/modules/FrameWorkerContent.js", true);
mm.sendAsyncMessage("frameworker:init", this.options);
mm.addMessageListener("frameworker:port-message", this);
mm.addMessageListener("frameworker:notify-worker-error", this);
});
}
_Worker.prototype = {
// Message handler.
receiveMessage: function(msg) {
switch (msg.name) {
case "frameworker:port-message":
let port = this.ports.get(msg.data.portId);
port._onmessage(msg.data.data);
break;
case "frameworker:notify-worker-error":
notifyWorkerError(msg.data.origin);
break;
}
}
}
// This WorkerHandle is exposed to consumers - it has the new port instance
// the consumer uses to communicate with the worker.
// public methods/properties on WorkerHandle should conform to the SharedWorker
// api - currently that's just .port and .terminate()
function WorkerHandle(port, worker) {
this.port = port;
this._worker = worker;
}
WorkerHandle.prototype = {
// A method to terminate the worker. The worker spec doesn't define a
// callback to be made in the worker when this happens, so we just kill the
// browser element.
terminate: function terminate() {
let url = this._worker.options.url;
if (!(url in workerCache)) {
// terminating an already terminated worker - ignore it
return;
}
delete workerCache[url];
// close all the ports we have handed out.
for (let [portid, port] of this._worker.ports) {
port.close();
}
this._worker.ports.clear();
this._worker.ports = null;
this._worker.browserPromise.then(browser => {
let iframe = browser.ownerDocument.defaultView.frameElement;
iframe.parentNode.removeChild(iframe);
});
// wipe things out just incase other reference have snuck out somehow...
this._worker.browserPromise = null;
this._worker = null;
}
};
// The port that lives in the parent chrome process. The other end of this
// port is the "client" port in the content process, which itself is just a
// shim which shuttles messages to/from the worker itself.
function ParentPort(portid, browserPromise, clientWindow) {
this._clientWindow = clientWindow;
this._browserPromise = browserPromise;
AbstractPort.call(this, portid);
}
ParentPort.prototype = {
__proto__: AbstractPort.prototype,
_portType: "parent",
_dopost: function(data) {
this._browserPromise.then(browser => {
browser.messageManager.sendAsyncMessage("frameworker:port-message", data);
});
},
_onerror: function(err) {
Cu.reportError("FrameWorker: Port " + this + " handler failed: " + err + "\n" + err.stack);
},
_JSONParse: function(data) {
if (this._clientWindow) {
return XPCNativeWrapper.unwrap(this._clientWindow).JSON.parse(data);
}
return JSON.parse(data);
},
close: function() {
if (this._closed) {
return; // already closed.
}
// a leaky abstraction due to the worker spec not specifying how the
// other end of a port knows it is closing.
this.postMessage({topic: "social.port-closing"});
AbstractPort.prototype.close.call(this);
this._clientWindow = null;
// this._pendingMessagesOutgoing should still be drained, as a closed
// port will still get "entangled" quickly enough to deliver the messages.
}
}
// Make the <browser remote="true"> element that hosts the worker.
function makeRemoteBrowser() {
let deferred = Promise.defer();
let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
// Create a HTML iframe with a chrome URL, then this can host the browser.
let iframe = hiddenDoc.createElementNS(HTML_NS, "iframe");
iframe.setAttribute("src", "chrome://global/content/mozilla.xhtml");
iframe.addEventListener("load", function onLoad() {
iframe.removeEventListener("load", onLoad, true);
let browser = iframe.contentDocument.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("remote", "true");
iframe.contentDocument.documentElement.appendChild(browser);
deferred.resolve(browser);
}, true);
hiddenDoc.documentElement.appendChild(iframe);
return deferred.promise;
}
function notifyWorkerError(origin) {
// Try to retrieve the worker's associated provider, if it has one, to set its
// error state.
SocialService.getProvider(origin, function (provider) {
if (provider)
provider.errorState = "frameworker-error";
Services.obs.notifyObservers(null, "social:frameworker-error", origin);
});
}

View File

@ -1,414 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=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/.
*/
"use strict";
// the singleton frameworker, available for (ab)use by tests.
var frameworker;
(function () { // bug 673569 workaround :(
/*
* This is an implementation of a "Shared Worker" using a remote <browser>
* element hosted in the hidden DOM window. This is the "content script"
* implementation - it runs in the child process but has chrome permissions.
*
* A set of new APIs that simulate a shared worker are introduced to a sandbox
* by cloning methods from the worker's JS origin.
*/
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/MessagePortBase.jsm");
function navigate(url) {
let webnav = docShell.QueryInterface(Ci.nsIWebNavigation);
webnav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
}
/**
* FrameWorker
*
* A FrameWorker is a <browser> element hosted by the hiddenWindow.
* It is constructed with the URL of some JavaScript that will be run in
* the context of the browser; the script does not have a full DOM but is
* instead run in a sandbox that has a select set of methods cloned from the
* URL's domain.
*/
function FrameWorker(url, name, origin, exposeLocalStorage) {
this.url = url;
this.name = name || url;
this.ports = new Map(); // all unclosed ports, including ones yet to be entangled
this.loaded = false;
this.origin = origin;
this._injectController = null;
this.exposeLocalStorage = exposeLocalStorage;
this.load();
}
FrameWorker.prototype = {
load: function FrameWorker_loadWorker() {
this._injectController = function(doc, topic, data) {
if (!doc.defaultView || doc.defaultView != content) {
return;
}
this._maybeRemoveInjectController();
try {
this.createSandbox();
} catch (e) {
Cu.reportError("FrameWorker: failed to create sandbox for " + this.url + ". " + e);
}
}.bind(this);
Services.obs.addObserver(this._injectController, "document-element-inserted", false);
navigate(this.url);
},
_maybeRemoveInjectController: function() {
if (this._injectController) {
Services.obs.removeObserver(this._injectController, "document-element-inserted");
this._injectController = null;
}
},
createSandbox: function createSandbox() {
let workerWindow = content;
let sandbox = new Cu.Sandbox(workerWindow);
// copy the window apis onto the sandbox namespace only functions or
// objects that are naturally a part of an iframe, I'm assuming they are
// safe to import this way
let workerAPI = ['WebSocket', 'atob', 'btoa',
'clearInterval', 'clearTimeout', 'dump',
'setInterval', 'setTimeout', 'XMLHttpRequest',
'FileReader', 'Blob', 'EventSource', 'indexedDB',
'location', 'Worker'];
// Only expose localStorage if the caller opted-in
if (this.exposeLocalStorage) {
workerAPI.push('localStorage');
}
// Bug 798660 - XHR, WebSocket and Worker have issues in a sandbox and need
// to be unwrapped to work
let needsWaive = ['XMLHttpRequest', 'WebSocket', 'Worker' ];
// Methods need to be bound with the proper |this|.
let needsBind = ['atob', 'btoa', 'dump', 'setInterval', 'clearInterval',
'setTimeout', 'clearTimeout'];
workerAPI.forEach(function(fn) {
try {
if (needsWaive.indexOf(fn) != -1)
sandbox[fn] = XPCNativeWrapper.unwrap(workerWindow)[fn];
else if (needsBind.indexOf(fn) != -1)
sandbox[fn] = workerWindow[fn].bind(workerWindow);
else
sandbox[fn] = workerWindow[fn];
}
catch(e) {
Cu.reportError("FrameWorker: failed to import API "+fn+"\n"+e+"\n");
}
});
// the "navigator" object in a worker is a subset of the full navigator;
// specifically, just the interfaces 'NavigatorID' and 'NavigatorOnLine'
let navigator = Cu.cloneInto({
// interface NavigatorID
appName: workerWindow.navigator.appName,
appVersion: workerWindow.navigator.appVersion,
platform: workerWindow.navigator.platform,
userAgent: workerWindow.navigator.userAgent,
}, sandbox);
Object.defineProperty(Cu.waiveXrays(navigator), 'onLine', {
configurable: true, enumerable: true,
get: Cu.exportFunction(() => workerWindow.navigator.onLine, sandbox)
});
sandbox.navigator = navigator;
// Our importScripts function needs to 'eval' the script code from inside
// a function, but using eval() directly means functions in the script
// don't end up in the global scope.
sandbox._evalInSandbox = function(s, url) {
let baseURI = Services.io.newURI(workerWindow.location.href, null, null);
Cu.evalInSandbox(s, sandbox, "1.8",
Services.io.newURI(url, null, baseURI).spec, 1);
};
// and we delegate ononline and onoffline events to the worker.
// See http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#workerglobalscope
workerWindow.addEventListener('offline', function fw_onoffline(event) {
Cu.evalInSandbox("onoffline();", sandbox);
}, false);
workerWindow.addEventListener('online', function fw_ononline(event) {
Cu.evalInSandbox("ononline();", sandbox);
}, false);
sandbox._postMessage = function fw_postMessage(d, o) {
workerWindow.postMessage(d, o)
};
sandbox._addEventListener = function fw_addEventListener(t, l, c) {
workerWindow.addEventListener(t, l, c)
};
// Note we don't need to stash |sandbox| in |this| as the unload handler
// has a reference in its closure, so it can't die until that handler is
// removed - at which time we've explicitly killed it anyway.
let worker = this;
workerWindow.addEventListener("DOMContentLoaded", function loadListener() {
workerWindow.removeEventListener("DOMContentLoaded", loadListener);
// no script, error out now rather than creating ports, etc
let scriptText = workerWindow.document.body.textContent.trim();
if (!scriptText) {
Cu.reportError("FrameWorker: Empty worker script received");
notifyWorkerError();
return;
}
// now that we've got the script text, remove it from the DOM;
// no need for it to keep occupying memory there
workerWindow.document.body.textContent = "";
// the content has loaded the js file as text - first inject the magic
// port-handling code into the sandbox.
try {
Services.scriptloader.loadSubScript("resource://gre/modules/MessagePortBase.jsm", sandbox);
Services.scriptloader.loadSubScript("resource://gre/modules/MessagePortWorker.js", sandbox);
}
catch (e) {
Cu.reportError("FrameWorker: Error injecting port code into content side of the worker: " + e + "\n" + e.stack);
notifyWorkerError();
return;
}
// and wire up the client message handling.
try {
initClientMessageHandler();
}
catch (e) {
Cu.reportError("FrameWorker: Error setting up event listener for chrome side of the worker: " + e + "\n" + e.stack);
notifyWorkerError();
return;
}
// Now get the worker js code and eval it into the sandbox
try {
Cu.evalInSandbox(scriptText, sandbox, "1.8", workerWindow.location.href, 1);
} catch (e) {
Cu.reportError("FrameWorker: Error evaluating worker script for " + worker.name + ": " + e + "; " +
(e.lineNumber ? ("Line #" + e.lineNumber) : "") +
(e.stack ? ("\n" + e.stack) : ""));
notifyWorkerError();
return;
}
// so finally we are ready to roll - dequeue all the pending connects
worker.loaded = true;
for (let [,port] of worker.ports) { // enumeration is in insertion order
if (!port._entangled) {
try {
port._createWorkerAndEntangle(worker);
}
catch(e) {
Cu.reportError("FrameWorker: Failed to entangle worker port: " + e + "\n" + e.stack);
}
}
}
});
// the 'unload' listener cleans up the worker and the sandbox. This
// will be triggered by the window unloading as part of shutdown or reload.
workerWindow.addEventListener("unload", function unloadListener() {
workerWindow.removeEventListener("unload", unloadListener);
worker.loaded = false;
// No need to close ports - the worker probably wont see a
// social.port-closing message and certainly isn't going to have time to
// do anything if it did see it.
worker.ports.clear();
if (sandbox) {
Cu.nukeSandbox(sandbox);
sandbox = null;
}
});
},
};
const FrameWorkerManager = {
init: function() {
// first, setup the docShell to disable some types of content
docShell.allowAuth = false;
docShell.allowPlugins = false;
docShell.allowImages = false;
docShell.allowMedia = false;
docShell.allowWindowControl = false;
addMessageListener("frameworker:init", this._onInit);
addMessageListener("frameworker:connect", this._onConnect);
addMessageListener("frameworker:port-message", this._onPortMessage);
addMessageListener("frameworker:cookie-get", this._onCookieGet);
},
// This new frameworker is being created. This should only be called once.
_onInit: function(msg) {
let {url, name, origin, exposeLocalStorage} = msg.data;
frameworker = new FrameWorker(url, name, origin, exposeLocalStorage);
},
// A new port is being established for this frameworker.
_onConnect: function(msg) {
let port = new ClientPort(msg.data.portId);
frameworker.ports.set(msg.data.portId, port);
if (frameworker.loaded && !frameworker.reloading)
port._createWorkerAndEntangle(frameworker);
},
// A message related to a port.
_onPortMessage: function(msg) {
// find the "client" port for this message and have it post it into
// the worker.
let port = frameworker.ports.get(msg.data.portId);
port._dopost(msg.data);
},
_onCookieGet: function(msg) {
sendAsyncMessage("frameworker:cookie-get-response", content.document.cookie);
},
};
FrameWorkerManager.init();
// This is the message listener for the chrome side of the world - ie, the
// port that exists with chrome permissions inside the <browser/> (ie, in the
// content process if a remote browser is used).
function initClientMessageHandler() {
function _messageHandler(event) {
// We will ignore all messages destined for otherType.
let data = event.data;
let portid = data.portId;
let port;
if (!data.portFromType || data.portFromType !== "worker") {
// this is a message posted by ourself so ignore it.
return;
}
switch (data.portTopic) {
// No "port-create" here - client ports are created explicitly.
case "port-connection-error":
// onconnect failed, we cannot connect the port, the worker has
// become invalid
notifyWorkerError();
break;
case "port-close":
// the worker side of the port was closed, so close this side too.
port = frameworker.ports.get(portid);
if (!port) {
// port already closed (which will happen when we call port.close()
// below - the worker side will send us this message but we've
// already closed it.)
return;
}
frameworker.ports.delete(portid);
port.close();
break;
case "port-message":
// the client posted a message to this worker port.
port = frameworker.ports.get(portid);
if (!port) {
return;
}
port._onmessage(data.data);
break;
default:
break;
}
}
// this can probably go once debugged and working correctly!
function messageHandler(event) {
try {
_messageHandler(event);
} catch (ex) {
Cu.reportError("FrameWorker: Error handling client port control message: " + ex + "\n" + ex.stack);
}
}
content.addEventListener('message', messageHandler);
}
/**
* ClientPort
*
* Client side of the entangled ports. This is just a shim that sends messages
* back to the "parent" port living in the chrome process.
*
* constructor:
* @param {integer} portid
*/
function ClientPort(portid) {
// messages posted to the worker before the worker has loaded.
this._pendingMessagesOutgoing = [];
AbstractPort.call(this, portid);
}
ClientPort.prototype = {
__proto__: AbstractPort.prototype,
_portType: "client",
// _entangled records if the port has ever been entangled (although may be
// reset during a reload).
_entangled: false,
_createWorkerAndEntangle: function fw_ClientPort_createWorkerAndEntangle(worker) {
this._entangled = true;
this._postControlMessage("port-create");
for (let message of this._pendingMessagesOutgoing) {
this._dopost(message);
}
this._pendingMessagesOutgoing = [];
// The client side of the port might have been closed before it was
// "entangled" with the worker, in which case we need to disentangle it
if (this._closed) {
worker.ports.delete(this._portid);
}
},
_dopost: function fw_ClientPort_dopost(data) {
if (!this._entangled) {
this._pendingMessagesOutgoing.push(data);
} else {
content.postMessage(data, "*");
}
},
// we are just a "shim" - any messages we get are just forwarded back to
// the chrome parent process.
_onmessage: function(data) {
sendAsyncMessage("frameworker:port-message", {portId: this._portid, data: data});
},
_onerror: function fw_ClientPort_onerror(err) {
Cu.reportError("FrameWorker: Port " + this + " handler failed: " + err + "\n" + err.stack);
},
close: function fw_ClientPort_close() {
if (this._closed) {
return; // already closed.
}
// a leaky abstraction due to the worker spec not specifying how the
// other end of a port knows it is closing.
this.postMessage({topic: "social.port-closing"});
AbstractPort.prototype.close.call(this);
// this._pendingMessagesOutgoing should still be drained, as a closed
// port will still get "entangled" quickly enough to deliver the messages.
}
}
function notifyWorkerError() {
sendAsyncMessage("frameworker:notify-worker-error", {origin: frameworker.origin});
}
}());

View File

@ -1,110 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Code that is shared between clients and workers.
this.EXPORTED_SYMBOLS = ["AbstractPort"];
this.AbstractPort = function AbstractPort(portid) {
this._portid = portid;
this._handler = undefined;
this._closed = false;
// pending messages sent to this port before it has a message handler.
this._pendingMessagesIncoming = [];
};
AbstractPort.prototype = {
_portType: null, // set by a subclass.
// abstract methods to be overridden.
_dopost: function fw_AbstractPort_dopost(data) {
throw new Error("not implemented");
},
_onerror: function fw_AbstractPort_onerror(err) {
throw new Error("not implemented");
},
// and concrete methods shared by client and workers.
toString: function fw_AbstractPort_toString() {
return "MessagePort(portType='" + this._portType + "', portId="
+ this._portid + (this._closed ? ", closed=true" : "") + ")";
},
_JSONParse: function fw_AbstractPort_JSONParse(data) {
return JSON.parse(data);
},
_postControlMessage: function fw_AbstractPort_postControlMessage(topic, data) {
let postData = {
portTopic: topic,
portId: this._portid,
portFromType: this._portType,
data: data
};
this._dopost(postData);
},
_onmessage: function fw_AbstractPort_onmessage(data) {
// See comments in postMessage below - we work around a cloning
// issue by using JSON for these messages.
// Further, we allow the workers to override exactly how the JSON parsing
// is done - we try and do such parsing in the client window so things
// like prototype overrides on Array work as expected.
if (!this._handler) {
this._pendingMessagesIncoming.push(data);
} else {
data = this._JSONParse(data);
try {
this._handler({
data: data,
__exposedProps__: {
data: 'r'
}
});
} catch (ex) {
this._onerror(ex);
}
}
},
set onmessage(handler) { // property setter for onmessage
this._handler = handler;
while (this._pendingMessagesIncoming.length) {
this._onmessage(this._pendingMessagesIncoming.shift());
}
},
get onmessage() {
return this._handler;
},
/**
* postMessage
*
* Send data to the onmessage handler on the other end of the port. The
* data object should have a topic property.
*
* @param {jsobj} data
*/
postMessage: function fw_AbstractPort_postMessage(data) {
if (this._closed) {
throw new Error("port is closed");
}
// There seems to be an issue with passing objects directly and letting
// the structured clone thing work - we sometimes get:
// [Exception... "The object could not be cloned." code: "25" nsresult: "0x80530019 (DataCloneError)"]
// The best guess is that this happens when funky things have been added to the prototypes.
// It doesn't happen for our "control" messages, only in messages from
// content - so we explicitly use JSON on these messages as that avoids
// the problem.
this._postControlMessage("port-message", JSON.stringify(data));
},
close: function fw_AbstractPort_close() {
if (this._closed) {
return; // already closed.
}
this._postControlMessage("port-close");
// and clean myself up.
this._handler = null;
this._pendingMessagesIncoming = [];
this._closed = true;
}
};

View File

@ -1,110 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Note: this is never instantiated in chrome - the source is sent across
// to the worker and it is evaluated there and created in response to a
// port-create message we send.
function WorkerPort(portid) {
AbstractPort.call(this, portid);
}
WorkerPort.prototype = {
__proto__: AbstractPort.prototype,
_portType: "worker",
_dopost: function fw_WorkerPort_dopost(data) {
// postMessage is injected into the sandbox.
_postMessage(data, "*");
},
_onerror: function fw_WorkerPort_onerror(err) {
// We throw an object that "derives" from the exception, but with
// a more detailed message.
throw {message: "Port " + this + " handler failed: " + err.message, __proto__: err};
}
}
function importScripts() {
for (var i=0; i < arguments.length; i++) {
// load the url *synchronously*
var scriptURL = arguments[i];
var xhr = new XMLHttpRequest();
xhr.open('GET', scriptURL, false);
xhr.onreadystatechange = function(aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 0) {
_evalInSandbox(xhr.responseText, scriptURL);
}
else {
throw new Error("Unable to importScripts ["+scriptURL+"], status " + xhr.status)
}
}
};
xhr.send(null);
}
}
// This function is magically injected into the sandbox and used there.
// Thus, it is only ever dealing with "worker" ports.
function __initWorkerMessageHandler() {
let ports = {}; // all "worker" ports currently alive, keyed by ID.
function messageHandler(event) {
// We will ignore all messages destined for otherType.
let data = event.data;
let portid = data.portId;
let port;
if (!data.portFromType || data.portFromType === "worker") {
// this is a message posted by ourself so ignore it.
return;
}
switch (data.portTopic) {
case "port-create":
// a new port was created on the "client" side - create a new worker
// port and store it in the map
port = new WorkerPort(portid);
ports[portid] = port;
// and call the "onconnect" handler.
try {
onconnect({ports: [port]});
} catch(e) {
// we have a bad worker and cannot continue, we need to signal
// an error
port._postControlMessage("port-connection-error", JSON.stringify(e.toString()));
throw e;
}
break;
case "port-close":
// the client side of the port was closed, so close this side too.
port = ports[portid];
if (!port) {
// port already closed (which will happen when we call port.close()
// below - the client side will send us this message but we've
// already closed it.)
return;
}
delete ports[portid];
port.close();
break;
case "port-message":
// the client posted a message to this worker port.
port = ports[portid];
if (!port) {
// port must be closed - this shouldn't happen!
return;
}
port._onmessage(data.data);
break;
default:
break;
}
}
// addEventListener is injected into the sandbox.
_addEventListener('message', messageHandler);
}
__initWorkerMessageHandler();

View File

@ -72,8 +72,7 @@ function injectController(doc, topic, data) {
}
// we always handle window.close on social content, even if they are not
// "enabled". "enabled" is about the worker state and a provider may
// still be in e.g. the share panel without having their worker enabled.
// "enabled".
hookWindowCloseForPanelClose(window);
SocialService.getProvider(doc.nodePrincipal.origin, function(provider) {
@ -98,47 +97,7 @@ function attachToWindow(provider, targetWindow) {
return;
}
let port = provider.workerURL ? provider.getWorkerPort(targetWindow) : null;
let mozSocialObj = {
// Use a method for backwards compat with existing providers, but we
// should deprecate this in favor of a simple .port getter.
getWorker: {
enumerable: true,
configurable: true,
writable: true,
value: function() {
// We do a bunch of hacky stuff to expose this API to content without
// relying on ChromeObjectWrapper functionality that is now unsupported.
// The content-facing API here should really move to JS-Implemented
// WebIDL.
let workerAPI = Cu.cloneInto({
port: {
postMessage: port.postMessage.bind(port),
close: port.close.bind(port),
toString: port.toString.bind(port)
}
}, targetWindow, {cloneFunctions: true});
// Jump through hoops to define the accessor property.
let abstractPortPrototype = Object.getPrototypeOf(Object.getPrototypeOf(port));
let desc = Object.getOwnPropertyDescriptor(port.__proto__.__proto__, 'onmessage');
desc.get = Cu.exportFunction(desc.get.bind(port), targetWindow);
desc.set = Cu.exportFunction(desc.set.bind(port), targetWindow);
Object.defineProperty(workerAPI.wrappedJSObject.port, 'onmessage', desc);
return workerAPI;
}
},
hasBeenIdleFor: {
enumerable: true,
configurable: true,
writable: true,
value: function() {
return false;
}
},
openChatWindow: {
enumerable: true,
configurable: true,
@ -238,15 +197,6 @@ function attachToWindow(provider, targetWindow) {
delete targetWindow.navigator.wrappedJSObject.mozSocial;
return targetWindow.navigator.wrappedJSObject.mozSocial = contentObj;
});
if (port) {
targetWindow.addEventListener("unload", function () {
// We want to close the port, but also want the target window to be
// able to use the port during an unload event they setup - so we
// set a timer which will fire after the unload events have all fired.
schedule(function () { port.close(); });
});
}
}
function hookWindowCloseForPanelClose(targetWindow) {

View File

@ -16,8 +16,6 @@ const ADDON_TYPE_SERVICE = "service";
const ID_SUFFIX = "@services.mozilla.org";
const STRING_TYPE_NAME = "type.%ID%.name";
XPCOMUtils.defineLazyModuleGetter(this, "getFrameWorkerHandle", "resource://gre/modules/FrameWorker.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WorkerAPI", "resource://gre/modules/WorkerAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MozSocialAPI", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "closeAllChatWindows", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
@ -505,7 +503,7 @@ this.SocialService = {
},
_manifestFromData: function(type, data, installOrigin) {
let featureURLs = ['workerURL', 'sidebarURL', 'shareURL', 'statusURL', 'markURL'];
let featureURLs = ['sidebarURL', 'shareURL', 'statusURL', 'markURL'];
let resolveURLs = featureURLs.concat(['postActivationURL']);
if (type == 'directory' || type == 'internal') {
@ -665,8 +663,6 @@ this.SocialService = {
// overwrite the existing provider then notify the front end so it can
// handle any reload that might be necessary.
if (ActiveProviders.has(manifest.origin)) {
// unload the worker prior to replacing the provider instance, also
// ensures the workerapi instance is terminated.
let provider = SocialServiceInternal.providers[manifest.origin];
provider.enabled = false;
provider = new SocialProvider(manifest);
@ -687,8 +683,7 @@ this.SocialService = {
};
/**
* The SocialProvider object represents a social provider, and allows
* access to its FrameWorker (if it has one).
* The SocialProvider object represents a social provider.
*
* @constructor
* @param {jsobj} object representing the manifest file describing this provider
@ -709,7 +704,6 @@ function SocialProvider(input) {
this.iconURL = input.iconURL;
this.icon32URL = input.icon32URL;
this.icon64URL = input.icon64URL;
this.workerURL = input.workerURL;
this.sidebarURL = input.sidebarURL;
this.shareURL = input.shareURL;
this.statusURL = input.statusURL;
@ -724,11 +718,6 @@ function SocialProvider(input) {
this.errorState = null;
this.frecency = 0;
// this provider has localStorage access in the worker if listed in the
// whitelist
let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
this.blessed = whitelist.indexOf(this.origin) >= 0;
try {
this.domain = etld.getBaseDomainFromHost(originUri.host);
} catch(e) {
@ -745,8 +734,7 @@ SocialProvider.prototype = {
Services.obs.notifyObservers(null, "social:provider-reload", this.origin);
},
// Provider enabled/disabled state. Disabled providers do not have active
// connections to their FrameWorkers.
// Provider enabled/disabled state.
_enabled: false,
get enabled() {
return this._enabled;
@ -776,81 +764,14 @@ SocialProvider.prototype = {
return undefined;
},
// Reference to a workerAPI object for this provider. Null if the provider has
// no FrameWorker, or is disabled.
workerAPI: null,
// Contains information related to the user's profile. Populated by the
// workerAPI via updateUserProfile.
// Properties:
// iconURL, portrait, userName, displayName, profileURL
// See https://github.com/mozilla/socialapi-dev/blob/develop/docs/socialAPI.md
// A value of null or an empty object means 'user not logged in'.
// A value of undefined means the service has not yet told us the status of
// the profile (ie, the service is still loading/initing, or the provider has
// no FrameWorker)
// This distinction might be used to cache certain data between runs - eg,
// browser-social.js caches the notification icons so they can be displayed
// quickly at startup without waiting for the provider to initialize -
// 'undefined' means 'ok to use cached values' versus 'null' meaning 'cached
// values aren't to be used as the user is logged out'.
profile: undefined,
// Map of objects describing the provider's notification icons, whose
// properties include:
// name, iconURL, counter, contentPanel
// See https://developer.mozilla.org/en-US/docs/Social_API
ambientNotificationIcons: null,
// Called by the workerAPI to update our profile information.
updateUserProfile: function(profile) {
if (!profile)
profile = {};
let accountChanged = !this.profile || this.profile.userName != profile.userName;
this.profile = profile;
// Sanitize the portrait from any potential script-injection.
if (profile.portrait) {
try {
let portraitUri = Services.io.newURI(profile.portrait, null, null);
let scheme = portraitUri ? portraitUri.scheme : "";
if (scheme != "data" && scheme != "http" && scheme != "https") {
profile.portrait = "";
}
} catch (ex) {
profile.portrait = "";
}
}
if (profile.iconURL)
this.iconURL = profile.iconURL;
if (!profile.displayName)
profile.displayName = profile.userName;
// if no userName, consider this a logged out state, emtpy the
// users ambient notifications. notify both profile and ambient
// changes to clear everything
if (!profile.userName) {
this.profile = {};
this.ambientNotificationIcons = {};
Services.obs.notifyObservers(null, "social:ambient-notification-changed", this.origin);
}
Services.obs.notifyObservers(null, "social:profile-changed", this.origin);
if (accountChanged)
closeAllChatWindows(this);
},
haveLoggedInUser: function () {
return !!(this.profile && this.profile.userName);
},
// Called by the workerAPI to add/update a notification icon.
setAmbientNotification: function(notification) {
if (!this.profile.userName)
throw new Error("unable to set notifications while logged out");
if (!this.ambientNotificationIcons[notification.name] &&
Object.keys(this.ambientNotificationIcons).length >= 3) {
throw new Error("ambient notification limit reached");
@ -862,47 +783,11 @@ SocialProvider.prototype = {
// Internal helper methods
_activate: function _activate() {
// Initialize the workerAPI and its port first, so that its initialization
// occurs before any other messages are processed by other ports.
let workerAPIPort = this.getWorkerPort();
if (workerAPIPort)
this.workerAPI = new WorkerAPI(this, workerAPIPort);
},
_terminate: function _terminate() {
closeAllChatWindows(this);
if (this.workerURL) {
try {
getFrameWorkerHandle(this.workerURL).terminate();
} catch (e) {
Cu.reportError("SocialProvider FrameWorker termination failed: " + e);
}
}
if (this.workerAPI) {
this.workerAPI.terminate();
}
this.errorState = null;
this.workerAPI = null;
this.profile = undefined;
},
/**
* Instantiates a FrameWorker for the provider if one doesn't exist, and
* returns a reference to a new port to that FrameWorker.
*
* Returns null if this provider has no workerURL, or is disabled.
*
* @param {DOMWindow} window (optional)
*/
getWorkerPort: function getWorkerPort(window) {
if (!this.workerURL || !this.enabled)
return null;
// Only allow localStorage in the frameworker for blessed providers
let allowLocalStorage = this.blessed;
let handle = getFrameWorkerHandle(this.workerURL, window,
"SocialProvider:" + this.origin, this.origin,
allowLocalStorage);
return handle.port;
},
/**

View File

@ -1,151 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "getFrameWorkerHandle", "resource://gre/modules/FrameWorker.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "openChatWindow", "resource://gre/modules/MozSocialAPI.jsm");
this.EXPORTED_SYMBOLS = ["WorkerAPI"];
this.WorkerAPI = function WorkerAPI(provider, port) {
if (!port)
throw new Error("Can't initialize WorkerAPI with a null port");
this._provider = provider;
this._port = port;
this._port.onmessage = this._handleMessage.bind(this);
// Send an "intro" message so the worker knows this is the port
// used for the api.
// later we might even include an API version - version 0 for now!
this._port.postMessage({topic: "social.initialize"});
}
WorkerAPI.prototype = {
terminate: function terminate() {
this._port.close();
},
_handleMessage: function _handleMessage(event) {
let {topic, data} = event.data;
let handler = this.handlers[topic];
if (!handler) {
Cu.reportError("WorkerAPI: topic doesn't have a handler: '" + topic + "'");
return;
}
try {
handler.call(this, data);
} catch (ex) {
Cu.reportError("WorkerAPI: failed to handle message '" + topic + "': " + ex + "\n" + ex.stack);
}
},
handlers: {
"social.manifest-get": function(data) {
// retreive the currently installed manifest from firefox
this._port.postMessage({topic: "social.manifest", data: this._provider.manifest});
},
"social.manifest-set": function(data) {
// the provider will get reloaded as a result of this call
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
let origin = this._provider.origin;
SocialService.updateProvider(origin, data);
},
"social.reload-worker": function(data) {
this._provider.reload();
},
"social.user-profile": function (data) {
this._provider.updateUserProfile(data);
},
"social.ambient-notification": function (data) {
this._provider.setAmbientNotification(data);
},
"social.cookies-get": function(data) {
// We don't want to trust provider.origin etc, just incase the provider
// redirected away or something else bad is going on. So we want to
// reach into the Worker's document and fetch the actual cookies it has.
// We need to do this via our own message dance.
let port = this._port;
let whandle = getFrameWorkerHandle(this._provider.workerURL, null);
whandle.port.close();
whandle._worker.browserPromise.then(browser => {
let mm = browser.messageManager;
mm.addMessageListener("frameworker:cookie-get-response", function _onCookieResponse(msg) {
mm.removeMessageListener("frameworker:cookie-get-response", _onCookieResponse);
let cookies = msg.json.split(";");
let results = [];
cookies.forEach(function(aCookie) {
let [name, value] = aCookie.split("=");
if (name || value) {
results.push({name: unescape(name.trim()),
value: value ? unescape(value.trim()) : ""});
}
});
port.postMessage({topic: "social.cookies-get-response",
data: results});
});
mm.sendAsyncMessage("frameworker:cookie-get");
});
},
'social.request-chat': function(data) {
openChatWindow(null, this._provider, data);
},
'social.notification-create': function(data) {
if (!Services.prefs.getBoolPref("social.toast-notifications.enabled"))
return;
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) {
let uriToOpen = provider.resolveUri(actionArgs.toURL);
// Bug 815970 - facebook gives us http:// links even though
// the origin is https:// - so we perform a fixup here.
let pUri = Services.io.newURI(provider.origin, null, null);
if (uriToOpen.scheme != pUri.scheme)
uriToOpen.scheme = pUri.scheme;
if (provider.isSameOrigin(uriToOpen)) {
let xulWindow = Services.wm.getMostRecentWindow("navigator:browser");
xulWindow.openUILinkIn(uriToOpen.spec, "tab");
} else {
Cu.reportError("Not opening notification link " + actionArgs.toURL
+ " as not in provider origin");
}
}
break;
default:
break;
}
}
}
alertsService.showAlertNotification(icon,
this._provider.name, // title
body,
!!action, // text clickable if an
// action was provided.
null,
listener,
type);
},
}
}

View File

@ -7,17 +7,10 @@
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
if CONFIG['MOZ_SOCIAL']:
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
# social is turned off for android
EXTRA_JS_MODULES += [
'FrameWorker.jsm',
'FrameWorkerContent.js',
'MessagePortBase.jsm',
'MessagePortWorker.js',
'MozSocialAPI.jsm',
'SocialService.jsm',
'WorkerAPI.jsm',
]
with Files('**'):

View File

@ -1,5 +0,0 @@
{
"extends": [
"../../../../../testing/mochitest/browser.eslintrc"
]
}

View File

@ -1,25 +0,0 @@
# Note: these tests are disabled if MOZ_SOCIAL is off.
[DEFAULT]
skip-if= buildapp == 'mulet'
support-files =
head.js
data.json
echo.sjs
worker_xhr.js
worker_relative.js
relative_import.js
worker_social.js
worker_eventsource.js
eventsource.resource
eventsource.resource^headers^
[browser_workerAPI.js]
[browser_SocialProvider.js]
[browser_notifications.js]
# These tests are currently unreliable on ASAN builds with remote frameworkers.
[browser_frameworker.js]
skip-if = asan || (os == 'linux' && debug) || (os == 'mac' && debug) # Bug 994798 for Linux debug disabling, bug 994300 for Mac debug disabling
[browser_frameworker_sandbox.js]
skip-if = asan || (os == 'linux' && debug) || (os == 'mac' && debug) # Bug 994798 for Linux debug disabling, bug 994300 for Mac debug disabling

View File

@ -1,85 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var provider;
function test() {
waitForExplicitFinish();
let manifest = {
origin: 'http://example.com',
name: "Example Provider",
workerURL: "http://example.com/browser/toolkit/components/social/test/browser/worker_social.js"
};
SocialService.addProvider(manifest, function (p) {
provider = p;
runTests(tests, undefined, undefined, function () {
SocialService.disableProvider(p.origin, function() {
ok(!provider.enabled, "removing an enabled provider should have disabled the provider");
let port = provider.getWorkerPort();
ok(!port, "should not be able to get a port after removing the provider");
provider = null;
finish();
});
});
});
}
var tests = {
testSingleProvider: function(next) {
ok(provider.enabled, "provider is initially enabled");
let port = provider.getWorkerPort();
ok(port, "should be able to get a port from enabled provider");
port.close();
ok(provider.workerAPI, "should be able to get a workerAPI from enabled provider");
provider.enabled = false;
ok(!provider.enabled, "provider is now disabled");
port = provider.getWorkerPort();
ok(!port, "shouldn't be able to get a port from disabled provider");
ok(!provider.workerAPI, "shouldn't be able to get a workerAPI from disabled provider");
provider.enabled = true;
ok(provider.enabled, "provider is re-enabled");
port = provider.getWorkerPort();
ok(port, "should be able to get a port from re-enabled provider");
port.close();
ok(provider.workerAPI, "should be able to get a workerAPI from re-enabled provider");
next();
},
testTwoProviders: function(next) {
// add another provider, test both workers
let manifest = {
origin: 'http://test2.example.com',
name: "Example Provider 2",
workerURL: "http://test2.example.com/browser/toolkit/components/social/test/browser/worker_social.js"
};
SocialService.addProvider(manifest, function (provider2) {
ok(provider.enabled, "provider is initially enabled");
ok(provider2.enabled, "provider2 is initially enabled");
let port = provider.getWorkerPort();
let port2 = provider2.getWorkerPort();
ok(port, "have port for provider");
ok(port2, "have port for provider2");
port.onmessage = function(e) {
if (e.data.topic == "test-initialization-complete") {
ok(true, "first provider initialized");
port2.postMessage({topic: "test-initialization"});
}
}
port2.onmessage = function(e) {
if (e.data.topic == "test-initialization-complete") {
ok(true, "second provider initialized");
SocialService.disableProvider(provider2.origin, function() {
next();
});
}
}
port.postMessage({topic: "test-initialization"});
});
}
}

View File

@ -1,389 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
requestLongerTimeout(2);
// This file tests message ports and semantics of the frameworker which aren't
// directly related to the sandbox. See also browser_frameworker_sandbox.js.
function makeWorkerUrl(runner) {
let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?";
if (typeof runner == "function") {
runner = "var run=" + runner.toSource() + ";run();";
}
return prefix + encodeURI(runner);
}
var getFrameWorkerHandle;
function test() {
waitForExplicitFinish();
let scope = {};
Cu.import("resource://gre/modules/FrameWorker.jsm", scope);
getFrameWorkerHandle = scope.getFrameWorkerHandle;
runTests(tests);
}
var tests = {
testSimple: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
port.postMessage({topic: "pong"});
}
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSimple");
worker.port.onmessage = function(e) {
if (e.data.topic == "pong") {
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "ping"})
},
// when the client closes early but the worker tries to send anyway...
// XXX - disabled due to bug 919878 - we close the frameworker before the
// remote browser has completed initializing, leading to failures. Given
// this can realistically only happen in this synthesized test environment,
// disabling just this test seems OK for now.
/***
testEarlyClose: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.postMessage({topic: "oh hai"});
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testEarlyClose");
worker.port.close();
worker.terminate();
cbnext();
},
***/
// Check we do get a social.port-closing message as the port is closed.
testPortClosingMessage: function(cbnext) {
// We use 2 ports - we close the first and report success via the second.
let run = function() {
let firstPort, secondPort;
onconnect = function(e) {
let port = e.ports[0];
if (firstPort === undefined) {
firstPort = port;
port.onmessage = function(e) {
if (e.data.topic == "social.port-closing") {
secondPort.postMessage({topic: "got-closing"});
}
}
} else {
secondPort = port;
// now both ports are connected we can trigger the client side
// closing the first.
secondPort.postMessage({topic: "connected"});
}
}
}
let workerurl = makeWorkerUrl(run);
let worker1 = getFrameWorkerHandle(workerurl, undefined, "testPortClosingMessage worker1");
let worker2 = getFrameWorkerHandle(workerurl, undefined, "testPortClosingMessage worker2");
worker2.port.onmessage = function(e) {
if (e.data.topic == "connected") {
// both ports connected, so close the first.
worker1.port.close();
} else if (e.data.topic == "got-closing") {
worker2.terminate();
cbnext();
}
}
},
// Tests that prototypes added to core objects work with data sent over
// the message ports.
testPrototypes: function(cbnext) {
let run = function() {
// Modify the Array prototype...
Array.prototype.customfunction = function() {};
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
// Check the data we get via the port has the prototype modification
if (e.data.topic == "hello" && e.data.data.customfunction) {
port.postMessage({topic: "hello", data: [1,2,3]});
}
}
}
}
// hrmph - this kinda sucks as it is really just testing the actual
// implementation rather than the end result, but is OK for now.
// Really we are just testing that JSON.parse in the client window
// is called.
let fakeWindow = {
JSON: {
parse: function(s) {
let data = JSON.parse(s);
data.data.somextrafunction = function() {};
return data;
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), fakeWindow, "testPrototypes");
worker.port.onmessage = function(e) {
if (e.data.topic == "hello") {
ok(e.data.data.somextrafunction, "have someextrafunction")
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "hello", data: [1,2,3]});
},
testSameOriginImport: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
try {
importScripts("http://mochi.test:8888/error");
} catch(ex) {
port.postMessage({topic: "pong", data: ex});
return;
}
port.postMessage({topic: "pong", data: null});
}
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSameOriginImport");
worker.port.onmessage = function(e) {
if (e.data.topic == "pong") {
isnot(e.data.data, null, "check same-origin applied to importScripts");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "ping"})
},
testRelativeImport: function(cbnext) {
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_relative.js";
let worker = getFrameWorkerHandle(url, undefined, "testSameOriginImport");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check relative url in importScripts");
worker.terminate();
cbnext();
}
}
},
testNavigator: function(cbnext) {
let run = function() {
let port;
ononline = function() {
port.postMessage({topic: "ononline", data: navigator.onLine});
}
onoffline = function() {
port.postMessage({topic: "onoffline", data: navigator.onLine});
}
onconnect = function(e) {
port = e.ports[0];
port.postMessage({topic: "ready",
data: {
appName: navigator.appName,
appVersion: navigator.appVersion,
platform: navigator.platform,
userAgent: navigator.userAgent,
}
});
}
}
let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService2);
let oldManage = ioService.manageOfflineStatus;
let oldOffline = ioService.offline;
ioService.manageOfflineStatus = false;
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNavigator");
let expected_topic = "onoffline";
let expected_data = false;
worker.port.onmessage = function(e) {
is(e.data.topic, "ready");
for (let attr of ['appName', 'appVersion', 'platform', 'userAgent']) {
// each attribute must be a string with length > 0.
is(typeof e.data.data[attr], "string");
ok(e.data.data[attr].length > 0);
}
worker.port.onmessage = function(e) {
// a handler specifically for the offline notification.
is(e.data.topic, "onoffline");
is(e.data.data, false);
// add another handler specifically for the 'online' case.
worker.port.onmessage = function(e) {
is(e.data.topic, "ononline");
is(e.data.data, true);
// all good!
ioService.manageOfflineStatus = oldManage;
ioService.offline = oldOffline;
worker.terminate();
cbnext();
}
ioService.offline = false;
}
ioService.offline = true;
}
},
testMissingWorker: function(cbnext) {
// don't ever create this file! We want a 404.
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_is_missing.js";
let worker = getFrameWorkerHandle(url, undefined, "testMissingWorker");
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
cbnext();
}
},
testNoConnectWorker: function(cbnext) {
let worker = getFrameWorkerHandle(makeWorkerUrl(function () {}),
undefined, "testNoConnectWorker");
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
cbnext();
}
},
testEmptyWorker: function(cbnext) {
let worker = getFrameWorkerHandle(makeWorkerUrl(''),
undefined, "testEmptyWorker");
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
cbnext();
}
},
testWorkerConnectError: function(cbnext) {
let run = function () {
onconnect = function(e) {
throw new Error("worker failure");
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run),
undefined, "testWorkerConnectError");
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
cbnext();
}
},
// This will create the worker, then send a message to the port, then close
// the port - all before the worker has actually initialized.
testCloseFirstSend: function(cbnext) {
let run = function() {
let numPings = 0, numCloses = 0;
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
numPings += 1;
} else if (e.data.topic == "social.port-closing") {
numCloses += 1;
} else if (e.data.topic == "get-counts") {
port.postMessage({topic: "result",
result: {ping: numPings, close: numCloses}});
}
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose");
worker.port.postMessage({topic: "ping"});
worker.port.close();
let newPort = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose").port;
newPort.onmessage = function(e) {
if (e.data.topic == "result") {
is(e.data.result.ping, 1, "the worker got the ping");
is(e.data.result.close, 1, "the worker got 1 close message");
worker.terminate();
cbnext();
}
}
newPort.postMessage({topic: "get-counts"});
},
// Like testCloseFirstSend, although in this test the worker has already
// initialized (so the "connect pending ports" part of the worker isn't
// what needs to handle this case.)
testCloseAfterInit: function(cbnext) {
let run = function() {
let numPings = 0, numCloses = 0;
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
numPings += 1;
} else if (e.data.topic == "social.port-closing") {
numCloses += 1;
} else if (e.data.topic == "get-counts") {
port.postMessage({topic: "result",
result: {ping: numPings, close: numCloses}});
} else if (e.data.topic == "get-ready") {
port.postMessage({topic: "ready"});
}
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose");
worker.port.onmessage = function(e) {
if (e.data.topic == "ready") {
let newPort = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testSendAndClose").port;
newPort.postMessage({topic: "ping"});
newPort.close();
worker.port.postMessage({topic: "get-counts"});
} else if (e.data.topic == "result") {
is(e.data.result.ping, 1, "the worker got the ping");
is(e.data.result.close, 1, "the worker got 1 close message");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "get-ready"});
},
}

View File

@ -1,347 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file tests features made available to the frameworker via the sandbox.
// For other frameworker tests, see browser_frameworker.js
function makeWorkerUrl(runner) {
let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?";
if (typeof runner == "function") {
runner = "var run=" + runner.toSource() + ";run();";
}
return prefix + encodeURI(runner);
}
var getFrameWorkerHandle;
function test() {
waitForExplicitFinish();
let scope = {};
Cu.import("resource://gre/modules/FrameWorker.jsm", scope);
getFrameWorkerHandle = scope.getFrameWorkerHandle;
runTests(tests);
}
var tests = {
testArrayUsingBuffer: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "go") {
let buffer = new ArrayBuffer(10);
// this one has always worked in the past, but worth checking anyway...
if (new Uint8Array(buffer).length != 10) {
port.postMessage({topic: "result", reason: "first length was not 10"});
return;
}
let reader = new FileReader();
reader.onload = function(event) {
if (new Uint8Array(buffer).length != 10) {
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
return;
}
// all seems good!
port.postMessage({topic: "result", reason: "ok"});
}
let blob = new Blob([buffer], {type: "binary"});
reader.readAsArrayBuffer(blob);
}
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testArray");
worker.port.onmessage = function(e) {
if (e.data.topic == "result") {
is(e.data.reason, "ok", "check the array worked");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "go"});
},
testArrayUsingReader: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "go") {
let buffer = new ArrayBuffer(10);
let reader = new FileReader();
reader.onload = function(event) {
try {
if (new Uint8Array(reader.result).length != 10) {
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
return;
}
// all seems good!
port.postMessage({topic: "result", reason: "ok"});
} catch (ex) {
port.postMessage({topic: "result", reason: ex.toString()});
}
}
let blob = new Blob([buffer], {type: "binary"});
reader.readAsArrayBuffer(blob);
}
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testArray");
worker.port.onmessage = function(e) {
if (e.data.topic == "result") {
is(e.data.reason, "ok", "check the array worked");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "go"});
},
testXHR: function(cbnext) {
// NOTE: this url MUST be in the same origin as worker_xhr.js fetches from!
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_xhr.js";
let worker = getFrameWorkerHandle(url, undefined, "testXHR");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check the xhr test worked");
worker.terminate();
cbnext();
}
}
},
testLocalStorage: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
try {
localStorage.setItem("foo", "1");
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to set localStorage, " + e.toString() });
return;
}
var ok;
try {
ok = localStorage["foo"] == 1;
} catch (e) {
port.postMessage({topic: "done", result: "FAILED to read localStorage, " + e.toString() });
return;
}
port.postMessage({topic: "done", result: "ok"});
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage", null, true);
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check the localStorage test worked");
worker.terminate();
cbnext();
}
}
},
testNoLocalStorage: function(cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
try {
localStorage.setItem("foo", "1");
} catch(e) {
port.postMessage({topic: "done", result: "ok"});
return;
}
port.postMessage({topic: "done", result: "FAILED because localStorage was exposed" });
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNoLocalStorage");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check that retrieving localStorage fails by default");
worker.terminate();
cbnext();
}
}
},
testBase64: function (cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
var ok = false;
try {
ok = btoa("1234") == "MTIzNA==";
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to call btoa, " + e.toString() });
return;
}
if (!ok) {
port.postMessage({topic: "done", result: "FAILED calling btoa"});
return;
}
try {
ok = atob("NDMyMQ==") == "4321";
} catch (e) {
port.postMessage({topic: "done", result: "FAILED to call atob, " + e.toString() });
return;
}
if (!ok) {
port.postMessage({topic: "done", result: "FAILED calling atob"});
return;
}
port.postMessage({topic: "done", result: "ok"});
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testBase64");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check the atob/btoa test worked");
worker.terminate();
cbnext();
}
}
},
testTimeouts: function (cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
var timeout;
try {
timeout = setTimeout(function () {
port.postMessage({topic: "done", result: "FAILED cancelled timeout was called"});
}, 100);
} catch (ex) {
port.postMessage({topic: "done", result: "FAILED calling setTimeout: " + ex});
return;
}
try {
clearTimeout(timeout);
} catch (ex) {
port.postMessage({topic: "done", result: "FAILED calling clearTimeout: " + ex});
return;
}
var counter = 0;
try {
timeout = setInterval(function () {
if (++counter == 2) {
clearInterval(timeout);
setTimeout(function () {
port.postMessage({topic: "done", result: "ok"});
return;
}, 0);
}
}, 100);
} catch (ex) {
port.postMessage({topic: "done", result: "FAILED calling setInterval: " + ex});
return;
}
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testTimeouts");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check that timeouts worked");
worker.terminate();
cbnext();
}
}
},
testWebSocket: function (cbnext) {
let run = function() {
onconnect = function(e) {
let port = e.ports[0];
try {
var exampleSocket = new WebSocket("ws://mochi.test:8888/socketserver");
} catch (e) {
port.postMessage({topic: "done", result: "FAILED calling WebSocket constructor: " + e});
return;
}
port.postMessage({topic: "done", result: "ok"});
}
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testWebSocket");
worker.port.onmessage = function(e) {
if (e.data.topic == "done") {
is(e.data.result, "ok", "check that websockets worked");
worker.terminate();
cbnext();
}
}
},
testEventSource: function(cbnext) {
let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_eventsource.js", undefined, "testEventSource");
worker.port.onmessage = function(e) {
let m = e.data;
if (m.topic == "eventSourceTest") {
if (m.result.ok != undefined)
ok(m.result.ok, e.data.result.msg);
if (m.result.is != undefined)
is(m.result.is, m.result.match, m.result.msg);
if (m.result.info != undefined)
info(m.result.info);
} else if (e.data.topic == "pong") {
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "ping"})
},
testIndexedDB: function(cbnext) {
let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_social.js", undefined, "testIndexedDB");
worker.port.onmessage = function(e) {
let m = e.data;
if (m.topic == "social.indexeddb-result") {
is(m.data.result, "ok", "created indexeddb");
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "test-indexeddb-create"})
},
testSubworker: function(cbnext) {
// the main "frameworker"...
let mainworker = function() {
onconnect = function(e) {
let port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "go") {
let suburl = e.data.data;
let worker = new Worker(suburl);
worker.onmessage = function(sube) {
port.postMessage({topic: "sub-message", data: sube.data});
}
}
}
}
}
// The "subworker" that is actually a real, bona-fide worker.
let subworker = function() {
postMessage("hello");
}
let worker = getFrameWorkerHandle(makeWorkerUrl(mainworker), undefined, "testSubWorker");
worker.port.onmessage = function(e) {
if (e.data.topic == "sub-message" && e.data.data == "hello") {
worker.terminate();
cbnext();
}
}
worker.port.postMessage({topic: "go", data: makeWorkerUrl(subworker)});
}
}

View File

@ -1,87 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
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: "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?"
+ encodeURI("let run=" + workerFunction.toSource()) + ";run();"
};
SocialService.addProvider(manifest, function (p) {
cb(p);
});
}
function test() {
waitForExplicitFinish();
let cbPostTest = function(cb) {
SocialService.disableProvider(TEST_PROVIDER_ORIGIN, function() {cb()});
};
replaceAlertsService();
registerCleanupFunction(restoreAlertsService);
runTests(tests, undefined, cbPostTest);
}
var 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) {
let observer = {
observedData: null,
observe: function(subject, topic, data) {
this.observedData = JSON.parse(data);
Services.obs.removeObserver(observer, "social-test:notification-alert");
}
}
Services.obs.addObserver(observer, "social-test:notification-alert", false);
let port = provider.getWorkerPort();
ok(port, "got port from worker");
port.onmessage = function(e) {
if (e.data.topic == "test.done") {
ok(e.data.data, "check the test worked");
ok(observer.observedData, "test observer fired");
is(observer.observedData.text, "test notification", "check the alert text is correct");
is(observer.observedData.title, "Example Provider", "check the alert title is correct");
is(observer.observedData.textClickable, true, "check the alert is clickable");
port.close();
cbnext();
}
}
port.postMessage({topic: "test.initialize"});
});
}
};

View File

@ -1,224 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var provider;
function test() {
waitForExplicitFinish();
replaceAlertsService();
registerCleanupFunction(restoreAlertsService);
let manifest = {
origin: 'http://example.com',
name: "Example Provider",
workerURL: "http://example.com/browser/toolkit/components/social/test/browser/worker_social.js"
};
SocialService.addProvider(manifest, function (p) {
// toolkit does not enable providers by default, that is handled in the
// browser, we need to enable it in our tests.
p.enabled = true;
provider = p;
runTests(tests, undefined, undefined, function () {
SocialService.disableProvider(provider.origin, finish);
});
});
}
var tests = {
testProfile: function(next) {
let expect = {
portrait: "https://example.com/portrait.jpg",
userName: "trickster",
displayName: "Kuma Lisa",
profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
}
function ob(aSubject, aTopic, aData) {
Services.obs.removeObserver(ob, "social:profile-changed");
is(aData, provider.origin, "update of profile from our provider");
let profile = provider.profile;
is(profile.portrait, expect.portrait, "portrait is set");
is(profile.userName, expect.userName, "userName is set");
is(profile.displayName, expect.displayName, "displayName is set");
is(profile.profileURL, expect.profileURL, "profileURL is set");
next();
}
Services.obs.addObserver(ob, "social:profile-changed", false);
let port = provider.getWorkerPort();
port.postMessage({topic: "test-profile", data: expect});
port.close();
},
testAmbientNotification: function(next) {
let expect = {
name: "test-ambient"
}
function ob(aSubject, aTopic, aData) {
Services.obs.removeObserver(ob, "social:ambient-notification-changed");
is(aData, provider.origin, "update is from our provider");
let notif = provider.ambientNotificationIcons[expect.name];
is(notif.name, expect.name, "ambientNotification reflected");
next();
}
Services.obs.addObserver(ob, "social:ambient-notification-changed", false);
let port = provider.getWorkerPort();
port.postMessage({topic: "test-ambient", data: expect});
port.close();
},
testProfileCleared: function(next) {
let sent = {
userName: ""
};
function ob(aSubject, aTopic, aData) {
Services.obs.removeObserver(ob, "social:profile-changed");
is(aData, provider.origin, "update of profile from our provider");
is(Object.keys(provider.profile).length, 0, "profile was cleared by empty username");
is(Object.keys(provider.ambientNotificationIcons).length, 0, "icons were cleared by empty username");
next();
}
Services.obs.addObserver(ob, "social:profile-changed", false);
provider.workerAPI._port.postMessage({topic: "test-profile", data: sent});
},
testNoCookies: function(next) {
// use a big, blunt stick to remove cookies.
Services.cookies.removeAll();
let port = provider.getWorkerPort();
port.onmessage = function onMessage(event) {
let {topic, data} = event.data;
if (topic == "test.cookies-get-response") {
is(data.length, 0, "got no cookies");
port.close();
next();
}
}
port.postMessage({topic: "test-initialization"});
port.postMessage({topic: "test.cookies-get"});
},
testCookies: function(next) {
let port = provider.getWorkerPort();
port.onmessage = function onMessage(event) {
let {topic, data} = event.data;
if (topic == "test.cookies-get-response") {
is(data.length, 2, "got 2 cookies");
is(data[0].name, "cheez", "cookie has the correct name");
is(data[0].value, "burger", "cookie has the correct value");
is(data[1].name, "moar", "cookie has the correct name");
is(data[1].value, "bacon", "cookie has the correct value");
Services.cookies.remove('.example.com', '/', 'cheez', {}, false);
Services.cookies.remove('.example.com', '/', 'moar', {}, false);
port.close();
next();
}
}
var MAX_EXPIRY = Math.pow(2, 62);
Services.cookies.add('.example.com', '/', 'cheez', 'burger', false, false, true, MAX_EXPIRY);
Services.cookies.add('.example.com', '/', 'moar', 'bacon', false, false, true, MAX_EXPIRY);
port.postMessage({topic: "test-initialization"});
port.postMessage({topic: "test.cookies-get"});
},
testWorkerReload: function(next) {
let fw = {};
Cu.import("resource://gre/modules/FrameWorker.jsm", fw);
let worker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload");
let port = provider.getWorkerPort();
// this observer will be fired when the worker reloads - it ensures the
// old port was closed and the new worker is functioning.
Services.obs.addObserver(function reloadObserver() {
Services.obs.removeObserver(reloadObserver, "social:provider-reload");
ok(port._closed, "old port was closed by the reload");
let newWorker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload - worker2");
let newPort = provider.getWorkerPort();
newPort.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-initialization-complete":
// and we are done.
newPort.close();
next();
break;
}
}
newPort.postMessage({topic: "test-initialization"});
}, "social:provider-reload", false);
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-initialization-complete":
// tell the worker to send the reload msg - that will trigger the
// frameworker to unload and for our content script's unload
// handler to post a "test-result" message.
port.postMessage({topic: "test-reload-init"});
// and the "social:provider-reload" observer should fire...
break;
}
}
port.postMessage({topic: "test-initialization"});
},
testNotificationLinks: function(next) {
let port = provider.getWorkerPort();
let data = {
id: 'an id',
body: 'the text',
action: 'link',
actionArgs: {} // will get a toURL elt during the tests...
}
let testArgs = [
// toURL, expectedLocation, expectedWhere]
["http://example.com", "http://example.com/", "tab"],
// bug 815970 - test that a mis-matched scheme gets patched up.
["https://example.com", "http://example.com/", "tab"],
// check an off-origin link is not opened.
["https://mochitest:8888/", null, null]
];
// we monkey-patch openUILinkIn
let oldopenUILinkIn = window.openUILinkIn;
registerCleanupFunction(function () {
// restore the monkey-patch
window.openUILinkIn = oldopenUILinkIn;
});
let openLocation;
let openWhere;
window.openUILinkIn = function(location, where) {
openLocation = location;
openWhere = where;
}
// the testing framework.
let toURL, expectedLocation, expectedWhere;
function nextTest() {
if (testArgs.length == 0) {
port.close();
next(); // all out of tests!
return;
}
openLocation = openWhere = null;
[toURL, expectedLocation, expectedWhere] = testArgs.shift();
data.actionArgs.toURL = toURL;
port.postMessage({topic: 'test-notification-create', data: data});
}
port.onmessage = function(evt) {
if (evt.data.topic == "did-notification-create") {
is(openLocation, expectedLocation, "url actually opened was " + openLocation);
is(openWhere, expectedWhere, "the url was opened in a " + expectedWhere);
nextTest();
}
}
// and kick off the tests.
port.postMessage({topic: "test-initialization"});
nextTest();
},
};

View File

@ -1,3 +0,0 @@
{
"response": "ok"
}

View File

@ -1,9 +0,0 @@
// A server-side JS test file for frameworker testing. It exists only to
// work-around a lack of data: URL support in the frameworker.
function handleRequest(request, response)
{
// The query string is the javascript - we just write it back.
response.setHeader("Content-Type", "application/javascript; charset=utf-8", false);
response.write(unescape(request.queryString));
}

View File

@ -1,12 +0,0 @@
:this file must be enconded in utf8
:and its Content-Type must be equal to text/event-stream
retry:10
data: 1
event: test-message
retry:10
data: 1
retry:10
data: 1

View File

@ -1,2 +0,0 @@
Content-Type: text/event-stream
Cache-Control: no-cache, must-revalidate

View File

@ -1,107 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var SocialService = Components.utils.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
var MockRegistrar = Components.utils.import("resource://testing-common/MockRegistrar.jsm", {}).MockRegistrar;
// A helper to run a suite of tests.
// The "test object" should be an object with function names as keys and a
// function as the value. The functions will be called with a "cbnext" param
// which should be called when the test is complete.
// eg:
// test = {
// foo: function(cbnext) {... cbnext();}
// }
function runTests(tests, cbPreTest, cbPostTest, cbFinish) {
let testIter = (function*() {
for (let name in tests) {
if (tests.hasOwnProperty(name)) {
yield [name, tests[name]];
}
}
})();
if (cbPreTest === undefined) {
cbPreTest = function(cb) {cb()};
}
if (cbPostTest === undefined) {
cbPostTest = function(cb) {cb()};
}
function runNextTest() {
let result = testIter.next();
if (result.done) {
// out of items:
(cbFinish || finish)();
return;
}
let [name, func] = result.value;
// We run on a timeout as the frameworker also makes use of timeouts, so
// this helps keep the debug messages sane.
executeSoon(function() {
function cleanupAndRunNextTest() {
info("sub-test " + name + " complete");
cbPostTest(runNextTest);
}
cbPreTest(function() {
info("sub-test " + name + " starting");
try {
func.call(tests, cleanupAndRunNextTest);
} catch (ex) {
ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
cleanupAndRunNextTest();
}
})
});
}
runNextTest();
}
// A mock notifications server. Based on:
// dom/tests/mochitest/notification/notification_common.js
const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
function MockAlertsService() {}
MockAlertsService.prototype = {
showAlertNotification: function(imageUrl, title, text, textClickable,
cookie, alertListener, name) {
let obData = JSON.stringify({
imageUrl: imageUrl,
title: title,
text:text,
textClickable: textClickable,
cookie: cookie,
name: name
});
Services.obs.notifyObservers(null, "social-test:notification-alert", obData);
if (textClickable) {
// probably should do this async....
alertListener.observe(null, "alertclickcallback", cookie);
}
alertListener.observe(null, "alertfinished", cookie);
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.nsISupports) ||
aIID.equals(Ci.nsIAlertsService))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
};
var originalAlertsServiceCID;
function replaceAlertsService() {
originalAlertsServiceCID =
MockRegistrar.register(ALERTS_SERVICE_CONTRACT_ID, MockAlertsService);
}
function restoreAlertsService() {
MockRegistrar.unregister(originalAlertsServiceCID);
}
// end of alerts service mock.

View File

@ -1,9 +0,0 @@
dump("relative_import file\n");
// Please keep 'causeError' on line 4; we test the error location.
function causeError() { does_not_exist(); }
testVar = "oh hai";
function testFunc() {
return "oh hai";
}

View File

@ -1,43 +0,0 @@
var port, es;
var url = "https://example.com/browser/toolkit/components/social/test/browser/eventsource.resource";
function ok(a, msg) {
port.postMessage({topic: "eventSourceTest",
result: {ok: a, msg: msg}});
}
function is(a, b, msg) {
port.postMessage({topic: "eventSourceTest",
result: {is: a, match: b, msg: msg}});
}
function esListener(e) {
esListener.msg_ok = true;
}
function esOnmessage(e) {
ok(true, "onmessage test");
ok(esListener.msg_ok, "listener test");
es.close();
port.postMessage({topic: "pong"});
}
function doTest() {
try {
es = new EventSource(url);
is(es.url, url, "eventsource.resource accessed", "we can create an eventsource instance");
es.addEventListener('test-message', esListener, true);
es.onmessage = esOnmessage;
} catch (e) {}
ok(!!es, "we can create an eventsource instance");
}
onconnect = function(e) {
port = e.ports[0];
port.onmessage = function(e) {
if (e.data.topic == "ping") {
doTest();
}
}
}

View File

@ -1,32 +0,0 @@
// Used to test XHR in the worker.
onconnect = function(e) {
let port = e.ports[0];
let req;
try {
importScripts("relative_import.js");
// the import should have exposed "testVar" and "testFunc" from the module.
if (testVar != "oh hai" || testFunc() != "oh hai") {
port.postMessage({topic: "done", result: "import worked but global is not available"});
return;
}
// causeError will cause a script error, so that we can check the
// error location for importScripts'ed files is correct.
try {
causeError();
} catch(e) {
let fileName = e.fileName;
if (fileName.startsWith("http") &&
fileName.endsWith("/relative_import.js") &&
e.lineNumber == 4)
port.postMessage({topic: "done", result: "ok"});
else
port.postMessage({topic: "done", result: "invalid error location: " + fileName + ":" + e.lineNumber});
return;
}
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to importScripts, " + e.toString() });
return;
}
port.postMessage({topic: "done", result: "FAILED to importScripts, no exception" });
}

View File

@ -1,59 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// apiPort is our port to WorkerAPI
var apiPort;
// testerPort is whatever port a test calls us on
var testerPort;
onconnect = function(e) {
// assume this is a test connecting, but if we get
// social.initialize, we know it is our WorkerAPI
// instance connecting and we'll set apiPort
let port = e.ports[0];
port.onmessage = function onMessage(event) {
let {topic, data} = event.data;
switch (topic) {
case "social.initialize":
apiPort = port;
break;
case "test-initialization":
testerPort = port;
port.postMessage({topic: "test-initialization-complete"});
break;
case "test-profile":
apiPort.postMessage({topic: "social.user-profile", data: data});
break;
case "test-ambient":
apiPort.postMessage({topic: "social.ambient-notification", data: data});
break;
case "test.cookies-get":
apiPort.postMessage({topic: "social.cookies-get"});
break;
case "social.cookies-get-response":
testerPort.postMessage({topic: "test.cookies-get-response", data: data});
break;
case "test-reload-init":
apiPort.postMessage({topic: 'social.reload-worker'});
break;
case "test-notification-create":
apiPort.postMessage({topic: 'social.notification-create',
data: data});
testerPort.postMessage({topic: 'did-notification-create'});
break;
case "test-indexeddb-create":
var request = indexedDB.open("workerdb", 1);
request.onerror = function(event) {
port.postMessage({topic: 'social.indexeddb-result', data: { result: "error" }});
};
request.onsuccess = function(event) {
// Do something with request.result!
var db = request.result;
db.close();
port.postMessage({topic: 'social.indexeddb-result', data: { result: "ok" }});
};
break;
}
}
}

View File

@ -1,34 +0,0 @@
// Used to test XHR in the worker.
onconnect = function(e) {
let port = e.ports[0];
let req;
try {
req = new XMLHttpRequest();
} catch(e) {
port.postMessage({topic: "done", result: "FAILED to create XHR object, " + e.toString() });
}
if (req === undefined) { // until bug 756173 is fixed...
port.postMessage({topic: "done", result: "FAILED to create XHR object"});
return;
}
// The test that uses this worker MUST use the same origin to load the worker.
// We fetch the test app manifest so we can check the data is what we expect.
let url = "https://example.com/browser/toolkit/components/social/test/browser/data.json";
req.open("GET", url, true);
req.onreadystatechange = function() {
if (req.readyState === 4) {
let ok = req.status == 200 && req.responseText.length > 0;
if (ok) {
// check we actually got something sane...
try {
let data = JSON.parse(req.responseText);
ok = "response" in data;
} catch(e) {
ok = e.toString();
}
}
port.postMessage({topic: "done", result: ok ? "ok" : "bad response"});
}
}
req.send(null);
}