Bug 1289549 P2 socialapi remove sidebar and chat, r=florian

MozReview-Commit-ID: JzSDh41IPBu
This commit is contained in:
Shane Caraveo 2016-08-23 15:24:06 -07:00
parent 51cf1dcb66
commit 4987c70c34
44 changed files with 27 additions and 4044 deletions

View File

@ -220,9 +220,6 @@
<menuitem id="menu_tabsSidebar"
observes="viewTabsSidebar"
label="&syncedTabs.sidebar.label;"/>
<!-- Service providers with sidebars are inserted between these two menuseperators -->
<menuseparator hidden="true"/>
<menuseparator class="social-provider-menu" hidden="true"/>
</menupopup>
</menu>
<menuseparator/>

View File

@ -108,10 +108,7 @@
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
<command id="Social:SharePage" oncommand="SocialShare.sharePage();"/>
<command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
<command id="Chat:Focus" oncommand="Cu.import('resource:///modules/Chat.jsm', {}).Chat.focus(window);"/>
</commandset>
<commandset id="placesCommands">
@ -182,7 +179,6 @@
sidebarurl="chrome://browser/content/syncedtabs/sidebar.xhtml"
oncommand="SidebarUI.toggle('viewTabsSidebar');"/>
<broadcaster id="workOfflineMenuitemState"/>
<broadcaster id="socialSidebarBroadcaster" hidden="true"/>
<broadcaster id="devtoolsMenuBroadcaster_PageSource"
label="&pageSourceCmd.label;"
@ -317,16 +313,6 @@
<key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
#endif
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Chat:Focus"
#ifdef XP_MACOSX
# Sadly the devtools uses shift-accel-c on non-mac and alt-accel-c everywhere else
# So we just use the other
modifiers="accel,shift"
#else
modifiers="accel,alt"
#endif
/>
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
#ifdef XP_MACOSX

View File

@ -4,16 +4,11 @@
// the "exported" symbols
var SocialUI,
SocialFlyout,
SocialShare,
SocialSidebar,
SocialActivationListener;
(function() {
XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame",
"resource:///modules/PanelFrame.jsm");
XPCOMUtils.defineLazyGetter(this, "OpenGraphBuilder", function() {
let tmp = {};
Cu.import("resource:///modules/Social.jsm", tmp);
@ -26,18 +21,6 @@ XPCOMUtils.defineLazyGetter(this, "DynamicResizeWatcher", function() {
return tmp.DynamicResizeWatcher;
});
XPCOMUtils.defineLazyGetter(this, "sizeSocialPanelToContent", function() {
let tmp = {};
Cu.import("resource:///modules/Social.jsm", tmp);
return tmp.sizeSocialPanelToContent;
});
XPCOMUtils.defineLazyGetter(this, "hookWindowCloseForPanelClose", function() {
let tmp = {};
Cu.import("resource://gre/modules/MozSocialAPI.jsm", tmp);
return tmp.hookWindowCloseForPanelClose;
});
SocialUI = {
_initialized: false,
@ -50,28 +33,14 @@ SocialUI = {
mm.loadFrameScript("chrome://browser/content/content.js", true);
mm.loadFrameScript("chrome://browser/content/social-content.js", true);
Services.obs.addObserver(this, "social:ambient-notification-changed", 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);
Services.obs.addObserver(this, "social:provider-disabled", false);
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
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.
document.getElementById("viewSidebarMenu").addEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
document.getElementById("social-statusarea-popup").addEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
Social.init().then((update) => {
if (update)
this._providersChanged();
// handle SessionStore for the sidebar state
SocialSidebar.restoreWindowState();
});
this._initialized = true;
@ -82,71 +51,23 @@ SocialUI = {
if (!this._initialized) {
return;
}
SocialSidebar.saveWindowState();
Services.obs.removeObserver(this, "social:ambient-notification-changed");
Services.obs.removeObserver(this, "social:providers-changed");
Services.obs.removeObserver(this, "social:provider-reload");
Services.obs.removeObserver(this, "social:provider-enabled");
Services.obs.removeObserver(this, "social:provider-disabled");
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);
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":
break;
case "social:provider-disabled":
SocialSidebar.disableProvider(data);
break;
case "social:provider-reload":
// if the reloaded provider is our current provider, fall through
// to social:providers-changed so the ui will be reset
if (!SocialSidebar.provider || SocialSidebar.provider.origin != data)
return;
// currently only the sidebar and flyout have a selected provider.
// sidebar provider has changed (possibly to null), ensure the content
// is unloaded and the frames are reset, they will be loaded in
// providers-changed below if necessary.
SocialSidebar.unloadSidebar();
SocialFlyout.unload();
// fall through to providers-changed to ensure the reloaded provider
// is correctly reflected in any UI and the multi-provider menu
case "social:providers-changed":
this._providersChanged();
break;
// Provider-specific notifications
case "social:ambient-notification-changed":
break;
case "nsPref:changed":
if (data == "social.toast-notifications.enabled") {
SocialSidebar.updateToggleNotifications();
}
break;
}
},
_providersChanged: function() {
SocialSidebar.clearProviderMenus();
SocialSidebar.update();
SocialShare.populateProviderMenu();
},
@ -246,9 +167,6 @@ SocialActivationListener = {
Social.installProvider(data, function(manifest) {
Social.activateFromOrigin(manifest.origin, function(provider) {
if (provider.sidebarURL) {
SocialSidebar.show(provider.origin);
}
if (provider.shareURL) {
// Ensure that the share button is somewhere usable.
// SocialShare.shareButton may return null if it is in the menu-panel
@ -285,138 +203,6 @@ SocialActivationListener = {
}
}
SocialFlyout = {
get panel() {
return document.getElementById("social-flyout-panel");
},
get iframe() {
if (!this.panel.firstChild)
this._createFrame();
return this.panel.firstChild;
},
dispatchPanelEvent: function(name) {
let doc = this.iframe.contentDocument;
let evt = doc.createEvent("CustomEvent");
evt.initCustomEvent(name, true, true, {});
doc.documentElement.dispatchEvent(evt);
},
_createFrame: function() {
let panel = this.panel;
if (!SocialUI.enabled || panel.firstChild)
return;
// create and initialize the panel for this window
let iframe = document.createElement("browser");
iframe.setAttribute("type", "content");
iframe.setAttribute("class", "social-panel-frame");
iframe.setAttribute("flex", "1");
iframe.setAttribute("message", "true");
iframe.setAttribute("messagemanagergroup", "social");
iframe.setAttribute("disablehistory", "true");
iframe.setAttribute("tooltip", "aHTMLTooltip");
iframe.setAttribute("context", "contentAreaContextMenu");
iframe.setAttribute("origin", SocialSidebar.provider.origin);
panel.appendChild(iframe);
this.messageManager.sendAsyncMessage("Social:SetErrorURL",
{ template: "about:socialerror?mode=compactInfo&origin=%{origin}" });
},
get messageManager() {
// The xbl bindings for the iframe may not exist yet, so we can't
// access iframe.messageManager directly - but can get at it with this dance.
return this.iframe.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;
},
unload: function() {
let panel = this.panel;
panel.hidePopup();
if (!panel.firstChild)
return
let iframe = panel.firstChild;
panel.removeChild(iframe);
},
onShown: function(aEvent) {
let panel = this.panel;
let iframe = this.iframe;
this._dynamicResizer = new DynamicResizeWatcher();
iframe.docShellIsActive = true;
if (iframe.contentDocument.readyState == "complete") {
this._dynamicResizer.start(panel, iframe);
} else {
// first time load, wait for load and dispatch after load
let mm = this.messageManager;
mm.addMessageListener("DOMContentLoaded", function panelBrowserOnload(e) {
mm.removeMessageListener("DOMContentLoaded", panelBrowserOnload);
setTimeout(function() {
if (SocialFlyout._dynamicResizer) { // may go null if hidden quickly
SocialFlyout._dynamicResizer.start(panel, iframe);
}
}, 0);
});
}
},
onHidden: function(aEvent) {
this._dynamicResizer.stop();
this._dynamicResizer = null;
this.iframe.docShellIsActive = false;
},
load: function(aURL, cb) {
if (!SocialSidebar.provider)
return;
this.panel.hidden = false;
let iframe = this.iframe;
// same url with only ref difference does not cause a new load, so we
// want to go right to the callback
let src = iframe.contentDocument && iframe.contentDocument.documentURIObject;
if (!src || !src.equalsExceptRef(Services.io.newURI(aURL, null, null))) {
let mm = this.messageManager;
mm.addMessageListener("DOMContentLoaded", function documentLoaded(e) {
mm.removeMessageListener("DOMContentLoaded", documentLoaded);
cb();
});
iframe.setAttribute("src", aURL);
} else {
// we still need to set the src to trigger the contents hashchange event
// for ref changes
iframe.setAttribute("src", aURL);
cb();
}
},
open: function(aURL, yOffset, aCallback) {
// Hide any other social panels that may be open.
document.getElementById("social-notification-panel").hidePopup();
if (!SocialUI.enabled)
return;
let panel = this.panel;
let iframe = this.iframe;
this.load(aURL, function() {
sizeSocialPanelToContent(panel, iframe);
let anchor = document.getElementById("social-sidebar-browser");
if (panel.state == "open") {
panel.moveToAnchor(anchor, "start_before", 0, yOffset, false);
} else {
panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
}
if (aCallback) {
try {
aCallback(iframe.contentWindow);
} catch(e) {
Cu.reportError(e);
}
}
});
}
}
SocialShare = {
get _dynamicResizer() {
delete this._dynamicResizer;
@ -448,6 +234,7 @@ SocialShare = {
let mm = this.messageManager;
mm.removeMessageListener("PageVisibility:Show", this);
mm.removeMessageListener("PageVisibility:Hide", this);
mm.removeMessageListener("Social:DOMWindowClose", this);
this.iframe.removeEventListener("load", this);
this.iframe.remove();
}
@ -475,6 +262,7 @@ SocialShare = {
mm.sendAsyncMessage("Social:SetErrorURL",
{ template: "about:socialerror?mode=compactInfo&origin=%{origin}&url=%{url}" });
iframe.addEventListener("load", this, true);
mm.addMessageListener("Social:DOMWindowClose", this);
this.populateProviderMenu();
},
@ -494,6 +282,9 @@ SocialShare = {
case "PageVisibility:Hide":
SocialShare._dynamicResizer.stop();
break;
case "Social:DOMWindowClose":
this.panel.hidePopup();
break;
}
},
@ -714,353 +505,4 @@ SocialShare = {
}
};
SocialSidebar = {
_openStartTime: 0,
get browser() {
return document.getElementById("social-sidebar-browser");
},
// Whether the sidebar can be shown for this window.
get canShow() {
if (!SocialUI.enabled || document.fullscreenElement)
return false;
return Social.providers.some(p => p.sidebarURL);
},
// Whether the user has toggled the sidebar on (for windows where it can appear)
get opened() {
let broadcaster = document.getElementById("socialSidebarBroadcaster");
return !broadcaster.hidden;
},
restoreWindowState: function() {
// Window state is used to allow different sidebar providers in each window.
// We also store the provider used in a pref as the default sidebar to
// maintain that state for users who do not restore window state. The
// existence of social.sidebar.provider means the sidebar is open with that
// provider.
this._initialized = true;
if (!this.canShow)
return;
if (Services.prefs.prefHasUserValue("social.provider.current")) {
// "upgrade" when the first window opens if we have old prefs. We get the
// values from prefs this one time, window state will be saved when this
// window is closed.
let origin = Services.prefs.getCharPref("social.provider.current");
Services.prefs.clearUserPref("social.provider.current");
// social.sidebar.open default was true, but we only opened if there was
// a current provider
let opened = origin && true;
if (Services.prefs.prefHasUserValue("social.sidebar.open")) {
opened = origin && Services.prefs.getBoolPref("social.sidebar.open");
Services.prefs.clearUserPref("social.sidebar.open");
}
let data = {
"hidden": !opened,
"origin": origin
};
SessionStore.setWindowValue(window, "socialSidebar", JSON.stringify(data));
}
let data = SessionStore.getWindowValue(window, "socialSidebar");
// if this window doesn't have it's own state, use the state from the opener
if (!data && window.opener && !window.opener.closed) {
try {
data = SessionStore.getWindowValue(window.opener, "socialSidebar");
} catch(e) {
// Window is not tracked, which happens on osx if the window is opened
// from the hidden window. That happens when you close the last window
// without quiting firefox, then open a new window.
}
}
if (data) {
data = JSON.parse(data);
this.browser.setAttribute("origin", data.origin);
if (!data.hidden)
this.show(data.origin);
} else if (Services.prefs.prefHasUserValue("social.sidebar.provider")) {
// no window state, use the global state if it is available
this.show(Services.prefs.getCharPref("social.sidebar.provider"));
}
},
saveWindowState: function() {
let broadcaster = document.getElementById("socialSidebarBroadcaster");
let sidebarOrigin = this.browser.getAttribute("origin");
let data = {
"hidden": broadcaster.hidden,
"origin": sidebarOrigin
};
if (broadcaster.hidden) {
Services.telemetry.getHistogramById("SOCIAL_SIDEBAR_OPEN_DURATION").add(Date.now() / 1000 - this._openStartTime);
} else {
this._openStartTime = Date.now() / 1000;
}
// Save a global state for users who do not restore state.
if (broadcaster.hidden)
Services.prefs.clearUserPref("social.sidebar.provider");
else
Services.prefs.setCharPref("social.sidebar.provider", sidebarOrigin);
try {
SessionStore.setWindowValue(window, "socialSidebar", JSON.stringify(data));
} catch(e) {
// window not tracked during uninit
}
},
setSidebarVisibilityState: function(aEnabled) {
let sbrowser = document.getElementById("social-sidebar-browser");
// it's possible we'll be called twice with aEnabled=false so let's
// just assume we may often be called with the same state.
if (aEnabled == sbrowser.docShellIsActive)
return;
sbrowser.docShellIsActive = aEnabled;
},
updateToggleNotifications: function() {
let command = document.getElementById("Social:ToggleNotifications");
command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
command.setAttribute("hidden", !SocialUI.enabled);
},
update: function SocialSidebar_update() {
// ensure we never update before restoreWindowState
if (!this._initialized)
return;
this.ensureProvider();
this.updateToggleNotifications();
this._updateHeader();
clearTimeout(this._unloadTimeoutId);
// Hide the toggle menu item if the sidebar cannot appear
let command = document.getElementById("Social:ToggleSidebar");
command.setAttribute("hidden", this.canShow ? "false" : "true");
// Hide the sidebar if it cannot appear, or has been toggled off.
// Also set the command "checked" state accordingly.
let hideSidebar = !this.canShow || !this.opened;
let broadcaster = document.getElementById("socialSidebarBroadcaster");
broadcaster.hidden = hideSidebar;
command.setAttribute("checked", !hideSidebar);
let sbrowser = this.browser;
if (hideSidebar) {
sbrowser.messageManager.removeMessageListener("DOMContentLoaded", SocialSidebar._loadListener);
this.setSidebarVisibilityState(false);
// If we've been disabled, unload the sidebar content immediately;
// if the sidebar was just toggled to invisible, wait a timeout
// before unloading.
if (!this.canShow) {
this.unloadSidebar();
} else {
this._unloadTimeoutId = setTimeout(
this.unloadSidebar,
Services.prefs.getIntPref("social.sidebar.unload_timeout_ms")
);
}
} else {
sbrowser.setAttribute("origin", this.provider.origin);
// Make sure the right sidebar URL is loaded
if (sbrowser.getAttribute("src") != this.provider.sidebarURL) {
sbrowser.setAttribute("src", this.provider.sidebarURL);
PopupNotifications.locationChange(sbrowser);
document.getElementById("social-sidebar-button").setAttribute("loading", "true");
sbrowser.messageManager.addMessageListener("DOMContentLoaded", SocialSidebar._loadListener);
} else {
// if the document has not loaded, delay until it is
if (sbrowser.contentDocument.readyState != "complete") {
document.getElementById("social-sidebar-button").setAttribute("loading", "true");
sbrowser.messageManager.addMessageListener("DOMContentLoaded", SocialSidebar._loadListener);
} else {
this.setSidebarVisibilityState(true);
}
}
}
this._updateCheckedMenuItems(this.opened && this.provider ? this.provider.origin : null);
},
_onclick: function() {
Services.telemetry.getHistogramById("SOCIAL_PANEL_CLICKS").add(3);
},
_loadListener: function SocialSidebar_loadListener() {
let sbrowser = SocialSidebar.browser;
sbrowser.messageManager.removeMessageListener("DOMContentLoaded", SocialSidebar._loadListener);
document.getElementById("social-sidebar-button").removeAttribute("loading");
SocialSidebar.setSidebarVisibilityState(true);
sbrowser.addEventListener("click", SocialSidebar._onclick, true);
},
unloadSidebar: function SocialSidebar_unloadSidebar() {
let sbrowser = SocialSidebar.browser;
if (!sbrowser.hasAttribute("origin"))
return;
sbrowser.removeEventListener("click", SocialSidebar._onclick, true);
sbrowser.stop();
sbrowser.removeAttribute("origin");
sbrowser.setAttribute("src", "about:blank");
// We need to explicitly create a new content viewer because the old one
// doesn't get destroyed until about:blank has loaded (which does not happen
// as long as the element is hidden).
sbrowser.messageManager.sendAsyncMessage("Social:ClearFrame");
SocialFlyout.unload();
},
_unloadTimeoutId: 0,
_provider: null,
ensureProvider: function() {
if (this._provider)
return;
// origin for sidebar is persisted, so get the previously selected sidebar
// first, otherwise fallback to the first provider in the list
let origin = this.browser.getAttribute("origin");
let providers = Social.providers.filter(p => p.sidebarURL);
let provider;
if (origin)
provider = Social._getProviderFromOrigin(origin);
if (!provider && providers.length > 0)
provider = providers[0];
if (provider)
this.provider = provider;
},
get provider() {
return this._provider;
},
set provider(provider) {
if (!provider || provider.sidebarURL) {
this._provider = provider;
this._updateHeader();
this._updateCheckedMenuItems(provider && provider.origin);
this.update();
}
},
disableProvider: function(origin) {
if (this._provider && this._provider.origin == origin) {
this._provider = null;
// force a selection of the next provider if there is one
this.ensureProvider();
}
},
_updateHeader: function() {
let provider = this.provider;
let image, title;
if (provider) {
image = "url(" + (provider.icon32URL || provider.iconURL) + ")";
title = provider.name;
}
document.getElementById("social-sidebar-favico").style.listStyleImage = image;
document.getElementById("social-sidebar-title").value = title;
},
_updateCheckedMenuItems: function(origin) {
// update selected menuitems
let menuitems = document.getElementsByClassName("social-provider-menuitem");
for (let mi of menuitems) {
if (origin && mi.getAttribute("origin") == origin) {
mi.setAttribute("checked", "true");
mi.setAttribute("oncommand", "SocialSidebar.hide();");
} else if (mi.getAttribute("checked")) {
mi.removeAttribute("checked");
mi.setAttribute("oncommand", "SocialSidebar.show(this.getAttribute('origin'));");
}
}
},
show: function(origin) {
// always show the sidebar, and set the provider
let broadcaster = document.getElementById("socialSidebarBroadcaster");
broadcaster.hidden = false;
if (origin)
this.provider = Social._getProviderFromOrigin(origin);
else
SocialSidebar.update();
this.saveWindowState();
Services.telemetry.getHistogramById("SOCIAL_SIDEBAR_STATE").add(true);
},
hide: function() {
let broadcaster = document.getElementById("socialSidebarBroadcaster");
broadcaster.hidden = true;
this._updateCheckedMenuItems();
this.clearProviderMenus();
SocialSidebar.update();
this.saveWindowState();
Services.telemetry.getHistogramById("SOCIAL_SIDEBAR_STATE").add(false);
},
toggleSidebar: function SocialSidebar_toggle() {
let broadcaster = document.getElementById("socialSidebarBroadcaster");
if (broadcaster.hidden)
this.show();
else
this.hide();
},
populateSidebarMenu: function(event) {
// Providers are removed from the view->sidebar menu when there is a change
// in providers, so we only have to populate onshowing if there are no
// provider menus. We populate this menu so long as there are enabled
// providers with sidebars.
let popup = event.target;
let providerMenuSeps = popup.getElementsByClassName("social-provider-menu");
if (providerMenuSeps[0].previousSibling.nodeName == "menuseparator")
SocialSidebar.populateProviderMenu(providerMenuSeps[0]);
},
clearProviderMenus: function() {
// called when there is a change in the provider list we clear all menus,
// they will be repopulated when the menu is shown
let providerMenuSeps = document.getElementsByClassName("social-provider-menu");
for (let providerMenuSep of providerMenuSeps) {
while (providerMenuSep.previousSibling.nodeName == "menuitem") {
let menu = providerMenuSep.parentNode;
menu.removeChild(providerMenuSep.previousSibling);
}
}
},
populateProviderMenu: function(providerMenuSep) {
let menu = providerMenuSep.parentNode;
// selectable providers are inserted before the provider-menu seperator,
// remove any menuitems in that area
while (providerMenuSep.previousSibling.nodeName == "menuitem") {
menu.removeChild(providerMenuSep.previousSibling);
}
// only show a selection in the sidebar header menu if there is more than one
let providers = Social.providers.filter(p => p.sidebarURL);
if (providers.length < 2 && menu.id != "viewSidebarMenu") {
providerMenuSep.hidden = true;
return;
}
let topSep = providerMenuSep.previousSibling;
for (let provider of providers) {
let menuitem = document.createElement("menuitem");
menuitem.className = "menuitem-iconic social-provider-menuitem";
menuitem.setAttribute("image", provider.iconURL);
menuitem.setAttribute("label", provider.name);
menuitem.setAttribute("origin", provider.origin);
if (this.opened && provider == this.provider) {
menuitem.setAttribute("checked", "true");
menuitem.setAttribute("oncommand", "SocialSidebar.hide();");
} else {
menuitem.setAttribute("oncommand", "SocialSidebar.show(this.getAttribute('origin'));");
}
menu.insertBefore(menuitem, providerMenuSep);
}
topSep.hidden = topSep.nextSibling == providerMenuSep;
providerMenuSep.hidden = !providerMenuSep.nextSibling;
}
}
})();

View File

@ -19,30 +19,6 @@
%endif
}
/* These values are chosen to keep the Loop detached chat window from
* getting too small. When it's too small, three bad things happen:
*
* - It looks terrible
* - It's not really usable
* - It's possible for the user to be transmitting video that's cropped by the
* the edge of the window, so that they're not aware of it, which is a
* privacy problem
*
* Note that if the chat window grows more users than Loop who want this
* ability, we'll need to generalize. A partial patch for this is in
* bug 1112264.
*/
#chat-window {
/*
* In some ideal world, we'd have a simple way to express "block resizing
* along any dimension beyond the point at which an overflow event would
* occur". But none of -moz-{fit,max,min}-content do what we want here. So..
*/
min-width: 260px;
min-height: 315px;
}
#main-window[customize-entered] {
min-width: -moz-fit-content;
}
@ -926,77 +902,6 @@ notification[value="translation"] {
-moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
}
/* Social */
/* Note the chatbox 'width' values are duplicated in socialchat.xml */
chatbox {
-moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
transition: height 150ms ease-out, width 150ms ease-out;
height: 290px;
width: 300px; /* CHAT_WIDTH_OPEN in socialchat.xml */
}
chatbox[customSize] {
width: 350px; /* CHAT_WIDTH_OPEN_ALT in socialchat.xml */
}
#chat-window[customSize] {
min-width: 350px;
}
chatbox[customSize="loopChatEnabled"] {
/* 430px as defined per UX */
height: 430px;
}
#chat-window[customSize="loopChatEnabled"] {
/* 325px + 30px top bar height. */
min-height: calc(325px + 30px);
}
chatbox[customSize="loopChatMessageAppended"] {
/* 430px as defined per UX */
height: 430px;
}
chatbox[customSize="loopChatDisabledMessageAppended"] {
/* 388px as defined per UX */
height: 388px;
}
#chat-window[customSize="loopChatMessageAppended"] {
/* 445px + 30px top bar height. */
min-height: calc(400px + 30px);
}
chatbox[minimized="true"] {
width: 160px;
height: 20px; /* CHAT_WIDTH_MINIMIZED in socialchat.xml */
}
chatbar {
-moz-binding: url("chrome://browser/content/socialchat.xml#chatbar");
height: 0;
max-height: 0;
}
.chatbar-innerbox {
margin: -285px 0 0;
}
chatbar[customSize] > .chatbar-innerbox {
/* 450px to make room for the maximum custom-size chatbox; currently 'loopChatMessageAppended'. */
margin-top: -450px;
}
/* Apply crisp rendering for favicons at exactly 2dppx resolution */
@media (resolution: 2dppx) {
#social-sidebar-favico,
.social-status-button,
.chat-status-icon {
image-rendering: -moz-crisp-edges;
}
}
/** See bug 872317 for why the following rule is necessary. */
#downloads-button {
@ -1028,18 +933,6 @@ toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-
visibility: hidden;
}
/* hide chat chrome when chat is fullscreen */
#chat-window[sizemode="fullscreen"] chatbox > .chat-titlebar {
display: none;
}
/* hide chatbar and sidebar if browser tab is fullscreen */
#main-window[inFullscreen][inDOMFullscreen] chatbar,
#main-window[inFullscreen][inDOMFullscreen] #social-sidebar-box,
#main-window[inFullscreen][inDOMFullscreen] #social-sidebar-splitter {
display: none;
}
/* Combobox dropdown renderer */
#ContentSelectDropdown > menupopup {
/* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with

View File

@ -2282,9 +2282,6 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
!E10SUtils.canLoadURIInProcess(args.URL, contentProcess)
}
// In the case of sidebars and chat windows, gBrowser is defined but null,
// because no #content element exists. For these cases, we need to find
// the most recent browser window.
// In the case of popups, we need to find a non-popup browser window.
if (!tabBrowser || !window.toolbar.visible) {
// This returns only non-popup browser windows by default.

View File

@ -272,23 +272,6 @@
<hbox id="share-container" flex="1"/>
</panel>
<panel id="social-notification-panel"
class="social-panel"
type="arrow"
hidden="true"
noautofocus="true"/>
<panel id="social-flyout-panel"
class="social-panel"
onpopupshown="SocialFlyout.onShown()"
onpopuphidden="SocialFlyout.onHidden()"
side="right"
type="arrow"
hidden="true"
flip="slide"
rolluponmousewheel="true"
noautofocus="true"
position="topcenter topright"/>
<menupopup id="toolbar-context-menu"
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
<menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
@ -1081,56 +1064,6 @@
contentcontextmenu="contentAreaContextMenu"
autocompletepopup="PopupAutoComplete"
selectmenulist="ContentSelectDropdown"/>
<chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
</vbox>
<splitter id="social-sidebar-splitter"
class="chromeclass-extrachrome sidebar-splitter"
observes="socialSidebarBroadcaster"/>
<vbox id="social-sidebar-box"
class="chromeclass-extrachrome"
observes="socialSidebarBroadcaster"
persist="width">
<sidebarheader id="social-sidebar-header" class="sidebar-header" align="center">
<image id="social-sidebar-favico"/>
<label id="social-sidebar-title" class="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
<toolbarbutton id="social-sidebar-button"
class="toolbarbutton-1"
type="menu">
<menupopup id="social-statusarea-popup" position="after_end">
<menuitem class="social-toggle-sidebar-menuitem"
type="checkbox"
autocheck="false"
command="Social:ToggleSidebar"
label="&social.toggleSidebar.label;"
accesskey="&social.toggleSidebar.accesskey;"/>
<menuitem class="social-toggle-notifications-menuitem"
type="checkbox"
autocheck="false"
command="Social:ToggleNotifications"
label="&social.toggleNotifications.label;"
accesskey="&social.toggleNotifications.accesskey;"/>
<menuseparator/>
<menuseparator class="social-provider-menu" hidden="true"/>
<menuitem class="social-addons-menuitem" command="Social:Addons"
label="&social.addons.label;"/>
<menuitem label="&social.learnMore.label;"
accesskey="&social.learnMore.accesskey;"
oncommand="SocialUI.showLearnMore();"/>
</menupopup>
</toolbarbutton>
</sidebarheader>
<browser id="social-sidebar-browser"
type="content"
context="contentAreaContextMenu"
message="true"
messagemanagergroup="social"
disableglobalhistory="true"
tooltip="aHTMLTooltip"
popupnotificationanchor="social-sidebar-favico"
flex="1"
style="min-width: 14em; width: 18em; max-width: 36em;"/>
</vbox>
<vbox id="browser-border-end" hidden="true" layer="true"/>
</hbox>

View File

@ -1,170 +0,0 @@
#filter substitution
<?xml version="1.0"?>
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
#include browser-doctype.inc
<window id="chat-window"
windowtype="Social:Chat"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&mainWindow.title;"
onload="gChatWindow.onLoad();"
onunload="gChatWindow.onUnload();"
macanimationtype="document"
fullscreenbutton="true"
# width and height are also used in socialchat.xml: chatbar dragend handler
width="400px"
height="420px"
persist="screenX screenY width height sizemode">
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
<script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
#include global-scripts.inc
<script type="application/javascript">
var gChatWindow = {
// cargo-culted from browser.js for nonBrowserStartup, but we're slightly
// different what what we need to leave enabled
onLoad: function() {
// Disable inappropriate commands / submenus
var disabledItems = ['Browser:SavePage', 'Browser:OpenFile',
'Browser:SendLink', 'cmd_pageSetup', 'cmd_print',
'cmd_find', 'cmd_findAgain', 'cmd_findPrevious',
'cmd_fullZoomReduce', 'cmd_fullZoomEnlarge', 'cmd_fullZoomReset',
#ifdef XP_MACOSX
'viewToolbarsMenu', 'viewSidebarMenuMenu',
'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu',
#else
'Browser:OpenLocation', 'Tools:Search',
#endif
'Tools:Sanitize', 'Tools:DevToolbox',
'key_selectTab1', 'key_selectTab2', 'key_selectTab3',
'key_selectTab4', 'key_selectTab5', 'key_selectTab6',
'key_selectTab7', 'key_selectTab8', 'key_selectLastTab',
'viewHistorySidebar', 'viewBookmarksSidebar',
'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs'];
for (let disabledItem of disabledItems) {
document.getElementById(disabledItem).setAttribute("disabled", "true");
}
window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
new chatBrowserAccess();
// initialise the offline listener
BrowserOffline.init();
},
onUnload: function() {
BrowserOffline.uninit();
}
}
// define a popupnotifications handler for this window. we don't use
// an iconbox here, and only support the browser frame for chat.
XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
let tmp = {};
Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
try {
return new tmp.PopupNotifications(document.getElementById("chatter").content,
document.getElementById("notification-popup"),
null);
} catch (ex) {
console.error(ex);
return null;
}
});
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
function chatBrowserAccess() { }
chatBrowserAccess.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
_openURIInNewTab: function(aURI, aWhere) {
if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB)
return null;
let win = RecentWindow.getMostRecentBrowserWindow();
if (!win) {
// We couldn't find a suitable window, a new one needs to be opened.
return null;
}
let loadInBackground =
Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank",
{inBackground: loadInBackground});
let browser = win.gBrowser.getBrowserForTab(tab);
win.focus();
return browser;
},
openURI: function (aURI, aOpener, aWhere, aContext) {
let browser = this._openURIInNewTab(aURI, aWhere);
return browser ? browser.contentWindow : null;
},
openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aContext) {
let browser = this._openURIInNewTab(aURI, aWhere);
return browser ? browser.QueryInterface(Ci.nsIFrameLoaderOwner) : null;
},
isTabContentWindow: function (aWindow) {
return this.contentWindow == aWindow;
},
canClose() {
let {BrowserUtils} = Cu.import("resource://gre/modules/BrowserUtils.jsm", {});
return BrowserUtils.canCloseWindow(window);
},
};
</script>
#include browser-sets.inc
#ifdef XP_MACOSX
#include browser-menubar.inc
#endif
<popupset id="mainPopupSet">
<tooltip id="aHTMLTooltip" page="true"/>
<menupopup id="contentAreaContextMenu" pagemenu="start"
onpopupshowing="if (event.target != this)
return true;
gContextMenu = new nsContextMenu(this, event.shiftKey);
if (gContextMenu.shouldDisplay)
document.popupNode = this.triggerNode;
return gContextMenu.shouldDisplay;"
onpopuphiding="if (event.target != this)
return;
gContextMenu.hiding();
gContextMenu = null;">
#include browser-context.inc
</menupopup>
#include popup-notifications.inc
</popupset>
<commandset id="editMenuCommands"/>
<chatbox id="chatter" flex="1"/>
</window>

View File

@ -714,10 +714,6 @@ PageMetadataMessenger.init();
addEventListener("ActivateSocialFeature", function (aEvent) {
let document = content.document;
if (PrivateBrowsingUtils.isContentWindowPrivate(content)) {
Cu.reportError("cannot use social providers in private windows");
return;
}
let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (!dwu.isHandlingUserInput) {

View File

@ -1051,12 +1051,7 @@ nsContextMenu.prototype = {
},
reload: function(event) {
if (this.onSocial) {
// full reload of social provider
Social._getProviderFromOrigin(this.browser.getAttribute("origin")).reload();
} else {
BrowserReloadOrDuplicate(event);
}
BrowserReloadOrDuplicate(event);
},
// View Partial Source
@ -1064,9 +1059,6 @@ nsContextMenu.prototype = {
let inWindow = !Services.prefs.getBoolPref("view_source.tab");
let openSelectionFn = inWindow ? null : function() {
let tabBrowser = gBrowser;
// In the case of sidebars and chat windows, gBrowser is defined but null,
// because no #content element exists. For these cases, we need to find
// the most recent browser window.
// In the case of popups, we need to find a non-popup browser window.
if (!tabBrowser || !window.toolbar.visible) {
// This returns only non-popup browser windows by default.

View File

@ -44,15 +44,6 @@ addEventListener("DOMTitleChanged", function(e) {
gDOMTitleChangedByUs = 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)
});
});
addMessageListener("Social:OpenGraphData", (message) => {
let ev = new content.CustomEvent("OpenGraphData", { detail: JSON.stringify(message.data) });
content.dispatchEvent(ev);

View File

@ -1,913 +0,0 @@
<?xml version="1.0"?>
<bindings id="socialChatBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="chatbox">
<content orient="vertical" mousethrough="never">
<xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected,activity" align="baseline">
<xul:hbox flex="1" onclick="document.getBindingParent(this).onTitlebarClick(event);">
<xul:image class="chat-status-icon" xbl:inherits="src=image"/>
<xul:label class="chat-title" flex="1" xbl:inherits="crop=titlecrop,value=label" crop="end"/>
</xul:hbox>
<xul:toolbarbutton anonid="webRTC-shareScreen-icon"
class="notification-anchor-icon chat-toolbarbutton screen-icon"
oncommand="document.getBindingParent(this).showNotifications(this); event.stopPropagation();"/>
<xul:toolbarbutton anonid="webRTC-sharingScreen-icon"
class="notification-anchor-icon chat-toolbarbutton screen-icon in-use"
oncommand="document.getBindingParent(this).showNotifications(this); event.stopPropagation();"/>
<xul:toolbarbutton anonid="notification-icon" class="notification-anchor-icon chat-toolbarbutton"
oncommand="document.getBindingParent(this).showNotifications(this); event.stopPropagation();"/>
<xul:toolbarbutton anonid="minimize" class="chat-minimize-button chat-toolbarbutton"
oncommand="document.getBindingParent(this).toggle();"/>
<xul:toolbarbutton anonid="swap" class="chat-swap-button chat-toolbarbutton"
oncommand="document.getBindingParent(this).swapWindows();"/>
<xul:toolbarbutton anonid="close" class="chat-close-button chat-toolbarbutton"
oncommand="document.getBindingParent(this).close();"/>
</xul:hbox>
<xul:browser anonid="remote-content" class="chat-frame" flex="1"
context="contentAreaContextMenu"
disableglobalhistory="true"
frameType="social"
message="true"
messagemanagergroup="social"
tooltip="aHTMLTooltip"
remote="true"
xbl:inherits="src,origin"
type="content"/>
<xul:browser anonid="content" class="chat-frame" flex="1"
context="contentAreaContextMenu"
disableglobalhistory="true"
message="true"
messagemanagergroup="social"
tooltip="aHTMLTooltip"
xbl:inherits="src,origin"
type="content"/>
</content>
<implementation implements="nsIDOMEventListener, nsIMessageListener">
<constructor><![CDATA[
const kAnchorMap = new Map([
["", "notification-"],
["webRTC-shareScreen-", ""],
["webRTC-sharingScreen-", ""]
]);
const kBrowsers = [
document.getAnonymousElementByAttribute(this, "anonid", "content"),
document.getAnonymousElementByAttribute(this, "anonid", "remote-content")
];
for (let content of kBrowsers) {
for (let [getterPrefix, idPrefix] of kAnchorMap) {
let getter = getterPrefix + "popupnotificationanchor";
let anonid = (idPrefix || getterPrefix) + "icon";
content.__defineGetter__(getter, () => {
delete content[getter];
return content[getter] = document.getAnonymousElementByAttribute(
this, "anonid", anonid);
});
}
}
let mm = this.content.messageManager;
// process this._callbacks, then set to null so the chatbox creator
// knows to make new callbacks immediately.
if (this._callbacks) {
for (let callback of this._callbacks) {
callback(this);
}
this._callbacks = null;
}
mm.addMessageListener("Social:DOMTitleChanged", this);
mm.sendAsyncMessage("WaitForDOMContentLoaded");
mm.addMessageListener("DOMContentLoaded", function DOMContentLoaded(event) {
mm.removeMessageListener("DOMContentLoaded", DOMContentLoaded);
this.isActive = !this.minimized;
this._chat.loadButtonSet(this, this.getAttribute("buttonSet"));
this._deferredChatLoaded.resolve(this);
}.bind(this));
this.setActiveBrowser();
]]></constructor>
<field name="_deferredChatLoaded" readonly="true">
Promise.defer();
</field>
<property name="promiseChatLoaded">
<getter>
return this._deferredChatLoaded.promise;
</getter>
</property>
<property name="content">
<getter>
return document.getAnonymousElementByAttribute(this, "anonid",
(this.remote ? "remote-" : "") + "content");
</getter>
</property>
<field name="_chat" readonly="true">
Cu.import("resource:///modules/Chat.jsm", {}).Chat;
</field>
<property name="minimized">
<getter>
return this.getAttribute("minimized") == "true";
</getter>
<setter><![CDATA[
// Note that this.isActive is set via our transitionend handler so
// the content doesn't see intermediate values.
let parent = this.chatbar;
if (val) {
this.setAttribute("minimized", "true");
// If this chat is the selected one a new one needs to be selected.
if (parent && parent.selectedChat == this)
parent._selectAnotherChat();
} else {
this.removeAttribute("minimized");
// this chat gets selected.
if (parent)
parent.selectedChat = this;
}
]]></setter>
</property>
<property name="chatbar">
<getter>
if (this.parentNode.nodeName == "chatbar")
return this.parentNode;
return null;
</getter>
</property>
<property name="isActive">
<getter>
return this.content.docShellIsActive;
</getter>
<setter>
this.content.docShellIsActive = !!val;
// Bug 1256431 to remove socialFrameShow/Hide from hello, keep this
// until that is complete.
// let the chat frame know if it is being shown or hidden
this.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
name: val ? "socialFrameShow" : "socialFrameHide"
});
</setter>
</property>
<field name="_remote">false</field>
<property name="remote" onget="return this._remote;">
<setter><![CDATA[
this._remote = !!val;
this.setActiveBrowser();
]]></setter>
</property>
<method name="setActiveBrowser">
<body><![CDATA[
// Make sure we only show one browser element at a time.
let content = document.getAnonymousElementByAttribute(this, "anonid", "content");
let remoteContent = document.getAnonymousElementByAttribute(this, "anonid", "remote-content");
remoteContent.setAttribute("hidden", !this.remote);
content.setAttribute("hidden", this.remote);
remoteContent.removeAttribute("src");
content.removeAttribute("src");
if (this.src) {
this.setAttribute("src", this.src);
// Stop loading of the document - that is set before this method was
// called - in the now hidden browser.
(this.remote ? content : remoteContent).setAttribute("src", "about:blank");
}
]]></body>
</method>
<method name="showNotifications">
<parameter name="aAnchor"/>
<body><![CDATA[
PopupNotifications._reshowNotifications(aAnchor,
this.content);
]]></body>
</method>
<method name="swapDocShells">
<parameter name="aTarget"/>
<body><![CDATA[
aTarget.setAttribute("label", this.content.contentTitle);
aTarget.remote = this.remote;
aTarget.src = this.src;
let content = aTarget.content;
content.setAttribute("origin", this.content.getAttribute("origin"));
content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
content.swapDocShells(this.content);
// When a chat window is attached or detached, the docShell hosting
// the chat document is swapped to the newly created chat window.
// (Be it inside a popup or back inside a chatbox element attached to
// the chatbar.)
// Since a swapDocShells call does not swap the messageManager instances
// attached to a browser, we'll need to add the message listeners to
// the new messageManager. This is not a bug in swapDocShells, merely
// a design decision.
content.messageManager.addMessageListener("Social:DOMTitleChanged", content);
]]></body>
</method>
<method name="setDecorationAttributes">
<parameter name="aTarget"/>
<body><![CDATA[
if (this.hasAttribute("customSize"))
aTarget.setAttribute("customSize", this.getAttribute("customSize"));
this._chat.loadButtonSet(aTarget, this.getAttribute("buttonSet"));
]]></body>
</method>
<method name="onTitlebarClick">
<parameter name="aEvent"/>
<body><![CDATA[
if (!this.chatbar)
return;
if (aEvent.button == 0) { // left-click: toggle minimized.
this.toggle();
// if we restored it, we want to focus it.
if (!this.minimized)
this.chatbar.focus();
} else if (aEvent.button == 1) // middle-click: close chat
this.close();
]]></body>
</method>
<method name="close">
<body><![CDATA[
if (this.chatbar)
this.chatbar.remove(this);
else
window.close();
if (!this.swappingWindows)
this.dispatchEvent(new CustomEvent("ChatboxClosed"));
]]></body>
</method>
<method name="swapWindows">
<body><![CDATA[
let deferred = Promise.defer();
let title = this.getAttribute("label");
if (this.chatbar) {
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }).then(
chatbox => {
chatbox.content.messageManager.sendAsyncMessage("Social:SetDocumentTitle", {
title: title
});
deferred.resolve(chatbox);
}
);
} else {
// attach this chatbox to the topmost browser window
let Chat = Cu.import("resource:///modules/Chat.jsm").Chat;
let win = Chat.findChromeWindowForChats();
let chatbar = win.document.getElementById("pinnedchats");
let origin = this.content.getAttribute("origin");
let cb = chatbar.openChat({
origin: origin,
title: title,
url: "about:blank"
});
cb.promiseChatLoaded.then(
() => {
this.setDecorationAttributes(cb);
this.swapDocShells(cb);
chatbar.focus();
this.swappingWindows = true;
this.close();
// chatboxForURL is a map of URL -> chatbox used to avoid opening
// duplicate chat windows. Ensure reattached chat windows aren't
// registered with about:blank as their URL, otherwise reattaching
// more than one chat window isn't possible.
chatbar.chatboxForURL.delete("about:blank");
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
cb.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
name: "socialFrameAttached"
});
deferred.resolve(cb);
}
);
}
return deferred.promise;
]]></body>
</method>
<method name="toggle">
<body><![CDATA[
this.minimized = !this.minimized;
]]></body>
</method>
<method name="setTitle">
<body><![CDATA[
try {
this.setAttribute("label", this.content.contentTitle);
} catch (ex) {}
if (this.chatbar)
this.chatbar.updateTitlebar(this);
]]></body>
</method>
<method name="receiveMessage">
<parameter name="aMessage" />
<body><![CDATA[
switch (aMessage.name) {
case "Social:DOMTitleChanged":
this.setTitle();
break;
}
]]></body>
</method>
</implementation>
<handlers>
<handler event="focus" phase="capturing">
if (this.chatbar)
this.chatbar.selectedChat = this;
</handler>
<handler event="DOMTitleChanged">
this.setTitle();
</handler>
<handler event="DOMLinkAdded"><![CDATA[
// Much of this logic is from DOMLinkHandler in browser.js.
// This sets the presence icon for a chat user, we simply use favicon
// style updating.
let link = event.originalTarget;
let rel = link.rel && link.rel.toLowerCase();
if (!link || !link.ownerDocument || !rel || !link.href)
return;
if (link.rel.indexOf("icon") < 0)
return;
let ContentLinkHandler = Cu.import("resource:///modules/ContentLinkHandler.jsm", {})
.ContentLinkHandler;
let uri = ContentLinkHandler.getLinkIconURI(link);
if (!uri)
return;
// We made it this far, use it.
this.setAttribute("image", uri.spec);
if (this.chatbar)
this.chatbar.updateTitlebar(this);
]]></handler>
<handler event="transitionend">
if (this.isActive == this.minimized)
this.isActive = !this.minimized;
</handler>
</handlers>
</binding>
<binding id="chatbar">
<content>
<xul:hbox align="end" pack="end" anonid="innerbox" class="chatbar-innerbox" mousethrough="always" flex="1">
<xul:spacer flex="1" anonid="spacer" class="chatbar-overflow-spacer"/>
<xul:toolbarbutton anonid="nub" class="chatbar-button" type="menu" collapsed="true" mousethrough="never">
<xul:menupopup anonid="nubMenu" oncommand="document.getBindingParent(this).showChat(event.target.chat)"/>
</xul:toolbarbutton>
<children/>
</xul:hbox>
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
// to avoid reflows we cache the width of the nub.
this.cachedWidthNub = 0;
this._selectedChat = null;
</constructor>
<field name="innerbox" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "innerbox");
</field>
<field name="menupopup" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "nubMenu");
</field>
<field name="nub" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "nub");
</field>
<method name="focus">
<body><![CDATA[
if (!this.selectedChat)
return;
this.selectedChat.content.messageManager.sendAsyncMessage("Social:EnsureFocus");
]]></body>
</method>
<method name="_isChatFocused">
<parameter name="aChatbox"/>
<body><![CDATA[
// If there are no XBL bindings for the chat it can't be focused.
if (!aChatbox.content)
return false;
let fw = Services.focus.focusedWindow;
if (!fw)
return false;
// We want to see if the focused window is in the subtree below our browser...
let containingBrowser = fw.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
return containingBrowser == aChatbox.content;
]]></body>
</method>
<property name="selectedChat">
<getter><![CDATA[
return this._selectedChat;
]]></getter>
<setter><![CDATA[
// this is pretty horrible, but we:
// * want to avoid doing touching 'selected' attribute when the
// specified chat is already selected.
// * remove 'activity' attribute on newly selected tab *even if*
// newly selected is already selected.
// * need to handle either current or new being null.
if (this._selectedChat != val) {
if (this._selectedChat) {
this._selectedChat.removeAttribute("selected");
}
this._selectedChat = val;
if (val) {
this._selectedChat.setAttribute("selected", "true");
}
}
if (val) {
this._selectedChat.removeAttribute("activity");
}
]]></setter>
</property>
<field name="menuitemMap">new WeakMap()</field>
<field name="chatboxForURL">new Map();</field>
<property name="hasCollapsedChildren">
<getter><![CDATA[
return !!this.querySelector("[collapsed]");
]]></getter>
</property>
<property name="collapsedChildren">
<getter><![CDATA[
// A generator yielding all collapsed chatboxes, in the order in
// which they should be restored.
return function*() {
let child = this.lastElementChild;
while (child) {
if (child.collapsed)
yield child;
child = child.previousElementSibling;
}
}
]]></getter>
</property>
<property name="visibleChildren">
<getter><![CDATA[
// A generator yielding all non-collapsed chatboxes.
return function*() {
let child = this.firstElementChild;
while (child) {
if (!child.collapsed)
yield child;
child = child.nextElementSibling;
}
}
]]></getter>
</property>
<property name="collapsibleChildren">
<getter><![CDATA[
// A generator yielding all children which are able to be collapsed
// in the order in which they should be collapsed.
// (currently this is all visible ones other than the selected one.)
return function*() {
for (let child of this.visibleChildren())
if (child != this.selectedChat)
yield child;
}
]]></getter>
</property>
<method name="_selectAnotherChat">
<body><![CDATA[
// Select a different chat (as the currently selected one is no
// longer suitable as the selection - maybe it is being minimized or
// closed.) We only select non-minimized and non-collapsed chats,
// and if none are found, set the selectedChat to null.
// It's possible in the future we will track most-recently-selected
// chats or similar to find the "best" candidate - for now though
// the choice is somewhat arbitrary.
let moveFocus = this.selectedChat && this._isChatFocused(this.selectedChat);
for (let other of this.children) {
if (other != this.selectedChat && !other.minimized && !other.collapsed) {
this.selectedChat = other;
if (moveFocus)
this.focus();
return;
}
}
// can't find another - so set no chat as selected.
this.selectedChat = null;
]]></body>
</method>
<method name="updateTitlebar">
<parameter name="aChatbox"/>
<body><![CDATA[
if (aChatbox.collapsed) {
let menuitem = this.menuitemMap.get(aChatbox);
if (aChatbox.getAttribute("activity")) {
menuitem.setAttribute("activity", true);
this.nub.setAttribute("activity", true);
}
menuitem.setAttribute("label", aChatbox.getAttribute("label"));
menuitem.setAttribute("image", aChatbox.getAttribute("image"));
}
]]></body>
</method>
<method name="calcTotalWidthOf">
<parameter name="aElement"/>
<body><![CDATA[
let cs = document.defaultView.getComputedStyle(aElement);
let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
return aElement.getBoundingClientRect().width + margins;
]]></body>
</method>
<method name="getTotalChildWidth">
<parameter name="aChatbox"/>
<body><![CDATA[
// These are from the CSS for the chatbox and must be kept in sync.
// We can't use calcTotalWidthOf due to the transitions...
const CHAT_WIDTH_OPEN = 300;
const CHAT_WIDTH_OPEN_ALT = 350;
const CHAT_WIDTH_MINIMIZED = 160;
let openWidth = aChatbox.hasAttribute("customSize") ?
CHAT_WIDTH_OPEN_ALT : CHAT_WIDTH_OPEN;
return aChatbox.minimized ? CHAT_WIDTH_MINIMIZED : openWidth;
]]></body>
</method>
<method name="collapseChat">
<parameter name="aChatbox"/>
<body><![CDATA[
// we ensure that the cached width for a child of this type is
// up-to-date so we can use it when resizing.
this.getTotalChildWidth(aChatbox);
aChatbox.collapsed = true;
aChatbox.isActive = false;
let menu = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
menu.setAttribute("class", "menuitem-iconic");
menu.setAttribute("label", aChatbox.content.contentTitle);
menu.setAttribute("image", aChatbox.getAttribute("image"));
menu.chat = aChatbox;
this.menuitemMap.set(aChatbox, menu);
this.menupopup.appendChild(menu);
this.nub.collapsed = false;
]]></body>
</method>
<method name="showChat">
<parameter name="aChatbox"/>
<parameter name="aMode"/>
<body><![CDATA[
if ((aMode != "minimized") && aChatbox.minimized)
aChatbox.minimized = false;
if (this.selectedChat != aChatbox)
this.selectedChat = aChatbox;
if (!aChatbox.collapsed)
return; // already showing - no more to do.
this._showChat(aChatbox);
// showing a collapsed chat might mean another needs to be collapsed
// to make room...
this.resize();
]]></body>
</method>
<method name="_showChat">
<parameter name="aChatbox"/>
<body><![CDATA[
// the actual implementation - doesn't check for overflow, assumes
// collapsed, etc.
let menuitem = this.menuitemMap.get(aChatbox);
this.menuitemMap.delete(aChatbox);
this.menupopup.removeChild(menuitem);
aChatbox.collapsed = false;
aChatbox.isActive = !aChatbox.minimized;
]]></body>
</method>
<method name="remove">
<parameter name="aChatbox"/>
<body><![CDATA[
this._remove(aChatbox);
// The removal of a chat may mean a collapsed one can spring up,
// or that the popup should be hidden. We also defer the selection
// of another chat until after a resize, as a new candidate may
// become uncollapsed after the resize.
this.resize();
if (this.selectedChat == aChatbox) {
this._selectAnotherChat();
}
]]></body>
</method>
<method name="_remove">
<parameter name="aChatbox"/>
<body><![CDATA[
this.removeChild(aChatbox);
// child might have been collapsed.
let menuitem = this.menuitemMap.get(aChatbox);
if (menuitem) {
this.menuitemMap.delete(aChatbox);
this.menupopup.removeChild(menuitem);
}
this.chatboxForURL.delete(aChatbox.src);
]]></body>
</method>
<method name="openChat">
<parameter name="aOptions"/>
<parameter name="aCallback"/>
<body><![CDATA[
let {origin, title, url, mode} = aOptions;
let cb = this.chatboxForURL.get(url);
if (cb && (cb = cb.get())) {
// A chatbox is still alive to us when it's parented and still has
// content.
if (cb.parentNode) {
this.showChat(cb, mode);
if (aCallback) {
if (cb._callbacks == null) {
// Chatbox has already been created, so callback now.
aCallback(cb);
} else {
// Chatbox is yet to have bindings created...
cb._callbacks.push(aCallback);
}
}
return cb;
}
this.chatboxForURL.delete(url);
}
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
cb._callbacks = [];
if (aCallback) {
// _callbacks is a javascript property instead of a <field> as it
// must exist before the (possibly delayed) bindings are created.
cb._callbacks.push(aCallback);
}
cb.remote = !!aOptions.remote;
// src also a javascript property; the src attribute is set in the ctor.
cb.src = url;
if (mode == "minimized")
cb.setAttribute("minimized", "true");
cb.setAttribute("origin", origin);
cb.setAttribute("label", title);
this.insertBefore(cb, this.firstChild);
this.selectedChat = cb;
this.chatboxForURL.set(url, Cu.getWeakReference(cb));
this.resize();
return cb;
]]></body>
</method>
<method name="resize">
<body><![CDATA[
// Checks the current size against the collapsed state of children
// and collapses or expands as necessary such that as many as possible
// are shown.
// So 2 basic strategies:
// * Collapse/Expand one at a time until we can't collapse/expand any
// more - but this is one reflow per change.
// * Calculate the dimensions ourself and choose how many to collapse
// or expand based on this, then do them all in one go. This is one
// reflow regardless of how many we change.
// So we go the more complicated but more efficient second option...
let availWidth = this.getBoundingClientRect().width;
let currentWidth = 0;
if (!this.nub.collapsed) { // the nub is visible.
if (!this.cachedWidthNub)
this.cachedWidthNub = this.calcTotalWidthOf(this.nub);
currentWidth += this.cachedWidthNub;
}
for (let child of this.visibleChildren()) {
currentWidth += this.getTotalChildWidth(child);
}
if (currentWidth > availWidth) {
// we need to collapse some.
let toCollapse = [];
for (let child of this.collapsibleChildren()) {
if (currentWidth <= availWidth)
break;
toCollapse.push(child);
currentWidth -= this.getTotalChildWidth(child);
}
if (toCollapse.length) {
for (let child of toCollapse)
this.collapseChat(child);
}
} else if (currentWidth < availWidth) {
// we *might* be able to expand some - see how many.
// XXX - if this was clever, it could know when removing the nub
// leaves enough space to show all collapsed
let toShow = [];
for (let child of this.collapsedChildren()) {
currentWidth += this.getTotalChildWidth(child);
if (currentWidth > availWidth)
break;
toShow.push(child);
}
for (let child of toShow)
this._showChat(child);
// If none remain collapsed remove the nub.
if (!this.hasCollapsedChildren) {
this.nub.collapsed = true;
}
}
// else: achievement unlocked - we are pixel-perfect!
]]></body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body><![CDATA[
if (aEvent.type == "resize") {
this.resize();
}
]]></body>
</method>
<method name="_getDragTarget">
<parameter name="event"/>
<body><![CDATA[
return event.target.localName == "chatbox" ? event.target : null;
]]></body>
</method>
<!-- Moves a chatbox to a new window. Returns a promise that is resolved
once the move to the other window is complete.
-->
<method name="detachChatbox">
<parameter name="aChatbox"/>
<parameter name="aOptions"/>
<body><![CDATA[
let deferred = Promise.defer();
let chatbar = this;
let options = "";
for (let name in aOptions)
options += "," + name + "=" + aOptions[name];
let otherWin = window.openDialog("chrome://browser/content/chatWindow.xul",
"_blank", "chrome,all,dialog=no" + options);
otherWin.addEventListener("load", function _chatLoad(event) {
if (event.target != otherWin.document)
return;
if (aChatbox.hasAttribute("customSize")) {
otherWin.document.getElementById("chat-window").
setAttribute("customSize", aChatbox.getAttribute("customSize"));
}
otherWin.removeEventListener("load", _chatLoad, true);
let otherChatbox = otherWin.document.getElementById("chatter");
aChatbox.setDecorationAttributes(otherChatbox);
aChatbox.swapDocShells(otherChatbox);
aChatbox.swappingWindows = true;
aChatbox.close();
let url = aChatbox.src;
chatbar.chatboxForURL.set(url, Cu.getWeakReference(otherChatbox));
// All processing is done, now we can fire the event.
otherChatbox.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
name: "socialFrameDetached"
});
Services.obs.addObserver(function onDOMWindowClosed(subject) {
if (subject !== otherWin)
return;
Services.obs.removeObserver(onDOMWindowClosed, "domwindowclosed");
chatbar.chatboxForURL.delete(url);
if (!otherChatbox.swappingWindows)
otherChatbox.dispatchEvent(new CustomEvent("ChatboxClosed"));
}, "domwindowclosed", false);
deferred.resolve(otherChatbox);
}, true);
return deferred.promise;
]]></body>
</method>
</implementation>
<handlers>
<handler event="popupshown"><![CDATA[
this.nub.removeAttribute("activity");
]]></handler>
<handler event="load"><![CDATA[
window.addEventListener("resize", this, true);
]]></handler>
<handler event="unload"><![CDATA[
window.removeEventListener("resize", this, true);
]]></handler>
<handler event="dragstart"><![CDATA[
// chat window dragging is essentially duplicated from tabbrowser.xml
// to acheive the same visual experience
let chatbox = this._getDragTarget(event);
if (!chatbox) {
return;
}
let dt = event.dataTransfer;
// we do not set a url in the drag data to prevent moving to tabbrowser
// or otherwise having unexpected drop handlers do something with our
// chatbox
dt.mozSetDataAt("application/x-moz-chatbox", chatbox, 0);
// Set the cursor to an arrow during tab drags.
dt.mozCursor = "default";
// Create a canvas to which we capture the current tab.
// Until canvas is HiDPI-aware (bug 780362), we need to scale the desired
// canvas size (in CSS pixels) to the window's backing resolution in order
// to get a full-resolution drag image for use on HiDPI displays.
let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.mozOpaque = true;
canvas.width = 160 * scale;
canvas.height = 90 * scale;
PageThumbs.captureToCanvas(chatbox, canvas);
dt.setDragImage(canvas, -16 * scale, -16 * scale);
event.stopPropagation();
]]></handler>
<handler event="dragend"><![CDATA[
let dt = event.dataTransfer;
let draggedChat = dt.mozGetDataAt("application/x-moz-chatbox", 0);
if (dt.mozUserCancelled || dt.dropEffect != "none") {
return;
}
let eX = event.screenX;
let eY = event.screenY;
// screen.availLeft et. al. only check the screen that this window is on,
// but we want to look at the screen the tab is being dropped onto.
let sX = {}, sY = {}, sWidth = {}, sHeight = {};
Cc["@mozilla.org/gfx/screenmanager;1"]
.getService(Ci.nsIScreenManager)
.screenForRect(eX, eY, 1, 1)
.GetAvailRect(sX, sY, sWidth, sHeight);
// default size for the chat window as used in chatWindow.xul, use them
// here to attempt to keep the window fully within the screen when
// opening at the drop point. If the user has resized the window to
// something larger (which gets persisted), at least a good portion of
// the window should still be within the screen.
let winWidth = 400;
let winHeight = 420;
// ensure new window entirely within screen
let left = Math.min(Math.max(eX, sX.value),
sX.value + sWidth.value - winWidth);
let top = Math.min(Math.max(eY, sY.value),
sY.value + sHeight.value - winHeight);
this.detachChatbox(draggedChat, { screenX: left, screenY: top });
event.stopPropagation();
]]></handler>
</handlers>
</binding>
</bindings>

View File

@ -14,13 +14,7 @@ support-files =
social_activate.html
social_activate_basic.html
social_activate_iframe.html
social_chat.html
social_crash_content_helper.js
social_flyout.html
social_panel.html
social_postActivation.html
social_sidebar.html
social_sidebar_empty.html
!/browser/base/content/test/plugins/blockNoPlugins.xml
[browser_aboutHome_activation.js]
@ -28,13 +22,3 @@ support-files =
[browser_blocklist.js]
[browser_share.js]
[browser_social_activation.js]
[browser_social_chatwindow.js]
[browser_social_chatwindow_resize.js]
[browser_social_chatwindowfocus.js]
skip-if = (os == 'linux' && e10s) # Bug 1072669 context menu relies on target element
[browser_social_errorPage.js]
[browser_social_flyout.js]
[browser_social_isVisible.js]
[browser_social_multiprovider.js]
[browser_social_sidebar.js]
[browser_social_window.js]

View File

@ -17,7 +17,7 @@ var snippet =
' "iconURL": "chrome://branding/content/icon16.png",' +
' "icon32URL": "chrome://branding/content/icon32.png",' +
' "icon64URL": "chrome://branding/content/icon64.png",' +
' "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
' "shareURL": "https://example.com/browser/browser/base/content/test/social/social_share.html",' +
' "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
' };' +
' function activateProvider(node) {' +
@ -39,7 +39,7 @@ var snippet2 =
' "iconURL": "chrome://branding/content/icon16.png",' +
' "icon32URL": "chrome://branding/content/icon32.png",' +
' "icon64URL": "chrome://branding/content/icon64.png",' +
' "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
' "shareURL": "https://example.com/browser/browser/base/content/test/social/social_share.html",' +
' "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
' "oneclick": true' +
' };' +
@ -101,10 +101,8 @@ function test()
yield new Promise(resolve => {
activateProvider(tab, test.panel).then(() => {
ok(SocialSidebar.provider, "provider activated");
checkSocialUI();
is(gBrowser.currentURI.spec, SocialSidebar.provider.manifest.postActivationURL, "postActivationURL was loaded");
SocialService.uninstallProvider(SocialSidebar.provider.origin, function () {
SocialService.uninstallProvider("https://example.com", function () {
info("provider uninstalled");
resolve();
});
@ -210,14 +208,12 @@ function sendActivationEvent(tab) {
function activateProvider(tab, expectPanel, aCallback) {
return new Promise(resolve => {
if (expectPanel) {
ensureEventFired(PopupNotifications.panel, "popupshown").then(() => {
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
let panel = document.getElementById("servicesInstall-notification");
panel.button.click();
});
}
waitForProviderLoad().then(() => {
ok(SocialSidebar.provider, "new provider is active");
ok(SocialSidebar.opened, "sidebar is open");
checkSocialUI();
resolve();
});
@ -229,6 +225,5 @@ function waitForProviderLoad(cb) {
return Promise.all([
promiseObserverNotified("social:provider-enabled"),
ensureFrameLoaded(gBrowser, "https://example.com/browser/browser/base/content/test/social/social_postActivation.html"),
ensureFrameLoaded(SocialSidebar.browser)
]);
}

View File

@ -4,20 +4,20 @@ var SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).So
var manifest = {
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
shareURL: "https://example.com/browser/browser/base/content/test/social/social_share.html",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
var manifest2 = { // used for testing install
name: "provider 2",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
shareURL: "https://test1.example.com/browser/browser/base/content/test/social/social_share.html",
iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
version: "1.0"
};
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",
shareURL: "https://test2.example.com/browser/browser/base/content/test/social/social_share.html",
iconURL: "https://test2.example.com/browser/browser/base/content/test/general/moz.png",
version: "1.0"
};

View File

@ -12,13 +12,13 @@ var blocklistURL = "http://example.com/browser/browser/base/content/test/social/
var manifest = { // normal provider
name: "provider ok",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
shareURL: "https://example.com/browser/browser/base/content/test/social/social_share.html",
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",
shareURL: "https://test1.example.com/browser/browser/base/content/test/social/social_share.html",
iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png"
};

View File

@ -73,7 +73,6 @@ function activateIFrameProvider(domain, callback) {
function waitForProviderLoad(origin) {
return Promise.all([
ensureFrameLoaded(gBrowser, origin + "/browser/browser/base/content/test/social/social_postActivation.html"),
ensureFrameLoaded(SocialSidebar.browser)
]);
}
@ -113,6 +112,7 @@ function clickAddonRemoveButton(tab, aCallback) {
}
function activateOneProvider(manifest, finishActivation, aCallback) {
info("activating provider "+manifest.name);
let panel = document.getElementById("servicesInstall-notification");
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
ok(!panel.hidden, "servicesInstall-notification panel opened");
@ -128,8 +128,6 @@ function activateOneProvider(manifest, finishActivation, aCallback) {
executeSoon(aCallback);
} else {
waitForProviderLoad(manifest.origin).then(() => {
is(SocialSidebar.provider.origin, manifest.origin, "new provider is active");
ok(SocialSidebar.opened, "sidebar is open");
checkSocialUI();
executeSoon(aCallback);
});
@ -147,19 +145,19 @@ var gProviders = [
{
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html?provider1",
shareURL: "https://example.com/browser/browser/base/content/test/social/social_share.html?provider1",
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_empty.html?provider2",
shareURL: "https://test1.example.com/browser/browser/base/content/test/social/social_share.html?provider2",
iconURL: "chrome://branding/content/icon64.png"
},
{
name: "provider 3",
origin: "https://test2.example.com",
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar_empty.html?provider2",
shareURL: "https://test2.example.com/browser/browser/base/content/test/social/social_share.html?provider2",
iconURL: "chrome://branding/content/about-logo.png"
}
];
@ -191,7 +189,6 @@ var tests = {
testIFrameActivation: function(next) {
activateIFrameProvider(gTestDomains[0], function() {
is(SocialUI.enabled, false, "SocialUI is not enabled");
ok(!SocialSidebar.provider, "provider is not installed");
let panel = document.getElementById("servicesInstall-notification");
ok(panel.hidden, "activation panel still hidden");
checkSocialUI();
@ -203,7 +200,6 @@ var tests = {
// first up we add a manifest entry for a single provider.
activateOneProvider(gProviders[0], false, function() {
// we deactivated leaving no providers left, so Social is disabled.
ok(!SocialSidebar.provider, "should be no provider left after disabling");
checkSocialUI();
next();
});
@ -221,7 +217,6 @@ var tests = {
// activate the last provider.
activateOneProvider(gProviders[2], false, function() {
// we deactivated - the first provider should be enabled.
is(SocialSidebar.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
checkSocialUI();
next();
});

View File

@ -1,141 +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 = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
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",
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",
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",
iconURL: "chrome://branding/content/icon48.png"
}
];
var chatId = 0;
function openChat(provider) {
return new Promise(resolve => {
SocialSidebar.provider = provider;
let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let url = chatUrl + "?id=" + (chatId++);
makeChat("normal", "chat " + chatId, (cb) => { resolve(cb); });
});
}
function windowHasChats(win) {
return !!getChatBar().firstElementChild;
}
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();
}, true);
}.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) {
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");
SocialSidebar.show();
runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight);
mm.removeDelayedFrameScript(frameScript);
finishcb();
});
});
}
var tests = {
testOpenCloseChat: function(next) {
openChat(SocialSidebar.provider).then((cb) => {
BrowserTestUtils.waitForCondition(() => { return cb.minimized; },
"chatbox is minimized").then(() => {
ok(cb.minimized, "chat is minimized after toggle");
BrowserTestUtils.waitForCondition(() => { return !cb.minimized; },
"chatbox is not minimized").then(() => {
ok(!cb.minimized, "chat is not minimized after toggle");
promiseNodeRemoved(cb).then(next);
let mm = cb.content.messageManager;
mm.sendAsyncMessage("socialTest-CloseSelf", {});
info("close chat window requested");
});
cb.toggle();
});
ok(!cb.minimized, "chat is not minimized on open");
// toggle to minimize chat
cb.toggle();
});
},
// Check what happens when you close the only visible chat.
testCloseOnlyVisible: function(next) {
let chatbar = getChatBar();
let chatWidth = undefined;
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
makeChat("normal", "first chat", function() {
// got the first one.
checkPopup();
ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
// we kinda cheat here and get the width of the first chat, assuming
// that all future chats will have the same width when open.
chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
let desired = chatWidth * 1.5;
resizeWindowToChatAreaWidth(desired, function(sizedOk) {
ok(sizedOk, "can't do any tests without this width");
checkPopup();
makeChat("normal", "second chat", function() {
is(chatbar.childNodes.length, 2, "now have 2 chats");
let first = chatbar.childNodes[0];
let second = chatbar.childNodes[1];
is(chatbar.selectedChat, first, "first chat is selected");
ok(second.collapsed, "second chat is currently collapsed");
// closing the first chat will leave enough room for the second
// chat to appear, and thus become selected.
chatbar.selectedChat.close();
is(chatbar.selectedChat, second, "second chat is selected");
Task.spawn(closeAllChats).then(next);
});
});
});
},
testShowWhenCollapsed: function(next) {
get3ChatsForCollapsing("normal", function(first, second, third) {
let chatbar = getChatBar();
chatbar.showChat(first);
ok(!first.collapsed, "first should no longer be collapsed");
is(second.collapsed || third.collapsed, true, "one of the others should be collapsed");
Task.spawn(closeAllChats).then(next);
});
}
}

View File

@ -1,82 +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() {
requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish();
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
// added for test purposes
chatURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html"
};
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) {
let chats = document.getElementById("pinnedchats");
ok(chats.children.length == 0, "no chatty children left behind");
cb();
};
runSocialTestWithProvider(manifest, function (finishcb) {
let sbrowser = document.getElementById("social-sidebar-browser");
ensureFrameLoaded(sbrowser).then(() => {
let provider = SocialSidebar.provider;
provider.chatURL = manifest.chatURL;
ok(provider, "provider is set");
ok(provider.chatURL, "provider has chatURL");
// executeSoon to let the browser UI observers run first
runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight);
finishcb();
});
});
SocialSidebar.show();
});
}
var tests = {
// resize and collapse testing.
testBrowserResize: function(next, mode) {
let chats = document.getElementById("pinnedchats");
get3ChatsForCollapsing(mode || "normal", function(first, second, third) {
let chatWidth = chats.getTotalChildWidth(first);
ok(chatWidth, "have a chatwidth");
let popupWidth = getPopupWidth();
ok(popupWidth, "have a popupwidth");
info("starting resize tests - each chat's width is " + chatWidth +
" and the popup width is " + popupWidth);
// Note that due to a difference between "device", "app" and "css" pixels
// we allow use 2 pixels as the minimum size difference.
resizeAndCheckWidths(first, second, third, [
[chatWidth-2, 1, "to < 1 chat width - only last should be visible."],
[chatWidth+2, 1, "2 pixels more then one fully exposed (not counting popup) - still only 1."],
[chatWidth+popupWidth+2, 1, "2 pixels more than one fully exposed (including popup) - still only 1."],
[chatWidth*2-2, 1, "second not showing by 2 pixels (not counting popup) - only 1 exposed."],
[chatWidth*2+popupWidth-2, 1, "second not showing by 2 pixelx (including popup) - only 1 exposed."],
[chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"],
[chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"],
[chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"],
[chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
[chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"],
[chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"],
[chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"],
[chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"],
[chatWidth*1.5, 1, "and a large jump back down to 1 visible (ie, affects 2)"],
], function() {
Task.spawn(closeAllChats).then(next);
});
});
},
testBrowserResizeMinimized: function(next) {
this.testBrowserResize(next);
}
}

View File

@ -1,66 +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 isChatFocused(chat) {
return getChatBar()._isChatFocused(chat);
}
var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
function test() {
waitForExplicitFinish();
// Note that (probably) due to bug 604289, if a tab is focused but the
// focused element is null, our chat windows can "steal" focus. This is
// avoided if we explicitly focus an element in the tab.
// So we load a page with an <input> field and focus that before testing.
let url = "data:text/html;charset=utf-8," + encodeURI('<input id="theinput">');
let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
let browser = tab.linkedBrowser;
browser.addEventListener("load", function tabLoad(event) {
browser.removeEventListener("load", tabLoad, true);
// before every test we focus the input field.
let preSubTest = function(cb) {
ContentTask.spawn(browser, null, function* () {
content.focus();
content.document.getElementById("theinput").focus();
yield ContentTaskUtils.waitForCondition(
() => Services.focus.focusedWindow == content, "tab should have focus");
}).then(cb);
}
let postSubTest = function(cb) {
Task.spawn(closeAllChats).then(cb);
}
// and run the tests.
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
runSocialTests(tests, preSubTest, postSubTest, function () {
BrowserTestUtils.removeTab(tab).then(finishcb);
});
});
}, true);
}
var tests = {
// 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) {
ensureFrameLoaded(document.getElementById("social-sidebar-browser")).then(() => {
let chatbar = getChatBar();
openChatViaUser();
ok(chatbar.firstElementChild, "chat opened");
BrowserTestUtils.waitForCondition(() => isChatFocused(chatbar.selectedChat),
"chat should be focused").then(() => {
is(chatbar.selectedChat, chatbar.firstElementChild, "chat is selected");
next();
});
});
},
};

View File

@ -1,187 +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 gc() {
Cu.forceGC();
let wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
wu.garbageCollect();
}
var openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
function openPanel(url, panelCallback, loadCallback) {
// open a flyout
SocialFlyout.open(url, 0, panelCallback);
// wait for both open and loaded before callback. Since the test doesn't close
// the panel between opens, we cannot rely on events here. We need to ensure
// popupshown happens before we finish out the tests.
BrowserTestUtils.waitForCondition(function() {
return SocialFlyout.panel.state == "open" &&
SocialFlyout.iframe.contentDocument.readyState == "complete";
},"flyout is open and loaded").then(() => { executeSoon(loadCallback) });
}
function openChat(url, panelCallback, loadCallback) {
// open a chat window
let chatbar = getChatBar();
openChatWindow(null, SocialSidebar.provider, url, panelCallback);
chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
executeSoon(loadCallback);
}, true);
}
function onSidebarLoad(callback) {
let sbrowser = document.getElementById("social-sidebar-browser");
sbrowser.addEventListener("load", function load() {
sbrowser.removeEventListener("load", load, true);
executeSoon(callback);
}, true);
}
var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
function test() {
waitForExplicitFinish();
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTests(tests, undefined, function(next) { goOnline().then(next) }, finishcb);
});
}
var tests = {
testSidebar: function(next) {
let sbrowser = document.getElementById("social-sidebar-browser");
onSidebarLoad(function() {
ok(sbrowser.contentDocument.documentURI.indexOf("about:socialerror?mode=tryAgainOnly")==0, "sidebar is on social error page");
gc();
// Add a new load listener, then find and click the "try again" button.
onSidebarLoad(function() {
// should still be on the error page.
ok(sbrowser.contentDocument.documentURI.indexOf("about:socialerror?mode=tryAgainOnly")==0, "sidebar is still on social error page");
// go online and try again - this should work.
goOnline().then(function () {
onSidebarLoad(function() {
// should now be on the correct page.
is(sbrowser.contentDocument.documentURI, manifest.sidebarURL, "sidebar is now on social sidebar page");
next();
});
sbrowser.contentDocument.getElementById("btnTryAgain").click();
});
});
sbrowser.contentDocument.getElementById("btnTryAgain").click();
});
// go offline then attempt to load the sidebar - it should fail.
goOffline().then(function() {
SocialSidebar.show();
});
},
testFlyout: function(next) {
let panelCallbackCount = 0;
let panel = document.getElementById("social-flyout-panel");
goOffline().then(function() {
openPanel(
manifest.sidebarURL, /* empty html page */
function() { // the panel api callback
panelCallbackCount++;
},
function() { // the "load" callback.
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
let href = panel.firstChild.contentDocument.documentURI;
ok(href.indexOf("about:socialerror?mode=compactInfo")==0, "flyout is on social error page");
// Bug 832943 - the listeners previously stopped working after a GC, so
// force a GC now and try again.
gc();
openPanel(
manifest.sidebarURL, /* empty html page */
function() { // the panel api callback
panelCallbackCount++;
},
function() { // the "load" callback.
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
let href = panel.firstChild.contentDocument.documentURI;
ok(href.indexOf("about:socialerror?mode=compactInfo")==0, "flyout is on social error page");
gc();
SocialFlyout.unload();
next();
}
);
}
);
});
},
testChatWindow: function(next) {
todo(false, "Bug 1245799 is needed to make error pages work again for chat windows.");
next();
return;
let panelCallbackCount = 0;
// chatwindow tests throw errors, which muddy test output, if the worker
// doesn't get test-init
goOffline().then(function() {
openChat(
manifest.sidebarURL, /* empty html page */
function() { // the panel api callback
panelCallbackCount++;
},
function() { // the "load" callback.
todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
let chat = getChatBar().selectedChat;
BrowserTestUtils.waitForCondition(() => chat.content != null && chat.contentDocument.documentURI.indexOf("about:socialerror?mode=tryAgainOnly")==0,
"error page didn't appear").then(() => {
chat.close();
next();
});
}
);
});
},
testChatWindowAfterTearOff: function(next) {
todo(false, "Bug 1245799 is needed to make error pages work again for chat windows.");
next();
return;
// Ensure that the error listener survives the chat window being detached.
let url = manifest.sidebarURL; /* empty html page */
let panelCallbackCount = 0;
// chatwindow tests throw errors, which muddy test output, if the worker
// doesn't get test-init
// open a chat while we are still online.
openChat(
url,
null,
function() { // the "load" callback.
let chat = getChatBar().selectedChat;
is(chat.contentDocument.documentURI, url, "correct url loaded");
// toggle to a detached window.
chat.swapWindows().then(chat => {
ok(!!chat.content, "we have chat content 1");
BrowserTestUtils.waitForCondition(() => chat.content != null && chat.contentDocument.readyState == "complete",
"swapped window loaded").then(() => {
// now go offline and reload the chat - about:socialerror should be loaded.
goOffline().then(() => {
ok(!!chat.content, "we have chat content 2");
chat.contentDocument.location.reload();
info("chat reload called");
BrowserTestUtils.waitForCondition(() => chat.contentDocument.documentURI.indexOf("about:socialerror?mode=tryAgainOnly")==0,
"error page didn't appear").then(() => {
chat.close();
next();
});
});
});
});
}
);
}
}

View File

@ -1,102 +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();
let frameScript = "data:,(" + function frame_script() {
addMessageListener("socialTest-CloseSelf", function(e) {
content.close();
});
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",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
runSocialTestWithProvider(manifest, function (finishcb) {
SocialSidebar.show();
ensureFrameLoaded(SocialSidebar.browser, manifest.sidebarURL).then(() => {
// disable transitions for the test
registerCleanupFunction(function () {
SocialFlyout.panel.removeAttribute("animate");
});
SocialFlyout.panel.setAttribute("animate", "false");
runSocialTests(tests, undefined, undefined, finishcb);
});
});
}
var tests = {
testResizeFlyout: function(next) {
let panel = document.getElementById("social-flyout-panel");
BrowserTestUtils.waitForEvent(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);
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");
BrowserTestUtils.waitForEvent(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");
BrowserTestUtils.waitForEvent(panel, "popuphidden").then(next);
panel.hidePopup();
});
SocialFlyout.dispatchPanelEvent("socialTest-MakeWider");
});
SocialSidebar.browser.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
},
testCloseSelf: function(next) {
let panel = document.getElementById("social-flyout-panel");
BrowserTestUtils.waitForEvent(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
BrowserTestUtils.waitForEvent(panel, "popuphidden").then(next);
let mm = panel.firstChild.messageManager;
mm.sendAsyncMessage("socialTest-CloseSelf", {});
});
SocialSidebar.browser.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
},
testCloseOnLinkTraversal: function(next) {
BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen", true).then(event => {
BrowserTestUtils.waitForCondition(function() { return panel.state == "closed" },
"panel should close after tab open").then(() => {
BrowserTestUtils.removeTab(event.target).then(next);
});
});
let panel = document.getElementById("social-flyout-panel");
BrowserTestUtils.waitForEvent(panel, "popupshown").then(() => {
is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
is(panel.state, "open", "flyout should be open");
let iframe = panel.firstChild;
iframe.contentDocument.getElementById('traversal').click();
});
SocialSidebar.browser.messageManager.sendAsyncMessage("socialTest-sendEvent", { name: "test-flyout-open", data: {} });
}
}

View File

@ -1,51 +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();
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
let frameScript = "data:,(" + function frame_script() {
addEventListener("visibilitychange", function() {
sendAsyncMessage("visibility", content.document.hidden ? "hidden" : "shown");
});
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
registerCleanupFunction(function () {
mm.removeDelayedFrameScript(frameScript);
});
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTests(tests, undefined, undefined, finishcb);
});
}
var tests = {
testIsVisible: function(next) {
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 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

@ -1,78 +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) {
SocialSidebar.provider = Social.providers[0];
SocialSidebar.show();
is(Social.providers[0].origin, SocialSidebar.provider.origin, "selected provider in sidebar");
runSocialTests(tests, undefined, undefined, finishcb);
});
}
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",
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",
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) {
let menuProviders = menu.querySelectorAll(".social-provider-menuitem");
is(menuProviders.length, gProviders.length, "correct number of providers listed in the menu");
// Find the selectedProvider's menu item
let el = menu.getElementsByAttribute("origin", selectedProvider.origin);
is(el.length, 1, "selected provider menu item exists");
is(el[0].getAttribute("checked"), "true", "selected provider menu item is checked");
}
// the menu is not populated until onpopupshowing, so wait for popupshown
BrowserTestUtils.waitForEvent(menu, "popupshown", true).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"
BrowserTestUtils.waitForEvent(sbrowser, "load", true).then(()=>{
checkUIStateMatchesProvider(Social.providers[1]);
BrowserTestUtils.waitForEvent(sbrowser, "load", true).then(()=>{
checkUIStateMatchesProvider(Social.providers[0]);
next();
});
// show the menu again so the menu is updated with the correct commands
BrowserTestUtils.waitForEvent(menu, "popupshown", true).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], {});
});
EventUtils.synthesizeMouseAtCenter(button, {});
});
SocialSidebar.provider = Social.providers[1];
});
EventUtils.synthesizeMouseAtCenter(button, {});
}
}
function checkUIStateMatchesProvider(provider) {
// Sidebar
is(document.getElementById("social-sidebar-browser").getAttribute("src"), provider.sidebarURL, "side bar URL is set");
}

View File

@ -1,98 +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 = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
};
function test() {
waitForExplicitFinish();
let frameScript = "data:,(" + function frame_script() {
addEventListener("visibilitychange", function() {
sendAsyncMessage("visibility", content.document.hidden ? "hidden" : "shown");
});
}.toString() + ")();";
let mm = getGroupMessageManager("social");
mm.loadFrameScript(frameScript, true);
registerCleanupFunction(function () {
mm.removeDelayedFrameScript(frameScript);
});
SocialService.addProvider(manifest, function() {
// the test will remove the provider
doTest();
});
}
function doTest() {
ok(SocialSidebar.canShow, "social sidebar should be able to be shown");
ok(!SocialSidebar.opened, "social sidebar should not be open by default");
let command = document.getElementById("Social:ToggleSidebar");
let sidebar = document.getElementById("social-sidebar-box");
let browser = sidebar.lastChild;
ok(!browser.docShellIsActive, "sidebar is not active");
is(sidebar.hidden, true, "sidebar should be hidden");
is(command.getAttribute("checked"), "false", "toggle command should be unchecked");
function checkShown(shouldBeShown) {
is(command.getAttribute("checked"), shouldBeShown ? "true" : "false",
"toggle command should be " + (shouldBeShown ? "checked" : "unchecked"));
is(sidebar.hidden, !shouldBeShown,
"sidebar should be " + (shouldBeShown ? "visible" : "hidden"));
is(browser.docShellIsActive, shouldBeShown, "sidebar isActive in correct state");
if (shouldBeShown) {
is(browser.getAttribute('src'), SocialSidebar.provider.sidebarURL, "sidebar url should be set");
// We don't currently check docShellIsActive as this is only set
// after load event fires, and the tests below explicitly wait for this
// anyway.
}
else {
ok(!browser.docShellIsActive, "sidebar should have an inactive docshell");
// sidebar will only be immediately unloaded (and thus set to
// about:blank) when canShow is false.
if (SocialSidebar.canShow) {
// should not have unloaded so will still be the provider URL.
is(browser.getAttribute('src'), SocialSidebar.provider.sidebarURL, "sidebar url should be set");
} else {
// should have been an immediate unload.
is(browser.getAttribute('src'), "about:blank", "sidebar url should be blank");
}
}
}
ensureFrameLoaded(browser).then(() => {
// First check the the sidebar is initially visible, and loaded
ok(!command.hidden, "toggle command should be visible");
let mm = getGroupMessageManager("social");
mm.addMessageListener("visibility", function shown(msg) {
if (msg.data == "shown") {
mm.removeMessageListener("visibility", shown);
checkShown(true);
info("Toggling sidebar to closed");
SocialSidebar.toggleSidebar();
}
});
mm.addMessageListener("visibility", function handler(msg) {
if (msg.data == "hidden") {
mm.removeMessageListener("visibility", handler);
// disable social.
SocialService.disableProvider(SocialSidebar.provider.origin, function() {
checkShown(false);
is(Social.providers.length, 0, "no providers left");
defaultFinishChecks();
// Finish the test
executeSoon(finish);
});
}
});
});
SocialSidebar.show();
}

View File

@ -1,251 +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/.
// Test the top-level window UI for social.
var SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
// This function should "reset" Social such that the next time Social.init()
// is called (eg, when a new window is opened), it re-performs all
// initialization.
function resetSocial() {
Social.initialized = false;
Social.providers = [];
// *sob* - listeners keep getting added...
SocialService._providerListeners.clear();
}
var createdWindows = [];
function openWindowAndWaitForInit(parentWin, callback) {
// this notification tells us SocialUI.init() has been run...
let topic = "browser-delayed-startup-finished";
let w = parentWin.OpenBrowserWindow();
createdWindows.push(w);
Services.obs.addObserver(function providerSet(subject, topic, data) {
Services.obs.removeObserver(providerSet, topic);
info(topic + " observer was notified - continuing test");
executeSoon(() => callback(w));
}, topic, false);
}
function closeWindow(w, cb) {
waitForNotification("domwindowclosed", cb);
w.close();
}
function closeOneWindow(cb) {
let w = createdWindows.pop();
if (!w || w.closed) {
cb();
return;
}
closeWindow(w, function() {
closeOneWindow(cb);
});
w.close();
}
function postTestCleanup(cb) {
closeOneWindow(cb);
}
var manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
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",
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",
};
function test() {
waitForExplicitFinish();
requestLongerTimeout(2);
runSocialTests(tests, undefined, postTestCleanup);
}
var tests = {
// check when social is totally disabled at startup (ie, no providers enabled)
testInactiveStartup: function(cbnext) {
is(Social.providers.length, 0, "needs zero providers to start this test.");
ok(!SocialService.hasEnabledProviders, "no providers are enabled");
resetSocial();
openWindowAndWaitForInit(window, function(w1) {
checkSocialUI(w1);
// Now social is (re-)initialized, open a secondary window and check that.
openWindowAndWaitForInit(window, function(w2) {
checkSocialUI(w2);
checkSocialUI(w1);
cbnext();
});
});
},
// Check when providers are enabled and social is turned on at startup.
testEnabledStartup: function(cbnext) {
setManifestPref("social.manifest.test", manifest);
ok(!SocialSidebar.opened, "sidebar is closed initially");
SocialService.addProvider(manifest, function() {
SocialService.addProvider(manifest2, function (provider) {
SocialSidebar.show();
BrowserTestUtils.waitForCondition(
() => SocialSidebar.opened, "sidebar did not open").then(() => {
ok(SocialSidebar.opened, "first window sidebar is open");
openWindowAndWaitForInit(window, function(w1) {
ok(w1.SocialSidebar.opened, "new window sidebar is open");
ok(SocialService.hasEnabledProviders, "providers are enabled");
checkSocialUI(w1);
// now init is complete, open a second window
openWindowAndWaitForInit(window, function(w2) {
ok(w1.SocialSidebar.opened, "w1 sidebar is open");
ok(w2.SocialSidebar.opened, "w2 sidebar is open");
checkSocialUI(w2);
checkSocialUI(w1);
// disable social and re-check
SocialService.disableProvider(manifest.origin, function() {
SocialService.disableProvider(manifest2.origin, function() {
ok(!Social.enabled, "social is disabled");
is(Social.providers.length, 0, "no providers");
ok(!w1.SocialSidebar.opened, "w1 sidebar is closed");
ok(!w2.SocialSidebar.opened, "w2 sidebar is closed");
checkSocialUI(w2);
checkSocialUI(w1);
Services.prefs.clearUserPref("social.manifest.test");
cbnext();
});
});
});
});
});
}, cbnext);
}, cbnext);
},
testGlobalState: function(cbnext) {
setManifestPref("social.manifest.test", manifest);
ok(!SocialSidebar.opened, "sidebar is closed initially");
ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "global state unset");
// mimick no session state in opener so we exercise the global state via pref
SessionStore.deleteWindowValue(window, "socialSidebar");
ok(!SessionStore.getWindowValue(window, "socialSidebar"), "window state unset");
SocialService.addProvider(manifest, function() {
openWindowAndWaitForInit(window, function(w1) {
w1.SocialSidebar.show();
BrowserTestUtils.waitForCondition(() => w1.SocialSidebar.opened, "sidebar opened").then(() => {
ok(Services.prefs.prefHasUserValue("social.sidebar.provider"), "global state set");
ok(!SocialSidebar.opened, "1. main sidebar is still closed");
ok(w1.SocialSidebar.opened, "1. window sidebar is open");
closeWindow(w1, function() {
// this time, the global state should cause the sidebar to be opened
// in the new window
openWindowAndWaitForInit(window, function(w1) {
ok(!SocialSidebar.opened, "2. main sidebar is still closed");
ok(w1.SocialSidebar.opened, "2. window sidebar is open");
w1.SocialSidebar.hide();
ok(!w1.SocialSidebar.opened, "2. window sidebar is closed");
ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "2. global state unset");
// global state should now be no sidebar gets opened on new window
closeWindow(w1, function() {
ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "3. global state unset");
ok(!SocialSidebar.opened, "3. main sidebar is still closed");
openWindowAndWaitForInit(window, function(w1) {
ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "4. global state unset");
ok(!SocialSidebar.opened, "4. main sidebar is still closed");
ok(!w1.SocialSidebar.opened, "4. window sidebar is closed");
SocialService.disableProvider(manifest.origin, function() {
Services.prefs.clearUserPref("social.manifest.test");
cbnext();
});
});
});
});
});
});
});
});
},
// Check per window sidebar functionality, including migration from using
// prefs to using session state, and state inheritance of windows (new windows
// inherit state from the opener).
testPerWindowSidebar: function(cbnext) {
function finishCheck() {
// disable social and re-check
SocialService.disableProvider(manifest.origin, function() {
SocialService.disableProvider(manifest2.origin, function() {
ok(!Social.enabled, "social is disabled");
is(Social.providers.length, 0, "no providers");
Services.prefs.clearUserPref("social.manifest.test");
cbnext();
});
});
}
setManifestPref("social.manifest.test", manifest);
ok(!SocialSidebar.opened, "sidebar is closed initially");
SocialService.addProvider(manifest, function() {
SocialService.addProvider(manifest2, function (provider) {
// test the migration of the social.sidebar.open pref. We'll set a user
// level pref to indicate it was open (along with the old
// social.provider.current pref), then we'll open a window. During the
// restoreState of the window, those prefs should be migrated, and the
// sidebar should be opened. Both prefs are then removed.
Services.prefs.setCharPref("social.provider.current", "https://example.com");
Services.prefs.setBoolPref("social.sidebar.open", true);
openWindowAndWaitForInit(window, function(w1) {
ok(w1.SocialSidebar.opened, "new window sidebar is open");
ok(SocialService.hasEnabledProviders, "providers are enabled");
ok(!Services.prefs.prefHasUserValue("social.provider.current"), "social.provider.current pref removed");
ok(!Services.prefs.prefHasUserValue("social.sidebar.open"), "social.sidebar.open pref removed");
checkSocialUI(w1);
// now init is complete, open a second window, it's state should be the same as the opener
openWindowAndWaitForInit(w1, function(w2) {
ok(w1.SocialSidebar.opened, "w1 sidebar is open");
ok(w2.SocialSidebar.opened, "w2 sidebar is open");
checkSocialUI(w2);
checkSocialUI(w1);
// change the sidebar in w2
w2.SocialSidebar.show(manifest2.origin);
let sbrowser1 = w1.document.getElementById("social-sidebar-browser");
is(manifest.origin, sbrowser1.getAttribute("origin"), "w1 sidebar origin matches");
let sbrowser2 = w2.document.getElementById("social-sidebar-browser");
is(manifest2.origin, sbrowser2.getAttribute("origin"), "w2 sidebar origin matches");
// hide sidebar, w1 sidebar should still be open
w2.SocialSidebar.hide();
ok(w1.SocialSidebar.opened, "w1 sidebar is opened");
ok(!w2.SocialSidebar.opened, "w2 sidebar is closed");
ok(sbrowser2.parentNode.hidden, "w2 sidebar is hidden");
// open a 3rd window from w2, it should inherit the state of w2
openWindowAndWaitForInit(w2, function(w3) {
// since the sidebar is not open, we need to ensure the provider
// is selected to test we inherited the provider from the opener
w3.SocialSidebar.ensureProvider();
is(w3.SocialSidebar.provider, w2.SocialSidebar.provider, "w3 has same provider as w2");
ok(!w3.SocialSidebar.opened, "w2 sidebar is closed");
// open a 4th window from w1, it should inherit the state of w1
openWindowAndWaitForInit(w1, function(w4) {
is(w4.SocialSidebar.provider, w1.SocialSidebar.provider, "w4 has same provider as w1");
ok(w4.SocialSidebar.opened, "w4 sidebar is opened");
finishCheck();
});
});
});
});
}, cbnext);
}, cbnext);
}
}

View File

@ -62,11 +62,9 @@ function runSocialTestWithProvider(manifest, callback, finishcallback) {
// Check that none of the provider's content ends up in history.
function finishCleanUp() {
ok(!SocialSidebar.provider, "no provider in sidebar");
SessionStore.setWindowValue(window, "socialSidebar", "");
for (let i = 0; i < manifests.length; i++) {
let m = manifests[i];
for (let what of ['sidebarURL', 'iconURL', 'shareURL']) {
for (let what of ['iconURL', 'shareURL']) {
if (m[what]) {
yield promiseSocialUrlNotRemembered(m[what]);
}
@ -196,13 +194,6 @@ function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
// internal consistency.
function checkSocialUI(win) {
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
win = win || window;
let doc = win.document;
let enabled = win.SocialUI.enabled;
let active = Social.providers.length > 0 && !win.SocialUI._chromeless &&
!PrivateBrowsingUtils.isWindowPrivate(win);
let sidebarEnabled = win.SocialSidebar.provider ? enabled : false;
// if we have enabled providers, we should also have instances of those
// providers
if (SocialService.hasEnabledProviders) {
@ -210,42 +201,6 @@ function checkSocialUI(win) {
} else {
is(Social.providers.length, 0, "providers are not enabled");
}
// some local helpers to avoid log-spew for the many checks made here.
let numGoodTests = 0, numTests = 0;
function _ok(what, msg) {
numTests++;
if (!ok)
ok(what, msg)
else
++numGoodTests;
}
function _is(a, b, msg) {
numTests++;
if (a != b)
is(a, b, msg)
else
++numGoodTests;
}
function isbool(a, b, msg) {
_is(!!a, !!b, msg);
}
isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?");
// and for good measure, check all the social commands.
isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?");
isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
// and report on overall success of failure of the various checks here.
is(numGoodTests, numTests, "The Social UI tests succeeded.")
}
function waitForNotification(topic, cb) {
function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
cb();
}
Services.obs.addObserver(observer, topic, false);
}
function setManifestPref(name, manifest) {
@ -261,32 +216,6 @@ function getManifestPrefname(aManifest) {
return "social.manifest." + originUri.hostPort.replace('.','-');
}
function setBuiltinManifestPref(name, manifest) {
// we set this as a default pref, it must not be a user pref
manifest.builtin = true;
let string = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(manifest);
Services.prefs.getDefaultBranch(null).setComplexValue(name, Ci.nsISupportsString, string);
// verify this is set on the default branch
let stored = Services.prefs.getComplexValue(name, Ci.nsISupportsString).data;
is(stored, string.data, "manifest '"+name+"' stored in default prefs");
// don't dirty our manifest, we'll need it without this flag later
delete manifest.builtin;
// verify we DO NOT have a user-level pref
ok(!Services.prefs.prefHasUserValue(name), "manifest '"+name+"' is not in user-prefs");
}
function resetBuiltinManifestPref(name) {
Services.prefs.getDefaultBranch(null).deleteBranch(name);
is(Services.prefs.getDefaultBranch(null).getPrefType(name),
Services.prefs.PREF_INVALID, "default manifest removed");
}
function ensureEventFired(elem, event) {
return BrowserTestUtils.waitForEvent(elem, event, true);
}
function ensureFrameLoaded(frame, uri) {
return new Promise(resolve => {
if (frame.contentDocument && frame.contentDocument.readyState == "complete" &&
@ -303,243 +232,6 @@ function ensureFrameLoaded(frame, uri) {
});
}
// chat test help functions
// And lots of helpers for the resize tests.
function get3ChatsForCollapsing(mode, cb) {
// We make one chat, then measure its size. We then resize the browser to
// 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.
let chatbar = getChatBar();
let chatWidth = undefined;
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
makeChat(mode, "first chat", function() {
// got the first one.
checkPopup();
ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
// we kinda cheat here and get the width of the first chat, assuming
// that all future chats will have the same width when open.
chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
let desired = chatWidth * 2.5;
resizeWindowToChatAreaWidth(desired, function(sizedOk) {
ok(sizedOk, "can't do any tests without this width");
checkPopup();
makeChat(mode, "second chat", function() {
is(chatbar.childNodes.length, 2, "now have 2 chats");
checkPopup();
// and create the third.
makeChat(mode, "third chat", function() {
is(chatbar.childNodes.length, 3, "now have 3 chats");
checkPopup();
// XXX - this is a hacky implementation detail around the order of
// the chats. Ideally things would be a little more sane wrt the
// other in which the children were created.
let second = chatbar.childNodes[2];
let first = chatbar.childNodes[1];
let third = chatbar.childNodes[0];
is(first.collapsed, true, "first collapsed state as promised");
is(second.collapsed, false, "second collapsed state as promised");
is(third.collapsed, false, "third collapsed state as promised");
is(chatbar.selectedChat, third, "third is selected as promised")
info("have 3 chats for collapse testing - starting actual test...");
cb(first, second, third);
}, mode);
}, mode);
});
}, mode);
}
function makeChat(mode, uniqueid, cb) {
info("making a chat window '" + uniqueid +"'");
let provider = SocialSidebar.provider;
let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
// chatURL is not a part of the provider class, but is added by tests if we
// want to use a specific url (different than above) for testing
if (provider.chatURL) {
chatUrl = provider.chatURL;
}
// Note that we use promiseChatLoaded instead of the callback to ensure the
// content has started loading.
let chatbox = getChatBar().openChat({
origin: provider.origin,
title: provider.name,url: chatUrl + "?id=" + uniqueid,
mode: mode
});
chatbox.promiseChatLoaded.then(
() => {
info("chat window has opened");
chatbox.content.messageManager.sendAsyncMessage("Social:SetDocumentTitle", {
title: uniqueid
});
cb(chatbox);
});
}
function checkPopup() {
// popup only showing if any collapsed popup children.
let chatbar = getChatBar();
let numCollapsed = 0;
for (let chat of chatbar.childNodes) {
if (chat.collapsed) {
numCollapsed += 1;
// and it have a menuitem weakmap
is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item");
} else {
ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item");
}
}
is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state");
is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children");
// todo - check each individual elt is what we expect?
}
// Resize the main window so the chat area's boxObject is |desired| wide.
// Does a callback passing |true| if the window is now big enough or false
// if we couldn't resize large enough to satisfy the test requirement.
function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
let current = getChatBar().getBoundingClientRect().width;
let delta = desired - current;
info(count + ": resizing window so chat area is " + desired + " wide, currently it is "
+ current + ". Screen avail is " + window.screen.availWidth
+ ", current outer width is " + window.outerWidth);
// WTF? Sometimes we will get fractional values due to the - err - magic
// of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
let widthDeltaCloseEnough = function(d) {
return Math.abs(d) < 2;
}
// attempting to resize by (0,0), unsurprisingly, doesn't cause a resize
// event - so just callback saying all is well.
if (widthDeltaCloseEnough(delta)) {
info(count + ": skipping this as screen width is close enough");
executeSoon(function() {
cb(true);
});
return;
}
// On lo-res screens we may already be maxed out but still smaller than the
// requested size, so asking to resize up also will not cause a resize event.
// So just callback now saying the test must be skipped.
if (window.screen.availWidth - window.outerWidth < delta) {
info(count + ": skipping this as screen available width is less than necessary");
executeSoon(function() {
cb(false);
});
return;
}
function resize_handler(event) {
// we did resize - but did we get far enough to be able to continue?
let newSize = getChatBar().getBoundingClientRect().width;
let sizedOk = widthDeltaCloseEnough(newSize - desired);
if (!sizedOk)
return;
window.removeEventListener("resize", resize_handler, true);
info(count + ": resized window width is " + newSize);
executeSoon(function() {
cb(sizedOk);
});
}
// Otherwise we request resize and expect a resize event
window.addEventListener("resize", resize_handler, true);
window.resizeBy(delta, 0);
}
function resizeAndCheckWidths(first, second, third, checks, cb) {
if (checks.length == 0) {
cb(); // nothing more to check!
return;
}
let count = checks.length;
let [width, numExpectedVisible, why] = checks.shift();
info("<< Check " + count + ": " + why);
info(count + ": " + "resizing window to " + width + ", expect " + numExpectedVisible + " visible items");
resizeWindowToChatAreaWidth(width, function(sizedOk) {
checkPopup();
ok(sizedOk, count+": window resized correctly");
function collapsedObserver(r, m) {
if ([first, second, third].filter(item => !item.collapsed).length == numExpectedVisible) {
if (m) {
m.disconnect();
}
ok(true, count + ": " + "correct number of chats visible");
info(">> Check " + count);
executeSoon(function() {
resizeAndCheckWidths(first, second, third, checks, cb);
});
}
}
let m = new MutationObserver(collapsedObserver);
m.observe(first, {attributes: true });
m.observe(second, {attributes: true });
m.observe(third, {attributes: true });
// and just in case we are already at the right size, explicitly call the
// observer.
collapsedObserver(undefined, m);
}, count);
}
function getChatBar() {
let cb = document.getElementById("pinnedchats");
cb.hidden = false;
return cb;
}
function getPopupWidth() {
let chatbar = getChatBar();
let popup = chatbar.menupopup;
ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
let cs = document.defaultView.getComputedStyle(popup.parentNode);
let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
return popup.parentNode.getBoundingClientRect().width + margins;
}
function promiseNodeRemoved(aNode) {
return new Promise(resolve => {
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 != aNode) {
continue;
}
observer.disconnect();
resolve();
}
}
});
observer.observe(parent, {childList: true});
});
}
function promiseCloseChat(chat) {
let promise = promiseNodeRemoved(chat);
chat.close();
return promise;
}
function closeAllChats() {
let chatbar = getChatBar();
while (chatbar.selectedChat) {
yield promiseCloseChat(chatbar.selectedChat);
}
}
function openChatViaUser() {
let sidebarDoc = document.getElementById("social-sidebar-browser").contentDocument;
let button = sidebarDoc.getElementById("chat-opener");
// Note we must use synthesizeMouseAtCenter() rather than calling
// .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
// to be true.
EventUtils.synthesizeMouseAtCenter(button, {}, sidebarDoc.defaultView);
}
// Support for going on and offline.
// (via browser/base/content/test/browser_bookmark_titles.js)
var origProxyType = Services.prefs.getIntPref('network.proxy.type');

View File

@ -13,7 +13,7 @@ var data = {
"icon64URL": "chrome://branding/content/icon64.png",
// at least one of these must be defined
"sidebarURL": "/browser/browser/base/content/test/social/social_sidebar.html",
"shareURL": "/browser/browser/base/content/test/social/social_share.html",
"postActivationURL": "/browser/browser/base/content/test/social/social_postActivation.html",
// should be available for display purposes

View File

@ -13,7 +13,7 @@ var data = {
"icon64URL": "chrome://branding/content/icon64.png",
// at least one of these must be defined
"sidebarURL": "/browser/browser/base/content/test/social/social_sidebar_empty.html",
"shareURL": "/browser/browser/base/content/test/social/social_share.html",
"postActivationURL": "/browser/browser/base/content/test/social/social_postActivation.html",
// should be available for display purposes

View File

@ -1,15 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<title>test chat window</title>
</head>
<body>
<p>This is a test social chat window.</p>
<!-- a couple of input fields to help with focus testing -->
<input id="input1"/>
<input id="input2"/>
<!-- an iframe here so this one page generates multiple load events -->
<iframe id="iframe" src="data:text/plain:this is an iframe"></iframe>
</body>
</html>

View File

@ -1,25 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<script>
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
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent("SocialTest-DoneMakeWider", true, true, {});
document.documentElement.dispatchEvent(evt);
}, false);
window.addEventListener("socialTest-CloseSelf", function(e) {
window.close();
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent("SocialTest-DoneCloseSelf", true, true, {});
document.documentElement.dispatchEvent(evt);
}, false);
</script>
</head>
<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>
</html>

View File

@ -1,8 +0,0 @@
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>This is a test social panel.</p>
</body>
</html>

View File

@ -1,17 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<script>
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>
<p>This is a test social sidebar.</p>
<button id="chat-opener" onclick="navigator.mozSocial.openChatWindow('./social_chat.html');"/>
</body>
</html>

View File

@ -1,8 +0,0 @@
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>This is a test social sidebar.</p>
</body>
</html>

View File

@ -101,7 +101,6 @@ browser.jar:
#endif
content/browser/browser-thumbnails.js (content/browser-thumbnails.js)
content/browser/browser-trackingprotection.js (content/browser-trackingprotection.js)
* content/browser/chatWindow.xul (content/chatWindow.xul)
content/browser/tab-content.js (content/tab-content.js)
content/browser/content.js (content/content.js)
content/browser/social-content.js (content/social-content.js)
@ -189,7 +188,6 @@ browser.jar:
#ifdef XP_WIN
content/browser/win6BrowserOverlay.xul (content/win6BrowserOverlay.xul)
#endif
content/browser/socialchat.xml (content/socialchat.xml)
# the following files are browser-specific overrides
* content/browser/license.html (/toolkit/content/license.html)
% override chrome://global/content/license.html chrome://browser/content/license.html

View File

@ -549,14 +549,8 @@ const CustomizableWidgets = [
let win = doc.defaultView;
let menu = doc.getElementById("viewSidebarMenu");
// First clear any existing menuitems then populate. Social sidebar
// options may not have been added yet, so we do that here. Add it to the
// First clear any existing menuitems then populate. Add it to the
// standard menu first, then copy all sidebar options to the panel.
win.SocialSidebar.clearProviderMenus();
let providerMenuSeps = menu.getElementsByClassName("social-provider-menu");
if (providerMenuSeps.length > 0)
win.SocialSidebar.populateProviderMenu(providerMenuSeps[0]);
let sidebarItems = doc.getElementById("PanelUI-sidebarItems");
clearSubview(sidebarItems);
fillSubviewFromMenuItems([...menu.children], sidebarItems);

View File

@ -161,7 +161,6 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
<!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
<!ENTITY bookmarkThisPageCmd.commandkey "d">
<!ENTITY markPageCmd.commandkey "l">
<!-- LOCALIZATION NOTE (findShareServices.label):
- Use the unicode ellipsis char, \u2026,
- or use "..." if \u2026 doesn't suit traditions in your locale. -->
@ -774,22 +773,8 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY syncReAuthItem.accesskey "R">
<!ENTITY syncToolbarButton.label "Sync">
<!ENTITY socialToolbar.title "Social Toolbar Button">
<!ENTITY social.ok.label "OK">
<!ENTITY social.ok.accesskey "O">
<!ENTITY social.toggleSidebar.label "Show sidebar">
<!ENTITY social.toggleSidebar.accesskey "s">
<!ENTITY social.addons.label "Manage Services…">
<!ENTITY social.toggleNotifications.label "Show desktop notifications">
<!ENTITY social.toggleNotifications.accesskey "n">
<!ENTITY social.learnMore.label "Learn more…">
<!ENTITY social.learnMore.accesskey "l">
<!ENTITY social.directory.label "Activations Directory">
<!ENTITY social.directory.text "You can activate Share services from the directory.">
<!ENTITY social.directory.button "Take me there!">
@ -810,15 +795,6 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
<!ENTITY customizeMode.lwthemes.menuGetMore.accessKey "G">
<!ENTITY social.chatBar.commandkey "c">
<!ENTITY social.chatBar.label "Focus chats">
<!ENTITY social.chatBar.accesskey "c">
<!ENTITY social.markpageMenu.accesskey "P">
<!ENTITY social.markpageMenu.label "Save Page To…">
<!ENTITY social.marklinkMenu.accesskey "L">
<!ENTITY social.marklinkMenu.label "Save Link To…">
<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
<!ENTITY getUserMedia.selectCamera.accesskey "C">
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">

View File

@ -1,323 +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/. */
"use strict";
// A module for working with chat windows.
this.EXPORTED_SYMBOLS = ["Chat", "kDefaultButtonSet"];
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, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kDefaultButtonSet = new Set(["minimize", "swap", "close"]);
const kHiddenDefaultButtons = new Set(["minimize", "close"]);
var gCustomButtons = new Map();
// A couple of internal helper function.
function isWindowChromeless(win) {
// XXX - stolen from browser-social.js, but there's no obvious place to
// put this so it can be shared.
// Is this a popup window that doesn't want chrome shown?
let docElem = win.document.documentElement;
// extrachrome is not restored during session restore, so we need
// to check for the toolbar as well.
let chromeless = docElem.getAttribute("chromehidden").includes("extrachrome") ||
docElem.getAttribute('chromehidden').includes("toolbar");
return chromeless;
}
function isWindowGoodForChats(win) {
return !win.closed &&
!!win.document.getElementById("pinnedchats") &&
!isWindowChromeless(win) &&
!PrivateBrowsingUtils.isWindowPrivate(win);
}
function getChromeWindow(contentWin) {
return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
/*
* The exported Chat object
*/
var Chat = {
/**
* Iterator of <chatbox> elements from this module in all windows.
*/
get chatboxes() {
return function*() {
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
let chatbar = win.document.getElementById("pinnedchats");
if (!chatbar)
continue;
// Make a new array instead of the live NodeList so this iterator can be
// used for closing/deleting.
let chatboxes = [...chatbar.children];
for (let chatbox of chatboxes) {
yield chatbox;
}
}
// include standalone chat windows
winEnum = Services.wm.getEnumerator("Social:Chat");
while (winEnum.hasMoreElements()) {
let win = winEnum.getNext();
if (win.closed)
continue;
yield win.document.getElementById("chatter");
}
}();
},
/**
* Open a new chatbox.
*
* @param contentWindow [optional]
* The content window that requested this chat. May be null.
* @param options
* Object that may contain the following properties:
* - origin
* The origin for the chat. This is primarily used as an identifier
* to help identify all chats from the same provider.
* - title
* The title to be used if a new chat window is created.
* - url
* The URL for the that. Should be under the origin. If an existing
* chatbox exists with the same URL, it will be reused and returned.
* - mode [optional]
* May be undefined or 'minimized'
* - focus [optional]
* Indicates if the chatbox should be focused. If undefined the chat
* will be focused if the window is currently handling user input (ie,
* if the chat is being opened as a direct result of user input)
* - remote [optional]
* Indicates if the chatbox browser should use the remote bindings
* to run in the content process when TRUE.
* @param callback
* Function to be invoked once the chat constructed. The chatbox binding
* is passed as the first argument.
*
* @return A chatbox binding. This binding has a number of promises which
* can be used to determine when the chatbox is being created and
* has loaded. Will return null if no chat can be created (Which
* should only happen in edge-cases)
*/
open: function(contentWindow, options, callback) {
let chromeWindow = this.findChromeWindowForChats(contentWindow);
if (!chromeWindow) {
Cu.reportError("Failed to open a chat window - no host window could be found.");
return null;
}
let chatbar = chromeWindow.document.getElementById("pinnedchats");
chatbar.hidden = false;
if (options.remote) {
// Double check that current window can handle remote browser elements.
let browser = chromeWindow.gBrowser && chromeWindow.gBrowser.selectedBrowser;
if (!browser || browser.getAttribute("remote") != "true") {
options.remote = false;
}
}
let chatbox = chatbar.openChat(options, callback);
// getAttention is ignored if the target window is already foreground, so
// we can call it unconditionally.
chromeWindow.getAttention();
// If focus is undefined we want automatic focus handling, and only focus
// if a direct result of user action.
if (!("focus" in options)) {
let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
options.focus = dwu.isHandlingUserInput;
}
if (options.focus) {
chatbar.focus();
}
return chatbox;
},
/**
* Close all chats from the specified origin.
*
* @param origin
* The origin from which all chats should be closed.
*/
closeAll: function(origin) {
for (let chatbox of this.chatboxes) {
if (chatbox.content.getAttribute("origin") != origin) {
continue;
}
chatbox.close();
}
},
/**
* Focus the chatbar associated with a window
*
* @param window
*/
focus: function(win) {
let chatbar = win.document.getElementById("pinnedchats");
if (chatbar && !chatbar.hidden) {
chatbar.focus();
}
},
// This is exported as socialchat.xml needs to find a window when a chat
// is re-docked.
findChromeWindowForChats: function(preferredWindow) {
if (preferredWindow) {
preferredWindow = getChromeWindow(preferredWindow);
if (isWindowGoodForChats(preferredWindow)) {
return preferredWindow;
}
}
// no good - we just use the "most recent" browser window which can host
// chats (we used to try and "group" all chats in the same browser window,
// but that didn't work out so well - see bug 835111
// Try first the most recent window as getMostRecentWindow works
// even on platforms where getZOrderDOMWindowEnumerator is broken
// (ie. Linux). This will handle most cases, but won't work if the
// foreground window is a popup.
let mostRecent = Services.wm.getMostRecentWindow("navigator:browser");
if (isWindowGoodForChats(mostRecent))
return mostRecent;
let topMost, enumerator;
// *sigh* - getZOrderDOMWindowEnumerator is broken except on Mac and
// Windows. We use BROKEN_WM_Z_ORDER as that is what some other code uses
// and a few bugs recommend searching mxr for this symbol to identify the
// workarounds - we want this code to be hit in such searches.
let os = Services.appinfo.OS;
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
if (BROKEN_WM_Z_ORDER) {
// this is oldest to newest and no way to change the order.
enumerator = Services.wm.getEnumerator("navigator:browser");
} else {
// here we explicitly ask for bottom-to-top so we can use the same logic
// where BROKEN_WM_Z_ORDER is true.
enumerator = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
}
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (!win.closed && isWindowGoodForChats(win))
topMost = win;
}
return topMost;
},
/**
* Adds a button to the collection of custom buttons that can be added to the
* titlebar of a chatbox.
* For the button to be visible, `Chat#loadButtonSet` has to be called with
* the new buttons' ID in the buttonSet argument.
*
* @param {Object} button Button object that may contain the following fields:
* - {String} id Button identifier.
* - {Function} [onBuild] Function that returns a valid DOM node to
* represent the button.
* - {Function} [onCommand] Callback function that is invoked when the DOM
* node is clicked.
*/
registerButton: function(button) {
if (gCustomButtons.has(button.id))
return;
gCustomButtons.set(button.id, button);
},
/**
* Load a set of predefined buttons in a chatbox' titlebar.
*
* @param {XULDOMNode} chatbox Chatbox XUL element.
* @param {Set|String} buttonSet Set of buttons to show in the titlebar. This
* may be a comma-separated string or a predefined
* set object.
*/
loadButtonSet: function(chatbox, buttonSet = kDefaultButtonSet) {
if (!buttonSet)
return;
// When the buttonSet is coming from an XML attribute, it will be a string.
if (typeof buttonSet == "string") {
buttonSet = buttonSet.split(",").map(button => button.trim());
}
// Make sure to keep the current set around.
chatbox.setAttribute("buttonSet", [...buttonSet].join(","));
let isUndocked = !chatbox.chatbar;
let document = chatbox.ownerDocument;
let titlebarNode = document.getAnonymousElementByAttribute(chatbox, "class",
"chat-titlebar");
let buttonsSeen = new Set();
for (let buttonId of buttonSet) {
buttonId = buttonId.trim();
buttonsSeen.add(buttonId);
let nodes, node;
if (kDefaultButtonSet.has(buttonId)) {
node = document.getAnonymousElementByAttribute(chatbox, "anonid", buttonId);
if (!node)
continue;
node.hidden = isUndocked && kHiddenDefaultButtons.has(buttonId) ? true : false;
} else if (gCustomButtons.has(buttonId)) {
let button = gCustomButtons.get(buttonId);
let buttonClass = "chat-" + buttonId;
// Custom buttons are not defined in the chatbox binding, thus not
// anonymous elements.
nodes = titlebarNode.getElementsByClassName(buttonClass);
node = nodes && nodes.length ? nodes[0] : null;
if (!node) {
// Allow custom buttons to build their own button node.
if (button.onBuild) {
node = button.onBuild(chatbox);
} else {
// We can also build a normal toolbarbutton to insert.
node = document.createElementNS(kNSXUL, "toolbarbutton");
node.classList.add(buttonClass);
node.classList.add("chat-toolbarbutton");
}
if (button.onCommand) {
node.addEventListener("command", e => {
button.onCommand(e, chatbox);
});
}
titlebarNode.appendChild(node);
}
// When the chat is undocked and the button wants to be visible then, it
// will be.
node.hidden = isUndocked && !button.visibleWhenUndocked;
} else {
Cu.reportError("Chatbox button '" + buttonId + "' could not be found!\n");
}
}
// Hide any button that is part of the default set, but not of the current set.
for (let button of kDefaultButtonSet) {
if (!buttonsSeen.has(button))
document.getAnonymousElementByAttribute(chatbox, "anonid", button).hidden = true;
}
}
};

View File

@ -108,11 +108,6 @@ this.Social = {
return !this._disabledForSafeMode && this.providers.length > 0;
},
toggleNotifications: function SocialNotifications_toggle() {
let prefValue = Services.prefs.getBoolPref("social.toast-notifications.enabled");
Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
},
_getProviderFromOrigin: function (origin) {
for (let p of this.providers) {
if (p.origin == origin) {
@ -142,10 +137,6 @@ this.Social = {
}
};
function schedule(callback) {
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
}
function sizeSocialPanelToContent(panel, iframe, requestedSize) {
let doc = iframe.contentDocument;
if (!doc || !doc.body) {

View File

@ -18,7 +18,6 @@ EXTRA_JS_MODULES += [
'BrowserUsageTelemetry.jsm',
'CaptivePortalWatcher.jsm',
'CastingApps.jsm',
'Chat.jsm',
'ContentClick.jsm',
'ContentCrashHandlers.jsm',
'ContentLinkHandler.jsm',

View File

@ -614,14 +614,7 @@ Tester.prototype = {
sidebar.docShell.createAboutBlankContentViewer(null);
sidebar.setAttribute("src", "about:blank");
// Do the same for the social sidebar.
let socialSidebar = document.getElementById("social-sidebar-browser");
socialSidebar.setAttribute("src", "data:text/html;charset=utf-8,");
socialSidebar.docShell.createAboutBlankContentViewer(null);
socialSidebar.setAttribute("src", "about:blank");
SelfSupportBackend.uninit();
SocialFlyout.unload();
SocialShare.uninit();
}

View File

@ -1,298 +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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SocialService", "resource://gre/modules/SocialService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Social", "resource:///modules/Social.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
this.EXPORTED_SYMBOLS = [
"MozSocialAPI", "openChatWindow", "findChromeWindowForChats", "closeAllChatWindows",
"hookWindowCloseForPanelClose"
];
this.MozSocialAPI = {
_enabled: false,
_everEnabled: false,
set enabled(val) {
let enable = !!val;
if (enable == this._enabled) {
return;
}
this._enabled = enable;
if (enable) {
Services.obs.addObserver(injectController, "document-element-inserted", false);
if (!this._everEnabled) {
this._everEnabled = true;
Services.telemetry.getHistogramById("SOCIAL_ENABLED_ON_SESSION").add(true);
}
} else {
Services.obs.removeObserver(injectController, "document-element-inserted");
}
}
};
// Called on document-element-inserted, checks that the API should be injected,
// and then calls attachToWindow as appropriate
function injectController(doc, topic, data) {
try {
let window = doc.defaultView;
if (!window || PrivateBrowsingUtils.isContentWindowPrivate(window))
return;
// Do not attempt to load the API into about: error pages
if (doc.documentURIObject.scheme == "about") {
return;
}
let containingBrowser = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
// limit injecting into social panels or same-origin browser tabs if
// social.debug.injectIntoTabs is enabled
let allowTabs = false;
try {
allowTabs = containingBrowser.contentWindow == window &&
Services.prefs.getBoolPref("social.debug.injectIntoTabs");
} catch (e) {}
let origin = containingBrowser.getAttribute("origin");
if (!allowTabs && !origin) {
return;
}
// we always handle window.close on social content, even if they are not
// "enabled".
hookWindowCloseForPanelClose(window);
SocialService.getProvider(doc.nodePrincipal.origin, function(provider) {
if (provider && provider.enabled) {
attachToWindow(provider, window);
}
});
} catch (e) {
Cu.reportError("MozSocialAPI injectController: unable to attachToWindow for " + doc.location + ": " + e);
}
}
// Loads mozSocial support functions associated with provider into targetWindow
function attachToWindow(provider, targetWindow) {
// If the loaded document isn't from the provider's origin (or a protocol
// that inherits the principal), don't attach the mozSocial API.
let targetDocURI = targetWindow.document.documentURIObject;
if (!provider.isSameOrigin(targetDocURI)) {
let msg = "MozSocialAPI: not attaching mozSocial API for " + provider.origin +
" to " + targetDocURI.spec + " since origins differ."
Services.console.logStringMessage(msg);
return;
}
let mozSocialObj = {
openChatWindow: {
enumerable: true,
configurable: true,
writable: true,
value: function(toURL, callback) {
let url = targetWindow.document.documentURIObject.resolve(toURL);
let dwu = getChromeWindow(targetWindow)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
openChatWindow(targetWindow, provider, url, chatWin => {
if (chatWin && dwu.isHandlingUserInput)
chatWin.focus();
if (callback)
callback(!!chatWin);
});
}
},
openPanel: {
enumerable: true,
configurable: true,
writable: true,
value: function(toURL, offset, callback) {
let chromeWindow = getChromeWindow(targetWindow);
if (!chromeWindow.SocialFlyout)
return;
let url = targetWindow.document.documentURIObject.resolve(toURL);
if (!provider.isSameOrigin(url))
return;
chromeWindow.SocialFlyout.open(url, offset, callback);
}
},
closePanel: {
enumerable: true,
configurable: true,
writable: true,
value: function(toURL, offset, callback) {
let chromeWindow = getChromeWindow(targetWindow);
if (!chromeWindow.SocialFlyout || !chromeWindow.SocialFlyout.panel)
return;
chromeWindow.SocialFlyout.panel.hidePopup();
}
},
// allow a provider to share to other providers through the browser
share: {
enumerable: true,
configurable: true,
writable: true,
value: function(data) {
let chromeWindow = getChromeWindow(targetWindow);
if (!chromeWindow.SocialShare || chromeWindow.SocialShare.shareButton.hidden)
throw new Error("Share is unavailable");
// ensure user action initates the share
let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (!dwu.isHandlingUserInput)
throw new Error("Attempt to share without user input");
// limit to a few params we want to support for now
let dataOut = {};
for (let sub of ["url", "title", "description", "source"]) {
dataOut[sub] = data[sub];
}
if (data.image)
dataOut.previews = [data.image];
chromeWindow.SocialShare.sharePage(null, dataOut);
}
},
getAttention: {
enumerable: true,
configurable: true,
writable: true,
value: function() {
getChromeWindow(targetWindow).getAttention();
}
},
isVisible: {
enumerable: true,
configurable: true,
get: function() {
return targetWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).isActive;
}
}
};
let contentObj = Cu.createObjectIn(targetWindow);
Object.defineProperties(contentObj, mozSocialObj);
Cu.makeObjectPropsNormal(contentObj);
targetWindow.navigator.wrappedJSObject.__defineGetter__("mozSocial", function() {
// We do this in a getter, so that we create these objects
// only on demand (this is a potential concern, since
// otherwise we might add one per iframe, and keep them
// alive for as long as the window is alive).
delete targetWindow.navigator.wrappedJSObject.mozSocial;
return targetWindow.navigator.wrappedJSObject.mozSocial = contentObj;
});
}
function hookWindowCloseForPanelClose(targetWindow) {
let _mozSocialDOMWindowClose;
if ("messageManager" in targetWindow) {
let _mozSocialSwapped;
let mm = targetWindow.messageManager;
mm.sendAsyncMessage("Social:HookWindowCloseForPanelClose");
mm.addMessageListener("Social:DOMWindowClose", _mozSocialDOMWindowClose = function() {
targetWindow.removeEventListener("SwapDocShells", _mozSocialSwapped);
closePanel(targetWindow);
});
targetWindow.addEventListener("SwapDocShells", _mozSocialSwapped = function(ev) {
targetWindow.removeEventListener("SwapDocShells", _mozSocialSwapped);
targetWindow = ev.detail;
targetWindow.messageManager.addMessageListener("Social:DOMWindowClose", _mozSocialDOMWindowClose);
});
return;
}
// We allow window.close() to close the panel, so add an event handler for
// this, then cancel the event (so the window itself doesn't die) and
// close the panel instead.
// However, this is typically affected by the dom.allow_scripts_to_close_windows
// preference, but we can avoid that check by setting a flag on the window.
let dwu = targetWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
dwu.allowScriptsToClose();
targetWindow.addEventListener("DOMWindowClose", _mozSocialDOMWindowClose = function(evt) {
let elt = targetWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
closePanel(elt);
// preventDefault stops the default window.close() function being called,
// which doesn't actually close anything but causes things to get into
// a bad state (an internal 'closed' flag is set and debug builds start
// asserting as the window is used.).
// None of the windows we inject this API into are suitable for this
// default close behaviour, so even if we took no action above, we avoid
// the default close from doing anything.
evt.preventDefault();
}, true);
}
function closePanel(elt) {
while (elt) {
if (elt.localName == "panel") {
elt.hidePopup();
break;
} else if (elt.localName == "chatbox") {
elt.close();
break;
}
elt = elt.parentNode;
}
}
function schedule(callback) {
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
}
function getChromeWindow(contentWin) {
return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
this.openChatWindow =
function openChatWindow(contentWindow, provider, url, callback, mode) {
let fullURI = provider.resolveUri(url);
if (!provider.isSameOrigin(fullURI)) {
Cu.reportError("Failed to open a social chat window - the requested URL is not the same origin as the provider.");
return;
}
let chatbox = Chat.open(contentWindow, {
origin: provider.origin,
title: provider.name,
url: fullURI.spec,
mode: mode
});
if (callback) {
chatbox.promiseChatLoaded.then(() => {
callback(chatbox);
});
}
}
this.closeAllChatWindows = function closeAllChatWindows(provider) {
return Chat.closeAll(provider.origin);
}

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, "MozSocialAPI", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "closeAllChatWindows", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "etld",
@ -141,7 +139,6 @@ XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
try {
if (ActiveProviders.has(manifest.origin)) {
// enable the api when a provider is enabled
MozSocialAPI.enabled = true;
let provider = new SocialProvider(manifest);
providers[provider.origin] = provider;
}
@ -404,7 +401,6 @@ this.SocialService = {
throw new Error("SocialService.addProvider: provider with this origin already exists");
// enable the api when a provider is enabled
MozSocialAPI.enabled = true;
let provider = new SocialProvider(manifest);
SocialServiceInternal.providers[provider.origin] = provider;
ActiveProviders.add(provider.origin);
@ -434,8 +430,6 @@ this.SocialService = {
ActiveProviders.delete(provider.origin);
delete SocialServiceInternal.providers[origin];
// disable the api if we have no enabled providers
MozSocialAPI.enabled = SocialServiceInternal.enabled;
if (addon) {
// we have to do this now so the addon manager ui will update an uninstall
@ -503,7 +497,7 @@ this.SocialService = {
},
_manifestFromData: function(type, data, installOrigin) {
let featureURLs = ['sidebarURL', 'shareURL'];
let featureURLs = ['shareURL'];
let resolveURLs = featureURLs.concat(['postActivationURL']);
if (type == 'directory' || type == 'internal') {
@ -704,7 +698,6 @@ function SocialProvider(input) {
this.iconURL = input.iconURL;
this.icon32URL = input.icon32URL;
this.icon64URL = input.icon64URL;
this.sidebarURL = input.sidebarURL;
this.shareURL = input.shareURL;
this.postActivationURL = input.postActivationURL;
this.origin = input.origin;
@ -760,29 +753,11 @@ SocialProvider.prototype = {
return 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 add/update a notification icon.
setAmbientNotification: function(notification) {
if (!this.ambientNotificationIcons[notification.name] &&
Object.keys(this.ambientNotificationIcons).length >= 3) {
throw new Error("ambient notification limit reached");
}
this.ambientNotificationIcons[notification.name] = notification;
Services.obs.notifyObservers(null, "social:ambient-notification-changed", this.origin);
},
// Internal helper methods
_activate: function _activate() {
},
_terminate: function _terminate() {
closeAllChatWindows(this);
this.errorState = null;
},

View File

@ -9,7 +9,6 @@ XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
if CONFIG['MOZ_SOCIAL']:
# social is turned off for android
EXTRA_JS_MODULES += [
'MozSocialAPI.jsm',
'SocialService.jsm',
]

View File

@ -16,17 +16,16 @@ function run_test() {
{ // normal provider
name: "provider 1",
origin: "https://example1.com",
sidebarURL: "https://example1.com/sidebar/",
shareURL: "https://example1.com/share/",
},
{ // provider without workerURL
name: "provider 2",
origin: "https://example2.com",
sidebarURL: "https://example2.com/sidebar/",
shareURL: "https://example2.com/share/",
}
];
Cu.import("resource://gre/modules/SocialService.jsm");
Cu.import("resource://gre/modules/MozSocialAPI.jsm");
let runner = new AsyncRunner();
let next = runner.next.bind(runner);
@ -45,7 +44,6 @@ function* testAddProviders(manifests, next) {
do_check_false(SocialService.enabled);
let provider = yield SocialService.addProvider(manifests[0], next);
do_check_true(SocialService.enabled);
do_check_true(MozSocialAPI._enabled);
do_check_false(provider.enabled);
provider = yield SocialService.addProvider(manifests[1], next);
do_check_false(provider.enabled);
@ -152,7 +150,7 @@ function* testOrderedProviders(manifests, next) {
let startDate = Date.now() * 1000;
for (let i = 0; i < 10; i++) {
visits.push({
uri: Services.io.newURI(providers[1].sidebarURL + i, null, null),
uri: Services.io.newURI(providers[1].shareURL + i, null, null),
visitDate: startDate + i
});
}