mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1132301: Part 2 - add navigator.mozLoop methods to allow interaction between Loop and the Social API. r=Standard8,mixedpuppy
This commit is contained in:
parent
6e29d331f9
commit
6d0af5f8be
@ -27,6 +27,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Social",
|
||||
"resource:///modules/Social.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
|
||||
return Cc["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Ci.nsIXULAppInfo)
|
||||
@ -199,6 +201,10 @@ function injectLoopAPI(targetWindow) {
|
||||
let roomsAPI;
|
||||
let callsAPI;
|
||||
let savedWindowListeners = new Map();
|
||||
let socialProviders;
|
||||
const kShareWidgetId = "social-share-button";
|
||||
let socialShareButtonListenersAdded = false;
|
||||
|
||||
|
||||
let api = {
|
||||
/**
|
||||
@ -912,20 +918,205 @@ function injectLoopAPI(targetWindow) {
|
||||
value: function(windowId, active) {
|
||||
MozLoopService.setScreenShareState(windowId, active);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the Social Share widget is available in any of the registered
|
||||
* widget areas (navbar, MenuPanel, etc).
|
||||
*
|
||||
* @return {Boolean} `true` if the widget is available and `false` when it's
|
||||
* still in the Customization palette.
|
||||
*/
|
||||
isSocialShareButtonAvailable: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function() {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win || !win.CustomizableUI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let widget = win.CustomizableUI.getWidget(kShareWidgetId);
|
||||
if (widget) {
|
||||
if (!socialShareButtonListenersAdded) {
|
||||
let eventName = "social:" + kShareWidgetId;
|
||||
Services.obs.addObserver(onShareWidgetChanged, eventName + "-added", false);
|
||||
Services.obs.addObserver(onShareWidgetChanged, eventName + "-removed", false);
|
||||
socialShareButtonListenersAdded = true;
|
||||
}
|
||||
return !!widget.areaType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the Social Share widget to the navbar area, but only when it's not
|
||||
* located anywhere else than the Customization palette.
|
||||
*/
|
||||
addSocialShareButton: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function() {
|
||||
// Don't do anything if the button is already available.
|
||||
if (api.isSocialShareButtonAvailable.value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win || !win.CustomizableUI) {
|
||||
return;
|
||||
}
|
||||
win.CustomizableUI.addWidgetToArea(kShareWidgetId, win.CustomizableUI.AREA_NAVBAR);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Activates the Social Share panel with the Social Provider panel opened
|
||||
* when the popup open.
|
||||
*/
|
||||
addSocialShareProvider: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function() {
|
||||
// Don't do anything if the button is _not_ available.
|
||||
if (!api.isSocialShareButtonAvailable.value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win || !win.SocialShare) {
|
||||
return;
|
||||
}
|
||||
win.SocialShare.showDirectory();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a sorted list of Social Providers that can share URLs. See
|
||||
* `updateSocialProvidersCache()` for more information.
|
||||
*
|
||||
* @return {Array} Sorted list of share-capable Social Providers.
|
||||
*/
|
||||
getSocialShareProviders: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function() {
|
||||
if (socialProviders) {
|
||||
return socialProviders;
|
||||
}
|
||||
return updateSocialProvidersCache();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Share a room URL through a Social Provider with the provided title message.
|
||||
* This action will open the share panel, which is anchored to the Social
|
||||
* Share widget.
|
||||
*
|
||||
* @param {String} providerOrigin Identifier of the targeted Social Provider
|
||||
* @param {String} roomURL URL that points to the standalone client
|
||||
* @param {String} title Message that augments the URL inside the
|
||||
* share message
|
||||
* @param {String} [body] Optional longer message to be displayed
|
||||
* similar to the body of an email
|
||||
*/
|
||||
socialShareRoom: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(providerOrigin, roomURL, title, body = null) {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win || !win.SocialShare) {
|
||||
return;
|
||||
}
|
||||
|
||||
let graphData = {
|
||||
url: roomURL,
|
||||
title: title
|
||||
};
|
||||
if (body) {
|
||||
graphData.body = body;
|
||||
}
|
||||
win.SocialShare.sharePage(providerOrigin, graphData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onStatusChanged(aSubject, aTopic, aData) {
|
||||
let event = new targetWindow.CustomEvent("LoopStatusChanged");
|
||||
/**
|
||||
* Send an event to the content window to indicate that the state on the chrome
|
||||
* side was updated.
|
||||
*
|
||||
* @param {name} name Name of the event, defaults to 'LoopStatusChanged'
|
||||
*/
|
||||
function sendEvent(name = "LoopStatusChanged") {
|
||||
let event = new targetWindow.CustomEvent(name);
|
||||
targetWindow.dispatchEvent(event);
|
||||
};
|
||||
}
|
||||
|
||||
function onStatusChanged(aSubject, aTopic, aData) {
|
||||
sendEvent();
|
||||
}
|
||||
|
||||
function onDOMWindowDestroyed(aSubject, aTopic, aData) {
|
||||
if (targetWindow && aSubject != targetWindow)
|
||||
return;
|
||||
Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed");
|
||||
Services.obs.removeObserver(onStatusChanged, "loop-status-changed");
|
||||
};
|
||||
// Stop listening for changes in the social provider list, if necessary.
|
||||
if (socialProviders)
|
||||
Services.obs.removeObserver(updateSocialProvidersCache, "social:providers-changed");
|
||||
if (socialShareButtonListenersAdded) {
|
||||
let eventName = "social:" + kShareWidgetId;
|
||||
Services.obs.removeObserver(onShareWidgetChanged, eventName + "-added");
|
||||
Services.obs.removeObserver(onShareWidgetChanged, eventName + "-removed");
|
||||
}
|
||||
}
|
||||
|
||||
function onShareWidgetChanged(aSubject, aTopic, aData) {
|
||||
sendEvent("LoopShareWidgetChanged");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of Social Providers from the Social API that are explicitly
|
||||
* capable of sharing URLs.
|
||||
* It also adds a listener that is fired whenever a new Provider is added or
|
||||
* removed.
|
||||
*
|
||||
* @return {Array} Sorted list of share-capable Social Providers.
|
||||
*/
|
||||
function updateSocialProvidersCache() {
|
||||
let providers = [];
|
||||
|
||||
for (let provider of Social.providers) {
|
||||
if (!provider.shareURL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only pass the relevant data on to content.
|
||||
providers.push({
|
||||
iconURL: provider.iconURL,
|
||||
name: provider.name,
|
||||
origin: provider.origin
|
||||
});
|
||||
}
|
||||
|
||||
let providersWasSet = !!socialProviders;
|
||||
// Replace old with new.
|
||||
socialProviders = cloneValueInto(providers.sort((a, b) =>
|
||||
a.name.toLowerCase().localeCompare(b.name.toLowerCase())), targetWindow);
|
||||
|
||||
// Start listening for changes in the social provider list, if we're not
|
||||
// doing that yet.
|
||||
if (!providersWasSet) {
|
||||
Services.obs.addObserver(updateSocialProvidersCache, "social:providers-changed", false);
|
||||
} else {
|
||||
// Dispatch an event to content to let stores freshen-up.
|
||||
sendEvent("LoopSocialProvidersChanged");
|
||||
}
|
||||
|
||||
return socialProviders;
|
||||
}
|
||||
|
||||
let contentObj = Cu.createObjectIn(targetWindow);
|
||||
Object.defineProperties(contentObj, api);
|
||||
|
@ -22,6 +22,7 @@ skip-if = buildapp == 'mulet'
|
||||
[browser_mozLoop_pluralStrings.js]
|
||||
[browser_mozLoop_sharingListeners.js]
|
||||
skip-if = e10s
|
||||
[browser_mozLoop_socialShare.js]
|
||||
[browser_mozLoop_telemetry.js]
|
||||
skip-if = e10s
|
||||
[browser_toolbarbutton.js]
|
||||
|
@ -0,0 +1,139 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This is an integration test from navigator.mozLoop through to the end
|
||||
* effects - rather than just testing MozLoopAPI alone.
|
||||
*/
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
const {SocialService} = Cu.import("resource://gre/modules/SocialService.jsm", {});
|
||||
|
||||
add_task(loadLoopPanel);
|
||||
|
||||
const kShareWidgetId = "social-share-button";
|
||||
const kShareProvider = {
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
|
||||
shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
|
||||
};
|
||||
const kShareProviderInvalid = {
|
||||
name: "provider 1",
|
||||
origin: "https://example2.com"
|
||||
};
|
||||
|
||||
add_task(function* test_mozLoop_isSocialShareButtonAvailable() {
|
||||
Assert.ok(gMozLoopAPI, "mozLoop should exist");
|
||||
|
||||
// First make sure the Social Share button is not available. This is probably
|
||||
// already the case, but make it explicit here.
|
||||
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
|
||||
|
||||
Assert.ok(!gMozLoopAPI.isSocialShareButtonAvailable(),
|
||||
"Social Share button should not be available");
|
||||
|
||||
// Add the widget to the navbar.
|
||||
CustomizableUI.addWidgetToArea(kShareWidgetId, CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
Assert.ok(gMozLoopAPI.isSocialShareButtonAvailable(),
|
||||
"Social Share button should be available");
|
||||
|
||||
// Add the widget to the MenuPanel.
|
||||
CustomizableUI.addWidgetToArea(kShareWidgetId, CustomizableUI.AREA_PANEL);
|
||||
|
||||
Assert.ok(gMozLoopAPI.isSocialShareButtonAvailable(),
|
||||
"Social Share button should still be available");
|
||||
|
||||
// Test button removal during the same session.
|
||||
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
|
||||
|
||||
Assert.ok(!gMozLoopAPI.isSocialShareButtonAvailable(),
|
||||
"Social Share button should not be available");
|
||||
});
|
||||
|
||||
add_task(function* test_mozLoop_addSocialShareButton() {
|
||||
gMozLoopAPI.addSocialShareButton();
|
||||
|
||||
Assert.ok(gMozLoopAPI.isSocialShareButtonAvailable(),
|
||||
"Social Share button should be available");
|
||||
|
||||
let widget = CustomizableUI.getWidget(kShareWidgetId);
|
||||
Assert.strictEqual(widget.areaType, CustomizableUI.TYPE_TOOLBAR,
|
||||
"Social Share button should be placed in the navbar");
|
||||
|
||||
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
|
||||
});
|
||||
|
||||
add_task(function* test_mozLoop_addSocialShareProvider() {
|
||||
gMozLoopAPI.addSocialShareButton();
|
||||
|
||||
gMozLoopAPI.addSocialShareProvider();
|
||||
|
||||
yield promiseWaitForCondition(() => SocialShare.panel.state == "open");
|
||||
|
||||
Assert.equal(SocialShare.iframe.getAttribute("src"), "about:providerdirectory",
|
||||
"Provider directory page should be visible");
|
||||
|
||||
SocialShare.panel.hidePopup();
|
||||
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
|
||||
});
|
||||
|
||||
add_task(function* test_mozLoop_getSocialShareProviders() {
|
||||
Assert.strictEqual(gMozLoopAPI.getSocialShareProviders().length, 0,
|
||||
"Provider list should be empty initially");
|
||||
|
||||
// Add a provider.
|
||||
yield new Promise(resolve => SocialService.addProvider(kShareProvider, resolve));
|
||||
|
||||
let providers = gMozLoopAPI.getSocialShareProviders();
|
||||
Assert.strictEqual(providers.length, 1,
|
||||
"The newly added provider should be part of the list");
|
||||
let provider = providers[0];
|
||||
Assert.strictEqual(provider.iconURL, kShareProvider.iconURL, "Icon URLs should match");
|
||||
Assert.strictEqual(provider.name, kShareProvider.name, "Names should match");
|
||||
Assert.strictEqual(provider.origin, kShareProvider.origin, "Origins should match");
|
||||
|
||||
// Add another provider that should not be picked up by Loop.
|
||||
yield new Promise(resolve => SocialService.addProvider(kShareProviderInvalid, resolve));
|
||||
|
||||
providers = gMozLoopAPI.getSocialShareProviders();
|
||||
Assert.strictEqual(providers.length, 1,
|
||||
"The newly added provider should not be part of the list");
|
||||
|
||||
// Let's add a valid second provider object.
|
||||
let provider2 = Object.create(kShareProvider);
|
||||
provider2.name = "Wildly different name";
|
||||
provider2.origin = "https://example3.com";
|
||||
yield new Promise(resolve => SocialService.addProvider(provider2, resolve));
|
||||
|
||||
providers = gMozLoopAPI.getSocialShareProviders();
|
||||
Assert.strictEqual(providers.length, 2,
|
||||
"The newly added provider should be part of the list");
|
||||
Assert.strictEqual(providers[1].name, provider2.name,
|
||||
"Providers should be ordered alphabetically");
|
||||
|
||||
// Remove the second valid provider.
|
||||
yield new Promise(resolve => SocialService.disableProvider(provider2.origin, resolve));
|
||||
providers = gMozLoopAPI.getSocialShareProviders();
|
||||
Assert.strictEqual(providers.length, 1,
|
||||
"The uninstalled provider should not be part of the list");
|
||||
Assert.strictEqual(providers[0].name, kShareProvider.name, "Names should match");
|
||||
});
|
||||
|
||||
add_task(function* test_mozLoop_socialShareRoom() {
|
||||
gMozLoopAPI.addSocialShareButton();
|
||||
|
||||
gMozLoopAPI.socialShareRoom(kShareProvider.origin, "https://someroom.com", "Some Title");
|
||||
|
||||
yield promiseWaitForCondition(() => SocialShare.panel.state == "open");
|
||||
|
||||
Assert.equal(SocialShare.iframe.getAttribute("origin"), kShareProvider.origin,
|
||||
"Origins should match");
|
||||
Assert.equal(SocialShare.iframe.getAttribute("src"), kShareProvider.shareURL,
|
||||
"Provider's share page should be displayed");
|
||||
|
||||
SocialShare.panel.hidePopup();
|
||||
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
|
||||
});
|
@ -71,6 +71,34 @@ function promiseGetMozLoopAPI() {
|
||||
});
|
||||
}
|
||||
|
||||
function waitForCondition(condition, nextTest, errorMsg) {
|
||||
var tries = 0;
|
||||
var interval = setInterval(function() {
|
||||
if (tries >= 30) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
var conditionPassed;
|
||||
try {
|
||||
conditionPassed = condition();
|
||||
} catch (e) {
|
||||
ok(false, e + "\n" + e.stack);
|
||||
conditionPassed = false;
|
||||
}
|
||||
if (conditionPassed) {
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
}, 100);
|
||||
var moveOn = function() { clearInterval(interval); nextTest(); };
|
||||
}
|
||||
|
||||
function promiseWaitForCondition(aConditionFn) {
|
||||
let deferred = Promise.defer();
|
||||
waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.");
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the loop panel by clicking the button and waits for its open to complete.
|
||||
* It also registers
|
||||
|
Loading…
Reference in New Issue
Block a user