mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1289549 P2 socialapi remove sidebar and chat, r=florian
MozReview-Commit-ID: JzSDh41IPBu
This commit is contained in:
parent
51cf1dcb66
commit
4987c70c34
@ -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/>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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>
|
@ -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]
|
||||
|
@ -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)
|
||||
]);
|
||||
}
|
||||
|
@ -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"
|
||||
};
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -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: {} });
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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');
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<p>This is a test social panel.</p>
|
||||
</body>
|
||||
</html>
|
@ -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>
|
@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<p>This is a test social sidebar.</p>
|
||||
</body>
|
||||
</html>
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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:">
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
@ -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) {
|
||||
|
@ -18,7 +18,6 @@ EXTRA_JS_MODULES += [
|
||||
'BrowserUsageTelemetry.jsm',
|
||||
'CaptivePortalWatcher.jsm',
|
||||
'CastingApps.jsm',
|
||||
'Chat.jsm',
|
||||
'ContentClick.jsm',
|
||||
'ContentCrashHandlers.jsm',
|
||||
'ContentLinkHandler.jsm',
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
},
|
||||
|
||||
|
@ -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',
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user