mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 891225 implement new provider status buttons, r=markh
This commit is contained in:
parent
e7e23e3306
commit
dafd27215e
@ -37,6 +37,10 @@ SocialUI = {
|
||||
Services.obs.addObserver(this, "social:provider-set", false);
|
||||
Services.obs.addObserver(this, "social:providers-changed", false);
|
||||
Services.obs.addObserver(this, "social:provider-reload", false);
|
||||
Services.obs.addObserver(this, "social:provider-installed", false);
|
||||
Services.obs.addObserver(this, "social:provider-uninstalled", false);
|
||||
Services.obs.addObserver(this, "social:provider-enabled", false);
|
||||
Services.obs.addObserver(this, "social:provider-disabled", false);
|
||||
|
||||
Services.prefs.addObserver("social.sidebar.open", this, false);
|
||||
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
||||
@ -62,6 +66,10 @@ SocialUI = {
|
||||
Services.obs.removeObserver(this, "social:provider-set");
|
||||
Services.obs.removeObserver(this, "social:providers-changed");
|
||||
Services.obs.removeObserver(this, "social:provider-reload");
|
||||
Services.obs.removeObserver(this, "social:provider-installed");
|
||||
Services.obs.removeObserver(this, "social:provider-uninstalled");
|
||||
Services.obs.removeObserver(this, "social:provider-enabled");
|
||||
Services.obs.removeObserver(this, "social:provider-disabled");
|
||||
|
||||
Services.prefs.removeObserver("social.sidebar.open", this);
|
||||
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
||||
@ -76,6 +84,18 @@ SocialUI = {
|
||||
// manually :(
|
||||
try {
|
||||
switch (topic) {
|
||||
case "social:provider-installed":
|
||||
SocialStatus.setPosition(data);
|
||||
break;
|
||||
case "social:provider-uninstalled":
|
||||
SocialStatus.removePosition(data);
|
||||
break;
|
||||
case "social:provider-enabled":
|
||||
SocialStatus.populateToolbarPalette();
|
||||
break;
|
||||
case "social:provider-disabled":
|
||||
SocialStatus.removeProvider(data);
|
||||
break;
|
||||
case "social:provider-reload":
|
||||
// if the reloaded provider is our current provider, fall through
|
||||
// to social:provider-set so the ui will be reset
|
||||
@ -98,6 +118,7 @@ SocialUI = {
|
||||
SocialSidebar.update();
|
||||
SocialMark.update();
|
||||
SocialToolbar.update();
|
||||
SocialStatus.populateToolbarPalette();
|
||||
SocialMenu.populate();
|
||||
break;
|
||||
case "social:providers-changed":
|
||||
@ -106,10 +127,12 @@ SocialUI = {
|
||||
// and the multi-provider menu
|
||||
SocialToolbar.populateProviderMenus();
|
||||
SocialShare.populateProviderMenu();
|
||||
SocialStatus.populateToolbarPalette();
|
||||
break;
|
||||
|
||||
// Provider-specific notifications
|
||||
case "social:ambient-notification-changed":
|
||||
SocialStatus.updateNotification(data);
|
||||
if (this._matchesCurrentProvider(data)) {
|
||||
SocialToolbar.updateButton();
|
||||
SocialMenu.populate();
|
||||
@ -1056,6 +1079,15 @@ SocialToolbar = {
|
||||
Services.prefs.clearUserPref(CACHE_PREF_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the provider uses the new SocialStatus button, then they do not get
|
||||
// to use the ambient icons in the old toolbar button. Since the status
|
||||
// button depends on multiple workers, if not enabled we will ignore this
|
||||
// limitation. That allows a provider to migrate to the new functionality
|
||||
// once we enable multiple workers.
|
||||
if (Social.provider.statusURL && Social.allowMultipleWorkers)
|
||||
return;
|
||||
|
||||
let icons = Social.provider.ambientNotificationIcons;
|
||||
let iconNames = Object.keys(icons);
|
||||
|
||||
@ -1154,9 +1186,8 @@ SocialToolbar = {
|
||||
socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
|
||||
|
||||
for (let frame of createdFrames) {
|
||||
if (frame.socialErrorListener) {
|
||||
if (frame.socialErrorListener)
|
||||
frame.socialErrorListener.remove();
|
||||
}
|
||||
if (frame.docShell) {
|
||||
frame.docShell.isActive = false;
|
||||
Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
|
||||
@ -1201,11 +1232,10 @@ SocialToolbar = {
|
||||
|
||||
panel.addEventListener("popupshown", function onpopupshown() {
|
||||
panel.removeEventListener("popupshown", onpopupshown);
|
||||
// This attribute is needed on both the button and the
|
||||
// containing toolbaritem since the buttons on OS X have
|
||||
// moz-appearance:none, while their container gets
|
||||
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
||||
// get combined on OS X.
|
||||
// The "open" attribute is needed on both the button and the containing
|
||||
// toolbaritem since the buttons on OS X have moz-appearance:none, while
|
||||
// their container gets moz-appearance:toolbarbutton due to the way that
|
||||
// toolbar buttons get combined on OS X.
|
||||
aToolbarButton.setAttribute("open", "true");
|
||||
aToolbarButton.parentNode.setAttribute("open", "true");
|
||||
notificationFrame.docShell.isActive = true;
|
||||
@ -1392,4 +1422,353 @@ SocialSidebar = {
|
||||
}
|
||||
}
|
||||
|
||||
// this helper class is used by removable/customizable buttons to handle
|
||||
// location persistence and insertion into palette and/or toolbars
|
||||
|
||||
// When a provider is installed we show all their UI so the user will see the
|
||||
// functionality of what they installed. The user can later customize the UI,
|
||||
// moving buttons around or off the toolbar.
|
||||
//
|
||||
// To make this happen, on install we add a button id to the navbar currentset.
|
||||
// On enabling the provider (happens just after install) we insert the button
|
||||
// into the toolbar as well. The button is then persisted on restart (assuming
|
||||
// it was not removed).
|
||||
//
|
||||
// When a provider is disabled, we do not remove the buttons from currentset.
|
||||
// That way, if the provider is re-enabled during the same session, the buttons
|
||||
// will reappear where they were before. When a provider is uninstalled, we make
|
||||
// sure that the id is removed from currentset.
|
||||
//
|
||||
// On startup, we insert the buttons of any enabled provider into either the
|
||||
// apropriate toolbar or the palette.
|
||||
function ToolbarHelper(type, createButtonFn) {
|
||||
this._createButton = createButtonFn;
|
||||
this._type = type;
|
||||
}
|
||||
|
||||
ToolbarHelper.prototype = {
|
||||
idFromOrgin: function(origin) {
|
||||
return this._type + "-" + origin;
|
||||
},
|
||||
|
||||
// find a button either in the document or the palette
|
||||
_getExistingButton: function(id) {
|
||||
let button = document.getElementById(id);
|
||||
if (button)
|
||||
return button;
|
||||
let palette = document.getElementById("navigator-toolbox").palette;
|
||||
let paletteItem = palette.firstChild;
|
||||
while (paletteItem) {
|
||||
if (paletteItem.id == id)
|
||||
return paletteItem;
|
||||
paletteItem = paletteItem.nextSibling;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
setPersistentPosition: function(id) {
|
||||
// called when a provider is installed. add provider buttons to nav-bar
|
||||
let toolbar = document.getElementById("nav-bar");
|
||||
// first startups will not have a currentset attribute, always rely on
|
||||
// currentSet since it will be derived from the defaultset in that case.
|
||||
let currentset = toolbar.currentSet;
|
||||
if (currentset == "__empty")
|
||||
currentset = []
|
||||
else
|
||||
currentset = currentset.split(",");
|
||||
if (currentset.indexOf(id) >= 0)
|
||||
return;
|
||||
// we do not set toolbar.currentSet since that will try to add the button,
|
||||
// and we have not added it yet (happens on provider being enabled)
|
||||
currentset.push(id);
|
||||
toolbar.setAttribute("currentset", currentset.join(","));
|
||||
document.persist(toolbar.id, "currentset");
|
||||
},
|
||||
|
||||
removeProviderButton: function(origin) {
|
||||
// this will remove the button from the palette or the toolbar
|
||||
let button = this._getExistingButton(this.idFromOrgin(origin));
|
||||
if (button)
|
||||
button.parentNode.removeChild(button);
|
||||
},
|
||||
|
||||
removePersistence: function(id) {
|
||||
let persisted = document.querySelectorAll("*[currentset]");
|
||||
for (let pent of persisted) {
|
||||
// the button will have been removed, but left in the currentset attribute
|
||||
// in case the user re-enables (e.g. undo in addon manager). So we only
|
||||
// check the attribute here.
|
||||
let currentset = pent.getAttribute("currentset").split(",");
|
||||
|
||||
let pos = currentset.indexOf(id);
|
||||
if (pos >= 0) {
|
||||
currentset.splice(pos, 1);
|
||||
pent.setAttribute("currentset", currentset.join(","));
|
||||
document.persist(pent.id, "currentset");
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// if social is entirely disabled, we need to clear the palette, but leave
|
||||
// the persisted id's in place
|
||||
clearPalette: function() {
|
||||
[this.removeProviderButton(p.origin) for (p of Social.providers)];
|
||||
},
|
||||
|
||||
// should be called on startup of each window, otherwise the addon manager
|
||||
// listener will handle new activations, or enable/disabling of a provider
|
||||
// XXX we currently call more regularly, will fix during refactoring
|
||||
populatePalette: function() {
|
||||
if (!Social.enabled) {
|
||||
this.clearPalette();
|
||||
return;
|
||||
}
|
||||
let persisted = document.querySelectorAll("*[currentset]");
|
||||
let persistedById = {};
|
||||
for (let pent of persisted) {
|
||||
let pset = pent.getAttribute("currentset").split(',');
|
||||
for (let id of pset)
|
||||
persistedById[id] = pent;
|
||||
}
|
||||
|
||||
// create any buttons that do not exist yet if they have been persisted
|
||||
// as a part of the UI (otherwise they belong in the palette).
|
||||
for (let provider of Social.providers) {
|
||||
let id = this.idFromOrgin(provider.origin);
|
||||
if (this._getExistingButton(id))
|
||||
return;
|
||||
let button = this._createButton(provider);
|
||||
if (button && persistedById.hasOwnProperty(id)) {
|
||||
let parent = persistedById[id];
|
||||
let pset = persistedById[id].getAttribute("currentset").split(',');
|
||||
let pi = pset.indexOf(id) + 1;
|
||||
let next = document.getElementById(pset[pi]);
|
||||
parent.insertItem(id, next, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SocialStatus = {
|
||||
populateToolbarPalette: function() {
|
||||
if (!Social.allowMultipleWorkers)
|
||||
return;
|
||||
this._toolbarHelper.populatePalette();
|
||||
},
|
||||
|
||||
setPosition: function(origin) {
|
||||
if (!Social.allowMultipleWorkers)
|
||||
return;
|
||||
// this is called during install, before the provider is enabled so we have
|
||||
// to use the manifest rather than the provider instance as we do elsewhere.
|
||||
let manifest = Social.getManifestByOrigin(origin);
|
||||
if (!manifest.statusURL)
|
||||
return;
|
||||
let tbh = this._toolbarHelper;
|
||||
tbh.setPersistentPosition(tbh.idFromOrgin(origin));
|
||||
},
|
||||
|
||||
removePosition: function(origin) {
|
||||
if (!Social.allowMultipleWorkers)
|
||||
return;
|
||||
let tbh = this._toolbarHelper;
|
||||
tbh.removePersistence(tbh.idFromOrgin(origin));
|
||||
},
|
||||
|
||||
removeProvider: function(origin) {
|
||||
if (!Social.allowMultipleWorkers)
|
||||
return;
|
||||
this._toolbarHelper.removeProviderButton(origin);
|
||||
},
|
||||
|
||||
get _toolbarHelper() {
|
||||
delete this._toolbarHelper;
|
||||
this._toolbarHelper = new ToolbarHelper("social-status-button", this._createButton.bind(this));
|
||||
return this._toolbarHelper;
|
||||
},
|
||||
|
||||
get _dynamicResizer() {
|
||||
delete this._dynamicResizer;
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
return this._dynamicResizer;
|
||||
},
|
||||
|
||||
_createButton: function(provider) {
|
||||
if (!provider.statusURL)
|
||||
return null;
|
||||
let palette = document.getElementById("navigator-toolbox").palette;
|
||||
let button = document.createElement("toolbarbutton");
|
||||
button.setAttribute("class", "toolbarbutton-1 social-status-button");
|
||||
button.setAttribute("type", "badged");
|
||||
button.setAttribute("removable", "true");
|
||||
button.setAttribute("image", provider.iconURL);
|
||||
button.setAttribute("label", provider.name);
|
||||
button.setAttribute("tooltiptext", provider.name);
|
||||
button.setAttribute("origin", provider.origin);
|
||||
button.setAttribute("oncommand", "SocialStatus.showPopup(this);");
|
||||
button.setAttribute("id", this._toolbarHelper.idFromOrgin(provider.origin));
|
||||
palette.appendChild(button);
|
||||
return button;
|
||||
},
|
||||
|
||||
// status panels are one-per button per-process, we swap the docshells between
|
||||
// windows when necessary
|
||||
_attachNotificatonPanel: function(aButton, provider) {
|
||||
let panel = document.getElementById("social-notification-panel");
|
||||
panel.hidden = !SocialUI.enabled;
|
||||
let notificationFrameId = "social-status-" + provider.origin;
|
||||
let frame = document.getElementById(notificationFrameId);
|
||||
|
||||
if (!frame) {
|
||||
frame = SharedFrame.createFrame(
|
||||
notificationFrameId, /* frame name */
|
||||
panel, /* parent */
|
||||
{
|
||||
"type": "content",
|
||||
"mozbrowser": "true",
|
||||
"class": "social-panel-frame",
|
||||
"id": notificationFrameId,
|
||||
"tooltip": "aHTMLTooltip",
|
||||
|
||||
// work around bug 793057 - by making the panel roughly the final size
|
||||
// we are more likely to have the anchor in the correct position.
|
||||
"style": "width: " + PANEL_MIN_WIDTH + "px;",
|
||||
|
||||
"origin": provider.origin,
|
||||
"src": provider.statusURL
|
||||
}
|
||||
);
|
||||
|
||||
if (frame.socialErrorListener)
|
||||
frame.socialErrorListener.remove();
|
||||
if (frame.docShell) {
|
||||
frame.docShell.isActive = false;
|
||||
Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
|
||||
}
|
||||
} else {
|
||||
frame.setAttribute("origin", provider.origin);
|
||||
SharedFrame.updateURL(notificationFrameId, provider.statusURL);
|
||||
}
|
||||
aButton.setAttribute("notificationFrameId", notificationFrameId);
|
||||
},
|
||||
|
||||
updateNotification: function(origin) {
|
||||
if (!Social.allowMultipleWorkers)
|
||||
return;
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
let button = document.getElementById(this._toolbarHelper.idFromOrgin(provider.origin));
|
||||
if (button) {
|
||||
// we only grab the first notification, ignore all others
|
||||
let icons = provider.ambientNotificationIcons;
|
||||
let iconNames = Object.keys(icons);
|
||||
let notif = icons[iconNames[0]];
|
||||
if (!notif) {
|
||||
button.setAttribute("badge", "");
|
||||
button.setAttribute("aria-label", "");
|
||||
button.setAttribute("tooltiptext", "");
|
||||
return;
|
||||
}
|
||||
|
||||
button.style.listStyleImage = "url(" + notif.iconURL || provider.iconURL + ")";
|
||||
button.setAttribute("tooltiptext", notif.label);
|
||||
|
||||
let badge = notif.counter || "";
|
||||
button.setAttribute("badge", badge);
|
||||
let ariaLabel = notif.label;
|
||||
// if there is a badge value, we must use a localizable string to insert it.
|
||||
if (badge)
|
||||
ariaLabel = gNavigatorBundle.getFormattedString("social.aria.toolbarButtonBadgeText",
|
||||
[ariaLabel, badge]);
|
||||
button.setAttribute("aria-label", ariaLabel);
|
||||
}
|
||||
},
|
||||
|
||||
showPopup: function(aToolbarButton) {
|
||||
if (!Social.allowMultipleWorkers)
|
||||
return;
|
||||
// attach our notification panel if necessary
|
||||
let origin = aToolbarButton.getAttribute("origin");
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
this._attachNotificatonPanel(aToolbarButton, provider);
|
||||
|
||||
let panel = document.getElementById("social-notification-panel");
|
||||
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
||||
let notificationFrame = document.getElementById(notificationFrameId);
|
||||
|
||||
let wasAlive = SharedFrame.isGroupAlive(notificationFrameId);
|
||||
SharedFrame.setOwner(notificationFrameId, notificationFrame);
|
||||
|
||||
// Clear dimensions on all browsers so the panel size will
|
||||
// only use the selected browser.
|
||||
let frameIter = panel.firstElementChild;
|
||||
while (frameIter) {
|
||||
frameIter.collapsed = (frameIter != notificationFrame);
|
||||
frameIter = frameIter.nextElementSibling;
|
||||
}
|
||||
|
||||
function dispatchPanelEvent(name) {
|
||||
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(name, true, true, {});
|
||||
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
let dynamicResizer = this._dynamicResizer;
|
||||
panel.addEventListener("popuphidden", function onpopuphiding() {
|
||||
panel.removeEventListener("popuphidden", onpopuphiding);
|
||||
aToolbarButton.removeAttribute("open");
|
||||
dynamicResizer.stop();
|
||||
notificationFrame.docShell.isActive = false;
|
||||
dispatchPanelEvent("socialFrameHide");
|
||||
});
|
||||
|
||||
panel.addEventListener("popupshown", function onpopupshown() {
|
||||
panel.removeEventListener("popupshown", onpopupshown);
|
||||
// This attribute is needed on both the button and the
|
||||
// containing toolbaritem since the buttons on OS X have
|
||||
// moz-appearance:none, while their container gets
|
||||
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
||||
// get combined on OS X.
|
||||
aToolbarButton.setAttribute("open", "true");
|
||||
notificationFrame.docShell.isActive = true;
|
||||
notificationFrame.docShell.isAppTab = true;
|
||||
if (notificationFrame.contentDocument.readyState == "complete" && wasAlive) {
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
dispatchPanelEvent("socialFrameShow");
|
||||
} else {
|
||||
// first time load, wait for load and dispatch after load
|
||||
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
||||
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
dispatchPanelEvent("socialFrameShow");
|
||||
}, true);
|
||||
}
|
||||
});
|
||||
|
||||
let navBar = document.getElementById("nav-bar");
|
||||
let anchor = navBar.getAttribute("mode") == "text" ?
|
||||
document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-text") :
|
||||
document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
|
||||
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
|
||||
// handling from preventing it being opened in some cases.
|
||||
setTimeout(function() {
|
||||
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||
}, 0);
|
||||
},
|
||||
|
||||
setPanelErrorMessage: function(aNotificationFrame) {
|
||||
if (!aNotificationFrame)
|
||||
return;
|
||||
|
||||
let src = aNotificationFrame.getAttribute("src");
|
||||
aNotificationFrame.removeAttribute("src");
|
||||
aNotificationFrame.webNavigation.loadURI("about:socialerror?mode=tryAgainOnly&url=" +
|
||||
encodeURIComponent(src),
|
||||
null, null, null, null);
|
||||
let panel = aNotificationFrame.parentNode;
|
||||
sizeSocialPanelToContent(panel, aNotificationFrame);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -31,6 +31,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_social_multiprovider.js \
|
||||
browser_social_multiworker.js \
|
||||
browser_social_errorPage.js \
|
||||
browser_social_status.js \
|
||||
browser_social_window.js \
|
||||
social_activate.html \
|
||||
social_activate_iframe.html \
|
||||
|
@ -50,9 +50,10 @@ function installListener(next, aManifest) {
|
||||
let expectEvent = "onInstalling";
|
||||
let prefname = getManifestPrefname(aManifest);
|
||||
// wait for the actual removal to call next
|
||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
||||
if (topic == "provider-removed") {
|
||||
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||
if (topic == "provider-disabled") {
|
||||
SocialService.unregisterProviderListener(providerListener);
|
||||
is(origin, aManifest.origin, "provider disabled");
|
||||
executeSoon(next);
|
||||
}
|
||||
});
|
||||
@ -295,14 +296,15 @@ var tests = {
|
||||
Social.enabled = true;
|
||||
|
||||
// watch for the provider-update and test the new version
|
||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
||||
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||
if (topic != "provider-update")
|
||||
return;
|
||||
is(origin, addonManifest.origin, "provider updated")
|
||||
SocialService.unregisterProviderListener(providerListener);
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
let provider = Social._getProviderFromOrigin(addonManifest.origin);
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
is(provider.manifest.version, 2, "manifest version is 2");
|
||||
Social.uninstallProvider(addonManifest.origin, function() {
|
||||
Social.uninstallProvider(origin, function() {
|
||||
gBrowser.removeTab(tab);
|
||||
next();
|
||||
});
|
||||
|
@ -155,14 +155,15 @@ var tests = {
|
||||
setManifestPref("social.manifest.blocked", manifest_bad);
|
||||
try {
|
||||
SocialService.addProvider(manifest_bad, function(provider) {
|
||||
// the act of blocking should cause a 'provider-removed' notification
|
||||
// the act of blocking should cause a 'provider-disabled' notification
|
||||
// from SocialService.
|
||||
SocialService.registerProviderListener(function providerListener(topic) {
|
||||
if (topic != "provider-removed")
|
||||
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||
if (topic != "provider-disabled")
|
||||
return;
|
||||
SocialService.unregisterProviderListener(providerListener);
|
||||
is(origin, provider.origin, "provider disabled");
|
||||
SocialService.getProvider(provider.origin, function(p) {
|
||||
ok(p==null, "blocklisted provider removed");
|
||||
ok(p == null, "blocklisted provider disabled");
|
||||
Services.prefs.clearUserPref("social.manifest.blocked");
|
||||
resetBlocklist(finish);
|
||||
});
|
||||
|
@ -19,6 +19,13 @@ function test() {
|
||||
|
||||
var tests = {
|
||||
testStatusIcons: function(next) {
|
||||
let icon = {
|
||||
name: "testIcon",
|
||||
iconURL: "chrome://browser/skin/Info.png",
|
||||
contentPanel: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
|
||||
counter: 1
|
||||
};
|
||||
|
||||
let iconsReady = false;
|
||||
let gotSidebarMessage = false;
|
||||
|
||||
@ -71,7 +78,7 @@ var tests = {
|
||||
ok(true, "got sidebar message");
|
||||
gotSidebarMessage = true;
|
||||
// load a status panel
|
||||
port.postMessage({topic: "test-ambient-notification"});
|
||||
port.postMessage({topic: "test-ambient-notification", data: icon});
|
||||
checkNext();
|
||||
break;
|
||||
}
|
||||
|
220
browser/base/content/test/social/browser_social_status.js
Normal file
220
browser/base/content/test/social/browser_social_status.js
Normal file
@ -0,0 +1,220 @@
|
||||
/* 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/. */
|
||||
|
||||
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
|
||||
|
||||
let 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/moz.png"
|
||||
};
|
||||
let 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/moz.png",
|
||||
version: 1
|
||||
};
|
||||
let manifest3 = { // used for testing install
|
||||
name: "provider test2",
|
||||
origin: "https://test2.example.com",
|
||||
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
iconURL: "https://test2.example.com/browser/browser/base/content/test/moz.png",
|
||||
version: 1
|
||||
};
|
||||
|
||||
|
||||
function openWindowAndWaitForInit(callback) {
|
||||
let topic = "browser-delayed-startup-finished";
|
||||
let w = OpenBrowserWindow();
|
||||
Services.obs.addObserver(function providerSet(subject, topic, data) {
|
||||
Services.obs.removeObserver(providerSet, topic);
|
||||
executeSoon(() => callback(w));
|
||||
}, topic, false);
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
|
||||
let toolbar = document.getElementById("nav-bar");
|
||||
let currentsetAtStart = toolbar.currentSet;
|
||||
info("tb0 "+currentsetAtStart);
|
||||
runSocialTestWithProvider(manifest, function () {
|
||||
runSocialTests(tests, undefined, undefined, function () {
|
||||
Services.prefs.clearUserPref("social.remote-install.enabled");
|
||||
// just in case the tests failed, clear these here as well
|
||||
Services.prefs.clearUserPref("social.allowMultipleWorkers");
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
|
||||
// This post-test test ensures that a new window maintains the same
|
||||
// toolbar button set as when we started. That means our insert/removal of
|
||||
// persistent id's is working correctly
|
||||
is(currentsetAtStart, toolbar.currentSet, "toolbar currentset unchanged");
|
||||
openWindowAndWaitForInit(function(w1) {
|
||||
checkSocialUI(w1);
|
||||
// Sometimes the new window adds other buttons to currentSet that are
|
||||
// outside the scope of what we're checking. So we verify that all
|
||||
// buttons from startup are in currentSet for a new window, and that the
|
||||
// provider buttons are properly removed. (e.g on try, window-controls
|
||||
// was not present in currentsetAtStart, but present on the second
|
||||
// window)
|
||||
let tb1 = w1.document.getElementById("nav-bar");
|
||||
info("tb0 "+toolbar.currentSet);
|
||||
info("tb1 "+tb1.currentSet);
|
||||
let startupSet = Set(toolbar.currentSet.split(','));
|
||||
let newSet = Set(tb1.currentSet.split(','));
|
||||
let intersect = Set([x for (x of startupSet) if (newSet.has(x))]);
|
||||
info("intersect "+intersect);
|
||||
let difference = Set([x for (x of newSet) if (!startupSet.has(x))]);
|
||||
info("difference "+difference);
|
||||
is(startupSet.size, intersect.size, "new window toolbar same as old");
|
||||
// verify that our provider buttons are not in difference
|
||||
let id = SocialStatus._toolbarHelper.idFromOrgin(manifest2.origin);
|
||||
ok(!difference.has(id), "status button not persisted at end");
|
||||
w1.close();
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var tests = {
|
||||
testNoButtonOnInstall: function(next) {
|
||||
// we expect the addon install dialog to appear, we need to accept the
|
||||
// install from the dialog.
|
||||
info("Waiting for install dialog");
|
||||
let panel = document.getElementById("servicesInstall-notification");
|
||||
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||
info("servicesInstall-notification panel opened");
|
||||
panel.button.click();
|
||||
})
|
||||
|
||||
let id = "social-status-button-" + manifest3.origin;
|
||||
let toolbar = document.getElementById("nav-bar");
|
||||
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||
ok(currentset.indexOf(id) < 0, "button is not part of currentset at start");
|
||||
|
||||
let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest3, function(addonManifest) {
|
||||
// enable the provider so we know the button would have appeared
|
||||
SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
|
||||
ok(provider, "provider is installed");
|
||||
currentset = toolbar.getAttribute("currentset").split(',');
|
||||
ok(currentset.indexOf(id) < 0, "button was not added to currentset");
|
||||
Social.uninstallProvider(manifest3.origin, function() {
|
||||
gBrowser.removeTab(tab);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
testButtonOnInstall: function(next) {
|
||||
// we expect the addon install dialog to appear, we need to accept the
|
||||
// install from the dialog.
|
||||
info("Waiting for install dialog");
|
||||
let panel = document.getElementById("servicesInstall-notification");
|
||||
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||
info("servicesInstall-notification panel opened");
|
||||
panel.button.click();
|
||||
})
|
||||
|
||||
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
// at this point, we should have a button id in the currentset for our provider
|
||||
let id = "social-status-button-" + manifest2.origin;
|
||||
let toolbar = document.getElementById("nav-bar");
|
||||
|
||||
waitForCondition(function() {
|
||||
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||
return currentset.indexOf(id) >= 0;
|
||||
},
|
||||
function() {
|
||||
// no longer need the tab
|
||||
gBrowser.removeTab(tab);
|
||||
next();
|
||||
}, "status button added to currentset");
|
||||
});
|
||||
});
|
||||
},
|
||||
testButtonOnEnable: function(next) {
|
||||
// enable the provider now
|
||||
SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
|
||||
ok(provider, "provider is installed");
|
||||
let id = "social-status-button-" + manifest2.origin;
|
||||
waitForCondition(function() { return document.getElementById(id) },
|
||||
next, "button exists after enabling social");
|
||||
});
|
||||
},
|
||||
testStatusPanel: function(next) {
|
||||
let icon = {
|
||||
name: "testIcon",
|
||||
iconURL: "chrome://browser/skin/Info.png",
|
||||
counter: 1
|
||||
};
|
||||
// click on panel to open and wait for visibility
|
||||
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||
let id = "social-status-button-" + provider.origin;
|
||||
let btn = document.getElementById(id)
|
||||
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") {
|
||||
let panel = document.getElementById("social-notification-panel");
|
||||
panel.hidePopup();
|
||||
} 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"});
|
||||
},
|
||||
testButtonOnDisable: function(next) {
|
||||
// enable the provider now
|
||||
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||
ok(provider, "provider is installed");
|
||||
SocialService.removeProvider(manifest2.origin, function() {
|
||||
let id = "social-status-button-" + manifest2.origin;
|
||||
waitForCondition(function() { return !document.getElementById(id) },
|
||||
next, "button does not exist after disabling the provider");
|
||||
});
|
||||
},
|
||||
testButtonOnUninstall: function(next) {
|
||||
Social.uninstallProvider(manifest2.origin, function() {
|
||||
// test that the button is no longer persisted
|
||||
let id = "social-status-button-" + manifest2.origin;
|
||||
let toolbar = document.getElementById("nav-bar");
|
||||
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||
is(currentset.indexOf(id), -1, "button no longer in currentset");
|
||||
next();
|
||||
});
|
||||
}
|
||||
}
|
@ -177,7 +177,7 @@ function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
|
||||
cbPostTest(runNextTest);
|
||||
}
|
||||
cbPreTest(function() {
|
||||
is(providersAtStart, Social.providers.length, "pre-test: no new providers left enabled");
|
||||
info("pre-test: starting with " + Social.providers.length + " providers");
|
||||
info("sub-test " + name + " starting");
|
||||
try {
|
||||
func.call(tests, cleanupAndRunNextTest);
|
||||
|
@ -14,6 +14,7 @@ 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",
|
||||
|
||||
// should be available for display purposes
|
||||
"description": "A short paragraph about this provider",
|
||||
|
@ -119,13 +119,7 @@ onconnect = function(e) {
|
||||
});
|
||||
break;
|
||||
case "test-ambient-notification":
|
||||
let icon = {
|
||||
name: "testIcon",
|
||||
iconURL: "chrome://browser/skin/Info.png",
|
||||
contentPanel: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
|
||||
counter: 1
|
||||
};
|
||||
apiPort.postMessage({topic: "social.ambient-notification", data: icon});
|
||||
apiPort.postMessage({topic: "social.ambient-notification", data: event.data.data});
|
||||
break;
|
||||
case "test-isVisible":
|
||||
sidebarPort.postMessage({topic: "test-isVisible"});
|
||||
|
@ -169,24 +169,28 @@ this.Social = {
|
||||
}
|
||||
|
||||
// Register an observer for changes to the provider list
|
||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
||||
SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
|
||||
// An engine change caused by adding/removing a provider should notify.
|
||||
// any providers we receive are enabled in the AddonsManager
|
||||
if (topic == "provider-added" || topic == "provider-removed") {
|
||||
Social._updateProviderCache(data);
|
||||
if (topic == "provider-installed" || topic == "provider-uninstalled") {
|
||||
// installed/uninstalled do not send the providers param
|
||||
Services.obs.notifyObservers(null, "social:" + topic, origin);
|
||||
return;
|
||||
}
|
||||
if (topic == "provider-enabled" || topic == "provider-disabled") {
|
||||
Social._updateProviderCache(providers);
|
||||
Social._updateWorkerState(true);
|
||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||
Services.obs.notifyObservers(null, "social:" + topic, origin);
|
||||
return;
|
||||
}
|
||||
if (topic == "provider-update") {
|
||||
// a provider has self-updated its manifest, we need to update our cache
|
||||
// and reload the provider.
|
||||
let provider = data;
|
||||
SocialService.getOrderedProviderList(function(providers) {
|
||||
Social._updateProviderCache(providers);
|
||||
provider.reload();
|
||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||
});
|
||||
Social._updateProviderCache(providers);
|
||||
let provider = Social._getProviderFromOrigin(origin);
|
||||
provider.reload();
|
||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -259,6 +263,10 @@ this.Social = {
|
||||
return null;
|
||||
},
|
||||
|
||||
getManifestByOrigin: function(origin) {
|
||||
return SocialService.getManifestByOrigin(origin);
|
||||
},
|
||||
|
||||
installProvider: function(doc, data, installCallback) {
|
||||
SocialService.installProvider(doc, data, installCallback);
|
||||
},
|
||||
|
@ -62,14 +62,6 @@ let SocialServiceInternal = {
|
||||
}
|
||||
}
|
||||
},
|
||||
getManifestByOrigin: function(origin) {
|
||||
for (let manifest of SocialServiceInternal.manifests) {
|
||||
if (origin == manifest.origin) {
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getManifestPrefname: function(origin) {
|
||||
// Retrieve the prefname for a given origin/manifest.
|
||||
// If no existing pref, return a generated prefname.
|
||||
@ -391,7 +383,7 @@ this.SocialService = {
|
||||
});
|
||||
return;
|
||||
}
|
||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
||||
let manifest = SocialService.getManifestByOrigin(origin);
|
||||
if (manifest) {
|
||||
let addon = new AddonWrapper(manifest);
|
||||
AddonManagerPrivate.callAddonListeners("onEnabling", addon, false);
|
||||
@ -416,7 +408,7 @@ this.SocialService = {
|
||||
ActiveProviders.add(provider.origin);
|
||||
|
||||
this.getOrderedProviderList(function (providers) {
|
||||
this._notifyProviderListeners("provider-added", providers);
|
||||
this._notifyProviderListeners("provider-enabled", provider.origin, providers);
|
||||
if (onDone)
|
||||
onDone(provider);
|
||||
}.bind(this));
|
||||
@ -429,7 +421,7 @@ this.SocialService = {
|
||||
throw new Error("SocialService.removeProvider: no provider with origin " + origin + " exists!");
|
||||
|
||||
let provider = SocialServiceInternal.providers[origin];
|
||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
||||
let manifest = SocialService.getManifestByOrigin(origin);
|
||||
let addon = manifest && new AddonWrapper(manifest);
|
||||
if (addon) {
|
||||
AddonManagerPrivate.callAddonListeners("onDisabling", addon, false);
|
||||
@ -450,7 +442,7 @@ this.SocialService = {
|
||||
}
|
||||
|
||||
this.getOrderedProviderList(function (providers) {
|
||||
this._notifyProviderListeners("provider-removed", providers);
|
||||
this._notifyProviderListeners("provider-disabled", origin, providers);
|
||||
if (onDone)
|
||||
onDone();
|
||||
}.bind(this));
|
||||
@ -471,6 +463,15 @@ this.SocialService = {
|
||||
});
|
||||
},
|
||||
|
||||
getManifestByOrigin: function(origin) {
|
||||
for (let manifest of SocialServiceInternal.manifests) {
|
||||
if (origin == manifest.origin) {
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Returns an array of installed providers, sorted by frecency
|
||||
getOrderedProviderList: function(onDone) {
|
||||
SocialServiceInternal.orderedProviders(onDone);
|
||||
@ -488,10 +489,10 @@ this.SocialService = {
|
||||
this._providerListeners.delete(listener);
|
||||
},
|
||||
|
||||
_notifyProviderListeners: function (topic, data) {
|
||||
_notifyProviderListeners: function (topic, origin, providers) {
|
||||
for (let [listener, ] of this._providerListeners) {
|
||||
try {
|
||||
listener(topic, data);
|
||||
listener(topic, origin, providers);
|
||||
} catch (ex) {
|
||||
Components.utils.reportError("SocialService: provider listener threw an exception: " + ex);
|
||||
}
|
||||
@ -499,7 +500,7 @@ this.SocialService = {
|
||||
},
|
||||
|
||||
_manifestFromData: function(type, data, principal) {
|
||||
let sameOriginRequired = ['workerURL', 'sidebarURL', 'shareURL'];
|
||||
let sameOriginRequired = ['workerURL', 'sidebarURL', 'shareURL', 'statusURL'];
|
||||
|
||||
if (type == 'directory') {
|
||||
// directory provided manifests must have origin in manifest, use that
|
||||
@ -517,8 +518,9 @@ this.SocialService = {
|
||||
// iconURL and name are required
|
||||
// iconURL may be a different origin (CDN or data url support) if this is
|
||||
// a whitelisted or directory listed provider
|
||||
if (!data['workerURL'] && !data['sidebarURL'] && !data['shareURL']) {
|
||||
Cu.reportError("SocialService.manifestFromData manifest missing required workerURL or sidebarURL.");
|
||||
let providerHasFeatures = [url for (url of sameOriginRequired) if (data[url])].length > 0;
|
||||
if (!providerHasFeatures) {
|
||||
Cu.reportError("SocialService.manifestFromData manifest missing required urls.");
|
||||
return null;
|
||||
}
|
||||
if (!data['name'] || !data['iconURL']) {
|
||||
@ -596,7 +598,10 @@ this.SocialService = {
|
||||
aAddon.userDisabled = false;
|
||||
}
|
||||
schedule(function () {
|
||||
this._installProvider(aDOMDocument, data, installCallback);
|
||||
this._installProvider(aDOMDocument, data, aManifest => {
|
||||
this._notifyProviderListeners("provider-installed", aManifest.origin);
|
||||
installCallback(aManifest);
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
@ -661,7 +666,7 @@ this.SocialService = {
|
||||
* have knowledge of the currently selected provider here, we will notify
|
||||
* the front end to deal with any reload.
|
||||
*/
|
||||
updateProvider: function(aUpdateOrigin, aManifest, aCallback) {
|
||||
updateProvider: function(aUpdateOrigin, aManifest) {
|
||||
let originUri = Services.io.newURI(aUpdateOrigin, null, null);
|
||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
||||
let installType = this.getOriginActivationType(aUpdateOrigin);
|
||||
@ -682,13 +687,15 @@ this.SocialService = {
|
||||
let provider = new SocialProvider(manifest);
|
||||
SocialServiceInternal.providers[provider.origin] = provider;
|
||||
// update the cache and ui, reload provider if necessary
|
||||
this._notifyProviderListeners("provider-update", provider);
|
||||
this.getOrderedProviderList(providers => {
|
||||
this._notifyProviderListeners("provider-update", provider.origin, providers);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
uninstallProvider: function(origin, aCallback) {
|
||||
let manifest = SocialServiceInternal.getManifestByOrigin(origin);
|
||||
let manifest = SocialService.getManifestByOrigin(origin);
|
||||
let addon = new AddonWrapper(manifest);
|
||||
addon.uninstall(aCallback);
|
||||
}
|
||||
@ -720,6 +727,7 @@ function SocialProvider(input) {
|
||||
this.workerURL = input.workerURL;
|
||||
this.sidebarURL = input.sidebarURL;
|
||||
this.shareURL = input.shareURL;
|
||||
this.statusURL = input.statusURL;
|
||||
this.origin = input.origin;
|
||||
let originUri = Services.io.newURI(input.origin, null, null);
|
||||
this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
||||
@ -766,7 +774,7 @@ SocialProvider.prototype = {
|
||||
},
|
||||
|
||||
get manifest() {
|
||||
return SocialServiceInternal.getManifestByOrigin(this.origin);
|
||||
return SocialService.getManifestByOrigin(this.origin);
|
||||
},
|
||||
|
||||
// Reference to a workerAPI object for this provider. Null if the provider has
|
||||
@ -1012,7 +1020,7 @@ function getPrefnameFromOrigin(origin) {
|
||||
function AddonInstaller(sourceURI, aManifest, installCallback) {
|
||||
aManifest.updateDate = Date.now();
|
||||
// get the existing manifest for installDate
|
||||
let manifest = SocialServiceInternal.getManifestByOrigin(aManifest.origin);
|
||||
let manifest = SocialService.getManifestByOrigin(aManifest.origin);
|
||||
let isNewInstall = !manifest;
|
||||
if (manifest && manifest.installDate)
|
||||
aManifest.installDate = manifest.installDate;
|
||||
@ -1088,6 +1096,7 @@ var SocialAddonProvider = {
|
||||
Services.prefs.clearUserPref(getPrefnameFromOrigin(aAddon.manifest.origin));
|
||||
aAddon.pendingOperations -= AddonManager.PENDING_UNINSTALL;
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon);
|
||||
SocialService._notifyProviderListeners("provider-uninstalled", aAddon.manifest.origin);
|
||||
if (aCallback)
|
||||
schedule(aCallback);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user