summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/social
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/social')
-rw-r--r--browser/base/content/test/social/.eslintrc.js7
-rw-r--r--browser/base/content/test/social/blocklist.xml6
-rw-r--r--browser/base/content/test/social/browser.ini23
-rw-r--r--browser/base/content/test/social/browser_aboutHome_activation.js229
-rw-r--r--browser/base/content/test/social/browser_addons.js217
-rw-r--r--browser/base/content/test/social/browser_blocklist.js211
-rw-r--r--browser/base/content/test/social/browser_share.js396
-rw-r--r--browser/base/content/test/social/browser_social_activation.js270
-rw-r--r--browser/base/content/test/social/head.js273
-rw-r--r--browser/base/content/test/social/microformats.html18
-rw-r--r--browser/base/content/test/social/moz.pngbin0 -> 580 bytes
-rw-r--r--browser/base/content/test/social/opengraph/og_invalid_url.html11
-rw-r--r--browser/base/content/test/social/opengraph/opengraph.html13
-rw-r--r--browser/base/content/test/social/opengraph/shortlink_linkrel.html10
-rw-r--r--browser/base/content/test/social/opengraph/shorturl_link.html10
-rw-r--r--browser/base/content/test/social/opengraph/shorturl_linkrel.html25
-rw-r--r--browser/base/content/test/social/share.html9
-rw-r--r--browser/base/content/test/social/share_activate.html35
-rw-r--r--browser/base/content/test/social/social_activate.html41
-rw-r--r--browser/base/content/test/social/social_activate_basic.html41
-rw-r--r--browser/base/content/test/social/social_activate_iframe.html11
-rw-r--r--browser/base/content/test/social/social_crash_content_helper.js31
-rw-r--r--browser/base/content/test/social/social_postActivation.html12
23 files changed, 1899 insertions, 0 deletions
diff --git a/browser/base/content/test/social/.eslintrc.js b/browser/base/content/test/social/.eslintrc.js
new file mode 100644
index 000000000..7c8021192
--- /dev/null
+++ b/browser/base/content/test/social/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/mochitest/browser.eslintrc.js"
+ ]
+};
diff --git a/browser/base/content/test/social/blocklist.xml b/browser/base/content/test/social/blocklist.xml
new file mode 100644
index 000000000..2e3665c36
--- /dev/null
+++ b/browser/base/content/test/social/blocklist.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem blockID="s1" id="test1.example.com@services.mozilla.org"></emItem>
+ </emItems>
+</blocklist>
diff --git a/browser/base/content/test/social/browser.ini b/browser/base/content/test/social/browser.ini
new file mode 100644
index 000000000..91f931602
--- /dev/null
+++ b/browser/base/content/test/social/browser.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+support-files =
+ blocklist.xml
+ head.js
+ opengraph/og_invalid_url.html
+ opengraph/opengraph.html
+ opengraph/shortlink_linkrel.html
+ opengraph/shorturl_link.html
+ opengraph/shorturl_linkrel.html
+ microformats.html
+ share.html
+ share_activate.html
+ social_activate.html
+ social_activate_basic.html
+ social_activate_iframe.html
+ social_postActivation.html
+ !/browser/base/content/test/plugins/blockNoPlugins.xml
+
+[browser_aboutHome_activation.js]
+[browser_addons.js]
+[browser_blocklist.js]
+[browser_share.js]
+[browser_social_activation.js]
diff --git a/browser/base/content/test/social/browser_aboutHome_activation.js b/browser/base/content/test/social/browser_aboutHome_activation.js
new file mode 100644
index 000000000..37cca53d2
--- /dev/null
+++ b/browser/base/content/test/social/browser_aboutHome_activation.js
@@ -0,0 +1,229 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
+ "resource:///modules/AboutHome.jsm");
+
+var snippet =
+' <script>' +
+' var manifest = {' +
+' "name": "Demo Social Service",' +
+' "origin": "https://example.com",' +
+' "iconURL": "chrome://branding/content/icon16.png",' +
+' "icon32URL": "chrome://branding/content/icon32.png",' +
+' "icon64URL": "chrome://branding/content/icon64.png",' +
+' "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) {' +
+' node.setAttribute("data-service", JSON.stringify(manifest));' +
+' var event = new CustomEvent("ActivateSocialFeature");' +
+' node.dispatchEvent(event);' +
+' }' +
+' </script>' +
+' <div id="activationSnippet" onclick="activateProvider(this)">' +
+' <img src="chrome://branding/content/icon32.png"></img>' +
+' </div>';
+
+// enable one-click activation
+var snippet2 =
+' <script>' +
+' var manifest = {' +
+' "name": "Demo Social Service",' +
+' "origin": "https://example.com",' +
+' "iconURL": "chrome://branding/content/icon16.png",' +
+' "icon32URL": "chrome://branding/content/icon32.png",' +
+' "icon64URL": "chrome://branding/content/icon64.png",' +
+' "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' +
+' };' +
+' function activateProvider(node) {' +
+' node.setAttribute("data-service", JSON.stringify(manifest));' +
+' var event = new CustomEvent("ActivateSocialFeature");' +
+' node.dispatchEvent(event);' +
+' }' +
+' </script>' +
+' <div id="activationSnippet" onclick="activateProvider(this)">' +
+' <img src="chrome://branding/content/icon32.png"></img>' +
+' </div>';
+
+var gTests = [
+
+{
+ desc: "Test activation with enable panel",
+ snippet: snippet,
+ panel: true
+},
+
+{
+ desc: "Test activation bypassing enable panel",
+ snippet: snippet2,
+ panel: false
+}
+];
+
+function test()
+{
+ waitForExplicitFinish();
+ requestLongerTimeout(2);
+ ignoreAllUncaughtExceptions();
+ PopupNotifications.panel.setAttribute("animate", "false");
+ registerCleanupFunction(function () {
+ PopupNotifications.panel.removeAttribute("animate");
+ });
+
+ Task.spawn(function* () {
+ for (let test of gTests) {
+ info(test.desc);
+
+ // Create a tab to run the test.
+ let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+ // Add an event handler to modify the snippets map once it's ready.
+ let snippetsPromise = promiseSetupSnippetsMap(tab, test.snippet);
+
+ // Start loading about:home and wait for it to complete, snippets should be loaded
+ yield promiseTabLoadEvent(tab, "about:home", "AboutHomeLoadSnippetsCompleted");
+
+ yield snippetsPromise;
+
+ // ensure our activation snippet is indeed available
+ yield ContentTask.spawn(tab.linkedBrowser, {}, function*(arg) {
+ ok(!!content.document.getElementById("snippets"), "Found snippets element");
+ ok(!!content.document.getElementById("activationSnippet"), "The snippet is present.");
+ });
+
+ yield new Promise(resolve => {
+ activateProvider(tab, test.panel).then(() => {
+ checkSocialUI();
+ SocialService.uninstallProvider("https://example.com", function () {
+ info("provider uninstalled");
+ resolve();
+ });
+ });
+ });
+
+ // activation opened a post-activation info tab, close it.
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ yield BrowserTestUtils.removeTab(tab);
+ }
+ }).then(finish, ex => {
+ ok(false, "Unexpected Exception: " + ex);
+ finish();
+ });
+}
+
+/**
+ * Starts a load in an existing tab and waits for it to finish (via some event).
+ *
+ * @param aTab
+ * The tab to load into.
+ * @param aUrl
+ * The url to load.
+ * @param aEvent
+ * The load event type to wait for. Defaults to "load".
+ * @return {Promise} resolved when the event is handled.
+ */
+function promiseTabLoadEvent(aTab, aURL, aEventType="load")
+{
+ return new Promise(resolve => {
+ info("Wait tab event: " + aEventType);
+ aTab.linkedBrowser.addEventListener(aEventType, function load(event) {
+ if (event.originalTarget != aTab.linkedBrowser.contentDocument ||
+ event.target.location.href == "about:blank") {
+ info("skipping spurious load event");
+ return;
+ }
+ aTab.linkedBrowser.removeEventListener(aEventType, load, true);
+ info("Tab event received: " + aEventType);
+ resolve();
+ }, true, true);
+ aTab.linkedBrowser.loadURI(aURL);
+ });
+}
+
+/**
+ * Cleans up snippets and ensures that by default we don't try to check for
+ * remote snippets since that may cause network bustage or slowness.
+ *
+ * @param aTab
+ * The tab containing about:home.
+ * @param aSetupFn
+ * The setup function to be run.
+ * @return {Promise} resolved when the snippets are ready. Gets the snippets map.
+ */
+function promiseSetupSnippetsMap(aTab, aSnippet)
+{
+ info("Waiting for snippets map");
+
+ return ContentTask.spawn(aTab.linkedBrowser,
+ {snippetsVersion: AboutHomeUtils.snippetsVersion,
+ snippet: aSnippet},
+ function*(arg) {
+ return new Promise(resolve => {
+ addEventListener("AboutHomeLoadSnippets", function load(event) {
+ removeEventListener("AboutHomeLoadSnippets", load, true);
+
+ let cw = content.window.wrappedJSObject;
+
+ // The snippets should already be ready by this point. Here we're
+ // just obtaining a reference to the snippets map.
+ cw.ensureSnippetsMapThen(function (aSnippetsMap) {
+ aSnippetsMap = Cu.waiveXrays(aSnippetsMap);
+ console.log("Got snippets map: " +
+ "{ last-update: " + aSnippetsMap.get("snippets-last-update") +
+ ", cached-version: " + aSnippetsMap.get("snippets-cached-version") +
+ " }");
+ // Don't try to update.
+ aSnippetsMap.set("snippets-last-update", Date.now());
+ aSnippetsMap.set("snippets-cached-version", arg.snippetsVersion);
+ // Clear snippets.
+ aSnippetsMap.delete("snippets");
+ aSnippetsMap.set("snippets", arg.snippet);
+ resolve();
+ });
+ }, true, true);
+ });
+ });
+}
+
+
+function sendActivationEvent(tab) {
+ // hack Social.lastEventReceived so we don't hit the "too many events" check.
+ Social.lastEventReceived = 0;
+ let doc = tab.linkedBrowser.contentDocument;
+ // if our test has a frame, use it
+ if (doc.defaultView.frames[0])
+ doc = doc.defaultView.frames[0].document;
+ let button = doc.getElementById("activationSnippet");
+ BrowserTestUtils.synthesizeMouseAtCenter(button, {}, tab.linkedBrowser);
+}
+
+function activateProvider(tab, expectPanel, aCallback) {
+ return new Promise(resolve => {
+ if (expectPanel) {
+ BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
+ let panel = document.getElementById("servicesInstall-notification");
+ panel.button.click();
+ });
+ }
+ waitForProviderLoad().then(() => {
+ checkSocialUI();
+ resolve();
+ });
+ sendActivationEvent(tab);
+ });
+}
+
+function waitForProviderLoad(cb) {
+ return Promise.all([
+ promiseObserverNotified("social:provider-enabled"),
+ ensureFrameLoaded(gBrowser, "https://example.com/browser/browser/base/content/test/social/social_postActivation.html"),
+ ]);
+}
diff --git a/browser/base/content/test/social/browser_addons.js b/browser/base/content/test/social/browser_addons.js
new file mode 100644
index 000000000..5a75d1d67
--- /dev/null
+++ b/browser/base/content/test/social/browser_addons.js
@@ -0,0 +1,217 @@
+var AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
+var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+
+var manifest = {
+ name: "provider 1",
+ origin: "https://example.com",
+ 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",
+ 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",
+ 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"
+};
+
+function test() {
+ waitForExplicitFinish();
+ PopupNotifications.panel.setAttribute("animate", "false");
+ registerCleanupFunction(function () {
+ PopupNotifications.panel.removeAttribute("animate");
+ });
+
+ let prefname = getManifestPrefname(manifest);
+ // ensure that manifest2 is NOT showing as builtin
+ is(SocialService.getOriginActivationType(manifest.origin), "foreign", "manifest is foreign");
+ is(SocialService.getOriginActivationType(manifest2.origin), "foreign", "manifest2 is foreign");
+
+ Services.prefs.setBoolPref("social.remote-install.enabled", true);
+ runSocialTests(tests, undefined, undefined, function () {
+ Services.prefs.clearUserPref("social.remote-install.enabled");
+ ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
+ // just in case the tests failed, clear these here as well
+ Services.prefs.clearUserPref("social.directories");
+ finish();
+ });
+}
+
+function installListener(next, aManifest) {
+ let expectEvent = "onInstalling";
+ let prefname = getManifestPrefname(aManifest);
+ // wait for the actual removal to call next
+ SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
+ if (topic == "provider-disabled") {
+ SocialService.unregisterProviderListener(providerListener);
+ is(origin, aManifest.origin, "provider disabled");
+ executeSoon(next);
+ }
+ });
+
+ return {
+ onInstalling: function(addon) {
+ is(expectEvent, "onInstalling", "install started");
+ is(addon.manifest.origin, aManifest.origin, "provider about to be installed");
+ ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
+ expectEvent = "onInstalled";
+ },
+ onInstalled: function(addon) {
+ is(addon.manifest.origin, aManifest.origin, "provider installed");
+ ok(addon.installDate.getTime() > 0, "addon has installDate");
+ ok(addon.updateDate.getTime() > 0, "addon has updateDate");
+ ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
+ expectEvent = "onUninstalling";
+ },
+ onUninstalling: function(addon) {
+ is(expectEvent, "onUninstalling", "uninstall started");
+ is(addon.manifest.origin, aManifest.origin, "provider about to be uninstalled");
+ ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
+ expectEvent = "onUninstalled";
+ },
+ onUninstalled: function(addon) {
+ is(expectEvent, "onUninstalled", "provider has been uninstalled");
+ is(addon.manifest.origin, aManifest.origin, "provider uninstalled");
+ ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
+ AddonManager.removeAddonListener(this);
+ }
+ };
+}
+
+var tests = {
+ testHTTPInstallFailure: function(next) {
+ let installFrom = "http://example.com";
+ is(SocialService.getOriginActivationType(installFrom), "foreign", "testing foriegn install");
+ let data = {
+ origin: installFrom,
+ url: installFrom+"/activate",
+ manifest: manifest,
+ window: window
+ }
+ Social.installProvider(data, function(addonManifest) {
+ ok(!addonManifest, "unable to install provider over http");
+ next();
+ });
+ },
+ testAddonEnableToggle: function(next) {
+ let expectEvent;
+ let prefname = getManifestPrefname(manifest);
+ let listener = {
+ onEnabled: function(addon) {
+ is(expectEvent, "onEnabled", "provider onEnabled");
+ ok(!addon.userDisabled, "provider enabled");
+ executeSoon(function() {
+ expectEvent = "onDisabling";
+ addon.userDisabled = true;
+ });
+ },
+ onEnabling: function(addon) {
+ is(expectEvent, "onEnabling", "provider onEnabling");
+ expectEvent = "onEnabled";
+ },
+ onDisabled: function(addon) {
+ is(expectEvent, "onDisabled", "provider onDisabled");
+ ok(addon.userDisabled, "provider disabled");
+ AddonManager.removeAddonListener(listener);
+ // clear the provider user-level pref
+ Services.prefs.clearUserPref(prefname);
+ executeSoon(next);
+ },
+ onDisabling: function(addon) {
+ is(expectEvent, "onDisabling", "provider onDisabling");
+ expectEvent = "onDisabled";
+ }
+ };
+ AddonManager.addAddonListener(listener);
+
+ // we're only testing enable disable, so we quickly set the user-level pref
+ // for this provider and test enable/disable toggling
+ setManifestPref(prefname, manifest);
+ ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
+ AddonManager.getAddonsByTypes(["service"], function(addons) {
+ for (let addon of addons) {
+ if (addon.userDisabled) {
+ expectEvent = "onEnabling";
+ addon.userDisabled = false;
+ // only test with one addon
+ return;
+ }
+ }
+ ok(false, "no addons toggled");
+ next();
+ });
+ },
+ testProviderEnableToggle: function(next) {
+ // enable and disabel a provider from the SocialService interface, check
+ // that the addon manager is updated
+
+ let expectEvent;
+ let prefname = getManifestPrefname(manifest);
+
+ let listener = {
+ onEnabled: function(addon) {
+ is(expectEvent, "onEnabled", "provider onEnabled");
+ is(addon.manifest.origin, manifest.origin, "provider enabled");
+ ok(!addon.userDisabled, "provider !userDisabled");
+ },
+ onEnabling: function(addon) {
+ is(expectEvent, "onEnabling", "provider onEnabling");
+ is(addon.manifest.origin, manifest.origin, "provider about to be enabled");
+ expectEvent = "onEnabled";
+ },
+ onDisabled: function(addon) {
+ is(expectEvent, "onDisabled", "provider onDisabled");
+ is(addon.manifest.origin, manifest.origin, "provider disabled");
+ ok(addon.userDisabled, "provider userDisabled");
+ },
+ onDisabling: function(addon) {
+ is(expectEvent, "onDisabling", "provider onDisabling");
+ is(addon.manifest.origin, manifest.origin, "provider about to be disabled");
+ expectEvent = "onDisabled";
+ }
+ };
+ AddonManager.addAddonListener(listener);
+
+ expectEvent = "onEnabling";
+ setManifestPref(prefname, manifest);
+ SocialService.enableProvider(manifest.origin, function(provider) {
+ expectEvent = "onDisabling";
+ SocialService.disableProvider(provider.origin, function() {
+ AddonManager.removeAddonListener(listener);
+ Services.prefs.clearUserPref(prefname);
+ next();
+ });
+ });
+ },
+ testDirectoryInstall: function(next) {
+ AddonManager.addAddonListener(installListener(next, manifest2));
+
+ BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
+ let panel = document.getElementById("servicesInstall-notification");
+ info("servicesInstall-notification panel opened");
+ panel.button.click();
+ });
+
+ Services.prefs.setCharPref("social.directories", manifest2.origin);
+ is(SocialService.getOriginActivationType(manifest2.origin), "directory", "testing directory install");
+ let data = {
+ origin: manifest2.origin,
+ url: manifest2.origin + "/directory",
+ manifest: manifest2,
+ window: window
+ }
+ Social.installProvider(data, function(addonManifest) {
+ Services.prefs.clearUserPref("social.directories");
+ SocialService.enableProvider(addonManifest.origin, function(provider) {
+ Social.uninstallProvider(addonManifest.origin);
+ });
+ });
+ }
+}
diff --git a/browser/base/content/test/social/browser_blocklist.js b/browser/base/content/test/social/browser_blocklist.js
new file mode 100644
index 000000000..b67d5efb3
--- /dev/null
+++ b/browser/base/content/test/social/browser_blocklist.js
@@ -0,0 +1,211 @@
+/* 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/. */
+
+// a place for miscellaneous social tests
+
+var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+var blocklistURL = "http://example.com/browser/browser/base/content/test/social/blocklist.xml";
+
+var manifest = { // normal provider
+ name: "provider ok",
+ origin: "https://example.com",
+ 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",
+ 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"
+};
+
+// blocklist testing
+function updateBlocklist() {
+ var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsITimerCallback);
+ let promise = promiseObserverNotified("blocklist-updated");
+ blocklistNotifier.notify(null);
+ return promise;
+}
+
+var _originalTestBlocklistURL = null;
+function setAndUpdateBlocklist(aURL) {
+ if (!_originalTestBlocklistURL)
+ _originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
+ Services.prefs.setCharPref("extensions.blocklist.url", aURL);
+ return updateBlocklist();
+}
+
+function resetBlocklist() {
+ // XXX - this has "forked" from the head.js helpers in our parent directory :(
+ // But let's reuse their blockNoPlugins.xml. Later, we should arrange to
+ // use their head.js helpers directly
+ let noBlockedURL = "http://example.com/browser/browser/base/content/test/plugins/blockNoPlugins.xml";
+ return new Promise(resolve => {
+ setAndUpdateBlocklist(noBlockedURL).then(() => {
+ Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL);
+ resolve();
+ });
+ });
+}
+
+function test() {
+ waitForExplicitFinish();
+ // turn on logging for nsBlocklistService.js
+ Services.prefs.setBoolPref("extensions.logging.enabled", true);
+ registerCleanupFunction(function () {
+ Services.prefs.clearUserPref("extensions.logging.enabled");
+ });
+
+ runSocialTests(tests, undefined, undefined, function () {
+ resetBlocklist().then(finish); // restore to original pref
+ });
+}
+
+var tests = {
+ testSimpleBlocklist: function(next) {
+ // this really just tests adding and clearing our blocklist for later tests
+ setAndUpdateBlocklist(blocklistURL).then(() => {
+ ok(Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest_bad)), "blocking 'blocked'");
+ ok(!Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest)), "not blocking 'good'");
+ resetBlocklist().then(() => {
+ ok(!Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest_bad)), "blocklist cleared");
+ next();
+ });
+ });
+ },
+ testAddingNonBlockedProvider: function(next) {
+ function finishTest(isgood) {
+ ok(isgood, "adding non-blocked provider ok");
+ Services.prefs.clearUserPref("social.manifest.good");
+ resetBlocklist().then(next);
+ }
+ setManifestPref("social.manifest.good", manifest);
+ setAndUpdateBlocklist(blocklistURL).then(() => {
+ try {
+ SocialService.addProvider(manifest, function(provider) {
+ try {
+ SocialService.disableProvider(provider.origin, function() {
+ ok(true, "added and removed provider");
+ finishTest(true);
+ });
+ } catch (e) {
+ ok(false, "SocialService.disableProvider threw exception: " + e);
+ finishTest(false);
+ }
+ });
+ } catch (e) {
+ ok(false, "SocialService.addProvider threw exception: " + e);
+ finishTest(false);
+ }
+ });
+ },
+ testAddingBlockedProvider: function(next) {
+ function finishTest(good) {
+ ok(good, "Unable to add blocklisted provider");
+ Services.prefs.clearUserPref("social.manifest.blocked");
+ resetBlocklist().then(next);
+ }
+ setManifestPref("social.manifest.blocked", manifest_bad);
+ setAndUpdateBlocklist(blocklistURL).then(() => {
+ try {
+ SocialService.addProvider(manifest_bad, function(provider) {
+ SocialService.disableProvider(provider.origin, function() {
+ ok(false, "SocialService.addProvider should throw blocklist exception");
+ finishTest(false);
+ });
+ });
+ } catch (e) {
+ ok(true, "SocialService.addProvider should throw blocklist exception: " + e);
+ finishTest(true);
+ }
+ });
+ },
+ testInstallingBlockedProvider: function(next) {
+ function finishTest(good) {
+ ok(good, "Unable to install blocklisted provider");
+ resetBlocklist().then(next);
+ }
+ let activationURL = manifest_bad.origin + "/browser/browser/base/content/test/social/social_activate.html"
+ setAndUpdateBlocklist(blocklistURL).then(() => {
+ try {
+ // expecting an exception when attempting to install a hard blocked
+ // provider
+ let data = {
+ origin: manifest_bad.origin,
+ url: activationURL,
+ manifest: manifest_bad,
+ window: window
+ }
+ Social.installProvider(data, function(addonManifest) {
+ finishTest(false);
+ });
+ } catch (e) {
+ finishTest(true);
+ }
+ });
+ },
+ testBlockingExistingProvider: function(next) {
+ let listener = {
+ _window: null,
+ onOpenWindow: function(aXULWindow) {
+ Services.wm.removeListener(this);
+ this._window = aXULWindow;
+ let domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+
+ domwindow.addEventListener("load", function _load() {
+ domwindow.removeEventListener("load", _load, false);
+
+ domwindow.addEventListener("unload", function _unload() {
+ domwindow.removeEventListener("unload", _unload, false);
+ info("blocklist window was closed");
+ Services.wm.removeListener(listener);
+ next();
+ }, false);
+
+ is(domwindow.document.location.href, URI_EXTENSION_BLOCKLIST_DIALOG, "dialog opened and focused");
+ // wait until after load to cancel so the dialog has initalized. we
+ // don't want to accept here since that restarts the browser.
+ executeSoon(() => {
+ let cancelButton = domwindow.document.documentElement.getButton("cancel");
+ info("***** hit the cancel button\n");
+ cancelButton.doCommand();
+ });
+ }, false);
+ },
+ onCloseWindow: function(aXULWindow) { },
+ onWindowTitleChange: function(aXULWindow, aNewTitle) { }
+ };
+
+ Services.wm.addListener(listener);
+
+ setManifestPref("social.manifest.blocked", manifest_bad);
+ try {
+ SocialService.addProvider(manifest_bad, function(provider) {
+ // the act of blocking should cause a 'provider-disabled' notification
+ // from SocialService.
+ SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
+ if (topic != "provider-disabled")
+ return;
+ SocialService.unregisterProviderListener(providerListener);
+ is(origin, provider.origin, "provider disabled");
+ SocialService.getProvider(provider.origin, function(p) {
+ ok(p == null, "blocklisted provider disabled");
+ Services.prefs.clearUserPref("social.manifest.blocked");
+ resetBlocklist();
+ });
+ });
+ // no callback - the act of updating should cause the listener above
+ // to fire.
+ setAndUpdateBlocklist(blocklistURL);
+ });
+ } catch (e) {
+ ok(false, "unable to add provider " + e);
+ next();
+ }
+ }
+}
diff --git a/browser/base/content/test/social/browser_share.js b/browser/base/content/test/social/browser_share.js
new file mode 100644
index 000000000..19dca519b
--- /dev/null
+++ b/browser/base/content/test/social/browser_share.js
@@ -0,0 +1,396 @@
+
+var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+
+var baseURL = "https://example.com/browser/browser/base/content/test/social/";
+
+var manifest = { // normal provider
+ name: "provider 1",
+ origin: "https://example.com",
+ iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
+ shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
+};
+var activationPage = "https://example.com/browser/browser/base/content/test/social/share_activate.html";
+
+function sendActivationEvent(subframe) {
+ // hack Social.lastEventReceived so we don't hit the "too many events" check.
+ Social.lastEventReceived = 0;
+ let doc = subframe.contentDocument;
+ // if our test has a frame, use it
+ let button = doc.getElementById("activation");
+ ok(!!button, "got the activation button");
+ EventUtils.synthesizeMouseAtCenter(button, {}, doc.defaultView);
+}
+
+function test() {
+ waitForExplicitFinish();
+ Services.prefs.setCharPref("social.shareDirectory", activationPage);
+
+ let frameScript = "data:,(" + function frame_script() {
+ addEventListener("OpenGraphData", function (aEvent) {
+ sendAsyncMessage("sharedata", aEvent.detail);
+ }, true, true);
+ /* bug 1042991, ensure history is available by calling history.back on close */
+ addMessageListener("closeself", function(e) {
+ content.history.back();
+ content.close();
+ }, true);
+ /* if text is entered into field, onbeforeunload will cause a modal dialog
+ unless dialogs have been disabled for the iframe. */
+ content.onbeforeunload = function(e) {
+ return 'FAIL.';
+ };
+ }.toString() + ")();";
+ let mm = getGroupMessageManager("social");
+ mm.loadFrameScript(frameScript, true);
+
+ // Animation on the panel can cause intermittent failures such as bug 1115131.
+ SocialShare.panel.setAttribute("animate", "false");
+ registerCleanupFunction(function () {
+ SocialShare.panel.removeAttribute("animate");
+ mm.removeDelayedFrameScript(frameScript);
+ Services.prefs.clearUserPref("social.directories");
+ Services.prefs.clearUserPref("social.shareDirectory");
+ Services.prefs.clearUserPref("social.share.activationPanelEnabled");
+ });
+ runSocialTests(tests, undefined, function(next) {
+ let shareButton = SocialShare.shareButton;
+ if (shareButton) {
+ CustomizableUI.removeWidgetFromArea("social-share-button", CustomizableUI.AREA_NAVBAR)
+ shareButton.remove();
+ }
+ next();
+ });
+}
+
+var corpus = [
+ {
+ url: baseURL+"opengraph/opengraph.html",
+ options: {
+ // og:title
+ title: ">This is my title<",
+ // og:description
+ description: "A test corpus file for open graph tags we care about",
+ // medium: this.getPageMedium(),
+ // source: this.getSourceURL(),
+ // og:url
+ url: "https://www.mozilla.org/",
+ // shortUrl: this.getShortURL(),
+ // og:image
+ previews:["https://www.mozilla.org/favicon.png"],
+ // og:site_name
+ siteName: ">My simple test page<"
+ }
+ },
+ {
+ // tests that og:url doesn't override the page url if it is bad
+ url: baseURL+"opengraph/og_invalid_url.html",
+ options: {
+ description: "A test corpus file for open graph tags passing a bad url",
+ url: baseURL+"opengraph/og_invalid_url.html",
+ previews: [],
+ siteName: "Evil chrome delivering website"
+ }
+ },
+ {
+ url: baseURL+"opengraph/shorturl_link.html",
+ options: {
+ previews: ["http://example.com/1234/56789.jpg"],
+ url: "http://www.example.com/photos/56789/",
+ shortUrl: "http://imshort/p/abcde"
+ }
+ },
+ {
+ url: baseURL+"opengraph/shorturl_linkrel.html",
+ options: {
+ previews: ["http://example.com/1234/56789.jpg"],
+ url: "http://www.example.com/photos/56789/",
+ shortUrl: "http://imshort/p/abcde"
+ }
+ },
+ {
+ url: baseURL+"opengraph/shortlink_linkrel.html",
+ options: {
+ previews: ["http://example.com/1234/56789.jpg"],
+ url: "http://www.example.com/photos/56789/",
+ shortUrl: "http://imshort/p/abcde"
+ }
+ }
+];
+
+function hasoptions(testOptions, options) {
+ for (let option in testOptions) {
+ let data = testOptions[option];
+ info("data: "+JSON.stringify(data));
+ let message_data = options[option];
+ info("message_data: "+JSON.stringify(message_data));
+ if (Array.isArray(data)) {
+ // the message may have more array elements than we are testing for, this
+ // is ok since some of those are hard to test. So we just test that
+ // anything in our test data IS in the message.
+ ok(Array.every(data, function(item) { return message_data.indexOf(item) >= 0 }), "option "+option);
+ } else {
+ is(message_data, data, "option "+option);
+ }
+ }
+}
+
+var tests = {
+ testShareDisabledOnActivation: function(next) {
+ // starting on about:blank page, share should be visible but disabled when
+ // adding provider
+ is(gBrowser.currentURI.spec, "about:blank");
+
+ // initialize the button into the navbar
+ CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+ // ensure correct state
+ SocialUI.onCustomizeEnd(window);
+
+ SocialService.addProvider(manifest, function(provider) {
+ is(SocialUI.enabled, true, "SocialUI is enabled");
+ checkSocialUI();
+ // share should not be enabled since we only have about:blank page
+ let shareButton = SocialShare.shareButton;
+ // verify the attribute for proper css
+ is(shareButton.getAttribute("disabled"), "true", "share button attribute is disabled");
+ // button should be visible
+ is(shareButton.hidden, false, "share button is visible");
+ SocialService.disableProvider(manifest.origin, next);
+ });
+ },
+ testShareEnabledOnActivation: function(next) {
+ // starting from *some* page, share should be visible and enabled when
+ // activating provider
+ // initialize the button into the navbar
+ CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+ // ensure correct state
+ SocialUI.onCustomizeEnd(window);
+
+ let testData = corpus[0];
+ BrowserTestUtils.openNewForegroundTab(gBrowser, testData.url).then(tab => {
+ SocialService.addProvider(manifest, function(provider) {
+ is(SocialUI.enabled, true, "SocialUI is enabled");
+ checkSocialUI();
+ // share should not be enabled since we only have about:blank page
+ let shareButton = SocialShare.shareButton;
+ // verify the attribute for proper css
+ ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
+ // button should be visible
+ is(shareButton.hidden, false, "share button is visible");
+ BrowserTestUtils.removeTab(tab).then(next);
+ });
+ });
+ },
+ testSharePage: function(next) {
+ let testTab;
+ let testIndex = 0;
+ let testData = corpus[testIndex++];
+
+ // initialize the button into the navbar
+ CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+ // ensure correct state
+ SocialUI.onCustomizeEnd(window);
+
+ let mm = getGroupMessageManager("social");
+ mm.addMessageListener("sharedata", function handler(msg) {
+ BrowserTestUtils.removeTab(testTab).then(() => {
+ hasoptions(testData.options, JSON.parse(msg.data));
+ testData = corpus[testIndex++];
+ BrowserTestUtils.waitForCondition(() => { return SocialShare.currentShare == null; }, "share panel closed").then(() => {
+ if (testData) {
+ runOneTest();
+ } else {
+ mm.removeMessageListener("sharedata", handler);
+ SocialService.disableProvider(manifest.origin, next);
+ }
+ });
+ SocialShare.iframe.messageManager.sendAsyncMessage("closeself", {});
+ });
+ });
+
+ function runOneTest() {
+ BrowserTestUtils.openNewForegroundTab(gBrowser, testData.url).then(tab => {
+ testTab = tab;
+
+ let shareButton = SocialShare.shareButton;
+ // verify the attribute for proper css
+ ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
+ // button should be visible
+ is(shareButton.hidden, false, "share button is visible");
+
+ SocialShare.sharePage(manifest.origin);
+ });
+ }
+ executeSoon(runOneTest);
+ },
+ testShareMicroformats: function(next) {
+ // initialize the button into the navbar
+ CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+ // ensure correct state
+ SocialUI.onCustomizeEnd(window);
+
+ SocialService.addProvider(manifest, function(provider) {
+ let target, testTab;
+
+ let expecting = JSON.stringify({
+ "url": "https://example.com/browser/browser/base/content/test/social/microformats.html",
+ "title": "Raspberry Pi Page",
+ "previews": ["https://example.com/someimage.jpg"],
+ "microformats": {
+ "items": [{
+ "type": ["h-product"],
+ "properties": {
+ "name": ["Raspberry Pi"],
+ "photo": ["https://example.com/someimage.jpg"],
+ "description": [{
+ "value": "The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It's a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.",
+ "html": "The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It's a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming."
+ }
+ ],
+ "url": ["https://example.com/"],
+ "price": ["29.95"],
+ "review": [{
+ "value": "4.5 out of 5",
+ "type": ["h-review"],
+ "properties": {
+ "rating": ["4.5"]
+ }
+ }
+ ],
+ "category": ["Computer", "Education"]
+ }
+ }
+ ],
+ "rels": {
+ "tag": ["https://example.com/wiki/computer", "https://example.com/wiki/education"]
+ },
+ "rel-urls": {
+ "https://example.com/wiki/computer": {
+ "text": "Computer",
+ "rels": ["tag"]
+ },
+ "https://example.com/wiki/education": {
+ "text": "Education",
+ "rels": ["tag"]
+ }
+ }
+ }
+ });
+
+ let mm = getGroupMessageManager("social");
+ mm.addMessageListener("sharedata", function handler(msg) {
+ is(msg.data, expecting, "microformats data ok");
+ BrowserTestUtils.waitForCondition(() => { return SocialShare.currentShare == null; },
+ "share panel closed").then(() => {
+ mm.removeMessageListener("sharedata", handler);
+ BrowserTestUtils.removeTab(testTab).then(() => {
+ SocialService.disableProvider(manifest.origin, next);
+ });
+ });
+ SocialShare.iframe.messageManager.sendAsyncMessage("closeself", {});
+ });
+
+ let url = "https://example.com/browser/browser/base/content/test/social/microformats.html"
+ BrowserTestUtils.openNewForegroundTab(gBrowser, url).then(tab => {
+ testTab = tab;
+
+ let shareButton = SocialShare.shareButton;
+ // verify the attribute for proper css
+ ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
+ // button should be visible
+ is(shareButton.hidden, false, "share button is visible");
+
+ let doc = tab.linkedBrowser.contentDocument;
+ target = doc.getElementById("simple-hcard");
+ SocialShare.sharePage(manifest.origin, null, target);
+ });
+ });
+ },
+ testSharePanelActivation: function(next) {
+ let testTab;
+ // cleared in the cleanup function
+ Services.prefs.setCharPref("social.directories", "https://example.com");
+ Services.prefs.setBoolPref("social.share.activationPanelEnabled", true);
+ // make the iframe so we can wait on the load
+ SocialShare._createFrame();
+ let iframe = SocialShare.iframe;
+
+ // initialize the button into the navbar
+ CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+ // ensure correct state
+ SocialUI.onCustomizeEnd(window);
+
+ ensureFrameLoaded(iframe).then(() => {
+ let subframe = iframe.contentDocument.getElementById("activation-frame");
+ ensureFrameLoaded(subframe, activationPage).then(() => {
+ is(subframe.contentDocument.location.href, activationPage, "activation page loaded");
+ promiseObserverNotified("social:provider-enabled").then(() => {
+ let mm = getGroupMessageManager("social");
+ mm.addMessageListener("sharedata", function handler(msg) {
+ ok(true, "share completed");
+
+ BrowserTestUtils.waitForCondition(() => { return SocialShare.currentShare == null; },
+ "share panel closed").then(() => {
+ BrowserTestUtils.removeTab(testTab).then(() => {
+ mm.removeMessageListener("sharedata", handler);
+ SocialService.uninstallProvider(manifest.origin, next);
+ });
+ });
+ SocialShare.iframe.messageManager.sendAsyncMessage("closeself", {});
+ });
+ });
+ sendActivationEvent(subframe);
+ });
+ });
+ BrowserTestUtils.openNewForegroundTab(gBrowser, activationPage).then(tab => {
+ let shareButton = SocialShare.shareButton;
+ // verify the attribute for proper css
+ ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
+ // button should be visible
+ is(shareButton.hidden, false, "share button is visible");
+
+ testTab = tab;
+ SocialShare.sharePage();
+ });
+ },
+ testSharePanelDialog: function(next) {
+ let testTab;
+ // initialize the button into the navbar
+ CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+ // ensure correct state
+ SocialUI.onCustomizeEnd(window);
+ SocialShare._createFrame();
+
+ SocialService.addProvider(manifest, () => {
+ BrowserTestUtils.openNewForegroundTab(gBrowser, activationPage).then(tab => {
+ ensureFrameLoaded(SocialShare.iframe).then(() => {
+ // send keys to the input field. An unexpected failure will happen
+ // if the onbeforeunload handler is fired.
+ EventUtils.sendKey("f");
+ EventUtils.sendKey("a");
+ EventUtils.sendKey("i");
+ EventUtils.sendKey("l");
+
+ SocialShare.panel.addEventListener("popuphidden", function hidden(evt) {
+ SocialShare.panel.removeEventListener("popuphidden", hidden);
+ let topwin = Services.wm.getMostRecentWindow(null);
+ is(topwin, window, "no dialog is open");
+
+ BrowserTestUtils.removeTab(testTab).then(() => {
+ SocialService.disableProvider(manifest.origin, next);
+ });
+ });
+ SocialShare.iframe.messageManager.sendAsyncMessage("closeself", {});
+ });
+
+ let shareButton = SocialShare.shareButton;
+ // verify the attribute for proper css
+ ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
+ // button should be visible
+ is(shareButton.hidden, false, "share button is visible");
+
+ testTab = tab;
+ SocialShare.sharePage();
+ });
+ });
+ }
+}
diff --git a/browser/base/content/test/social/browser_social_activation.js b/browser/base/content/test/social/browser_social_activation.js
new file mode 100644
index 000000000..2af0d8021
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -0,0 +1,270 @@
+/* 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/. */
+
+//
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: Assert is null");
+
+
+var SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+
+var tabsToRemove = [];
+
+function removeProvider(provider) {
+ return new Promise(resolve => {
+ // a full install sets the manifest into a pref, addProvider alone doesn't,
+ // make sure we uninstall if the manifest was added.
+ if (provider.manifest) {
+ SocialService.uninstallProvider(provider.origin, resolve);
+ } else {
+ SocialService.disableProvider(provider.origin, resolve);
+ }
+ });
+}
+
+function postTestCleanup(callback) {
+ Task.spawn(function* () {
+ // any tabs opened by the test.
+ for (let tab of tabsToRemove) {
+ yield BrowserTestUtils.removeTab(tab);
+ }
+ tabsToRemove = [];
+ // all the providers may have been added.
+ while (Social.providers.length > 0) {
+ yield removeProvider(Social.providers[0]);
+ }
+ }).then(callback);
+}
+
+function newTab(url) {
+ return new Promise(resolve => {
+ BrowserTestUtils.openNewForegroundTab(gBrowser, url).then(tab => {
+ tabsToRemove.push(tab);
+ resolve(tab);
+ });
+ });
+}
+
+function sendActivationEvent(tab, callback, nullManifest) {
+ // hack Social.lastEventReceived so we don't hit the "too many events" check.
+ Social.lastEventReceived = 0;
+ BrowserTestUtils.synthesizeMouseAtCenter("#activation", {}, tab.linkedBrowser);
+ executeSoon(callback);
+}
+
+function activateProvider(domain, callback, nullManifest) {
+ let activationURL = domain+"/browser/browser/base/content/test/social/social_activate_basic.html"
+ newTab(activationURL).then(tab => {
+ sendActivationEvent(tab, callback, nullManifest);
+ });
+}
+
+function activateIFrameProvider(domain, callback) {
+ let activationURL = domain+"/browser/browser/base/content/test/social/social_activate_iframe.html"
+ newTab(activationURL).then(tab => {
+ sendActivationEvent(tab, callback, false);
+ });
+}
+
+function waitForProviderLoad(origin) {
+ return Promise.all([
+ ensureFrameLoaded(gBrowser, origin + "/browser/browser/base/content/test/social/social_postActivation.html"),
+ ]);
+}
+
+function getAddonItemInList(aId, aList) {
+ var item = aList.firstChild;
+ while (item) {
+ if ("mAddon" in item && item.mAddon.id == aId) {
+ aList.ensureElementIsVisible(item);
+ return item;
+ }
+ item = item.nextSibling;
+ }
+ return null;
+}
+
+function clickAddonRemoveButton(tab, aCallback) {
+ AddonManager.getAddonsByTypes(["service"], function(aAddons) {
+ let addon = aAddons[0];
+
+ let doc = tab.linkedBrowser.contentDocument;
+ let list = doc.getElementById("addon-list");
+
+ let item = getAddonItemInList(addon.id, list);
+ let button = item._removeBtn;
+ isnot(button, null, "Should have a remove button");
+ ok(!button.disabled, "Button should not be disabled");
+
+ // uninstall happens after about:addons tab is closed, so we wait on
+ // disabled
+ promiseObserverNotified("social:provider-disabled").then(() => {
+ is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+ executeSoon(function() { aCallback(addon); });
+ });
+
+ BrowserTestUtils.synthesizeMouseAtCenter(button, {}, tab.linkedBrowser);
+ });
+}
+
+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");
+ if (finishActivation)
+ panel.button.click();
+ else
+ panel.closebutton.click();
+ });
+ BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden").then(() => {
+ ok(panel.hidden, "servicesInstall-notification panel hidden");
+ if (!finishActivation) {
+ ok(panel.hidden, "activation panel is not showing");
+ executeSoon(aCallback);
+ } else {
+ waitForProviderLoad(manifest.origin).then(() => {
+ checkSocialUI();
+ executeSoon(aCallback);
+ });
+ }
+ });
+
+ // the test will continue as the popup events fire...
+ activateProvider(manifest.origin, function() {
+ info("waiting on activation panel to open/close...");
+ });
+}
+
+var gTestDomains = ["https://example.com", "https://test1.example.com", "https://test2.example.com"];
+var gProviders = [
+ {
+ name: "provider 1",
+ origin: "https://example.com",
+ 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",
+ 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",
+ shareURL: "https://test2.example.com/browser/browser/base/content/test/social/social_share.html?provider2",
+ iconURL: "chrome://branding/content/about-logo.png"
+ }
+];
+
+
+function test() {
+ PopupNotifications.panel.setAttribute("animate", "false");
+ registerCleanupFunction(function () {
+ PopupNotifications.panel.removeAttribute("animate");
+ });
+ waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, () => {
+ runSocialTests(tests, undefined, postTestCleanup);
+ });
+}
+
+var tests = {
+ testActivationWrongOrigin: function(next) {
+ // At this stage none of our providers exist, so we expect failure.
+ Services.prefs.setBoolPref("social.remote-install.enabled", false);
+ activateProvider(gTestDomains[0], function() {
+ is(SocialUI.enabled, false, "SocialUI is not enabled");
+ let panel = document.getElementById("servicesInstall-notification");
+ ok(panel.hidden, "activation panel still hidden");
+ checkSocialUI();
+ Services.prefs.clearUserPref("social.remote-install.enabled");
+ next();
+ });
+ },
+
+ testIFrameActivation: function(next) {
+ activateIFrameProvider(gTestDomains[0], function() {
+ is(SocialUI.enabled, false, "SocialUI is not enabled");
+ let panel = document.getElementById("servicesInstall-notification");
+ ok(panel.hidden, "activation panel still hidden");
+ checkSocialUI();
+ next();
+ });
+ },
+
+ testActivationFirstProvider: function(next) {
+ // 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.
+ checkSocialUI();
+ next();
+ });
+ },
+
+ testActivationMultipleProvider: function(next) {
+ // The trick with this test is to make sure that Social.providers[1] is
+ // the current provider when doing the undo - this makes sure that the
+ // Social code doesn't fallback to Social.providers[0], which it will
+ // do in some cases (but those cases do not include what this test does)
+ // first enable the 2 providers
+ SocialService.addProvider(gProviders[0], function() {
+ SocialService.addProvider(gProviders[1], function() {
+ checkSocialUI();
+ // activate the last provider.
+ activateOneProvider(gProviders[2], false, function() {
+ // we deactivated - the first provider should be enabled.
+ checkSocialUI();
+ next();
+ });
+ });
+ });
+ },
+
+ testAddonManagerDoubleInstall: function(next) {
+ // Create a new tab and load about:addons
+ let addonsTab = gBrowser.addTab();
+ gBrowser.selectedTab = addonsTab;
+ BrowserOpenAddonsMgr('addons://list/service');
+ gBrowser.selectedBrowser.addEventListener("load", function tabLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", tabLoad, true);
+ is(addonsTab.linkedBrowser.currentURI.spec, "about:addons", "about:addons should load into blank tab.");
+
+ activateOneProvider(gProviders[0], true, function() {
+ info("first activation completed");
+ is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html", "postActivationURL loaded");
+ BrowserTestUtils.removeTab(gBrowser.selectedTab).then(() => {
+ is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_activate_basic.html", "activation page selected");
+ BrowserTestUtils.removeTab(gBrowser.selectedTab).then(() => {
+ tabsToRemove.pop();
+ // uninstall the provider
+ clickAddonRemoveButton(addonsTab, function(addon) {
+ checkSocialUI();
+ activateOneProvider(gProviders[0], true, function() {
+ info("second activation completed");
+ is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html", "postActivationURL loaded");
+ BrowserTestUtils.removeTab(gBrowser.selectedTab).then(() => {
+
+ // after closing the addons tab, verify provider is still installed
+ AddonManager.getAddonsByTypes(["service"], function(aAddons) {
+ is(aAddons.length, 1, "there can be only one");
+
+ let doc = addonsTab.linkedBrowser.contentDocument;
+ let list = doc.getElementById("addon-list");
+ is(list.childNodes.length, 1, "only one addon is displayed");
+
+ BrowserTestUtils.removeTab(addonsTab).then(next);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ }, true);
+ }
+}
diff --git a/browser/base/content/test/social/head.js b/browser/base/content/test/social/head.js
new file mode 100644
index 000000000..ea175c97a
--- /dev/null
+++ b/browser/base/content/test/social/head.js
@@ -0,0 +1,273 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+
+
+function promiseObserverNotified(aTopic) {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
+ dump("notification promised "+aTopic);
+ Services.obs.removeObserver(onNotification, aTopic);
+ TestUtils.executeSoon(() => resolve({subject: aSubject, data: aData}));
+ }, aTopic, false);
+ });
+}
+
+// Check that a specified (string) URL hasn't been "remembered" (ie, is not
+// in history, will not appear in about:newtab or auto-complete, etc.)
+function promiseSocialUrlNotRemembered(url) {
+ return new Promise(resolve => {
+ let uri = Services.io.newURI(url, null, null);
+ PlacesUtils.asyncHistory.isURIVisited(uri, function(aURI, aIsVisited) {
+ ok(!aIsVisited, "social URL " + url + " should not be in global history");
+ resolve();
+ });
+ });
+}
+
+var gURLsNotRemembered = [];
+
+
+function checkProviderPrefsEmpty(isError) {
+ let MANIFEST_PREFS = Services.prefs.getBranch("social.manifest.");
+ let prefs = MANIFEST_PREFS.getChildList("", []);
+ let c = 0;
+ for (let pref of prefs) {
+ if (MANIFEST_PREFS.prefHasUserValue(pref)) {
+ info("provider [" + pref + "] manifest left installed from previous test");
+ c++;
+ }
+ }
+ is(c, 0, "all provider prefs uninstalled from previous test");
+ is(Social.providers.length, 0, "all providers uninstalled from previous test " + Social.providers.length);
+}
+
+function defaultFinishChecks() {
+ checkProviderPrefsEmpty(true);
+ finish();
+}
+
+function runSocialTestWithProvider(manifest, callback, finishcallback) {
+
+ let SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+
+ let manifests = Array.isArray(manifest) ? manifest : [manifest];
+
+ // Check that none of the provider's content ends up in history.
+ function* finishCleanUp() {
+ for (let i = 0; i < manifests.length; i++) {
+ let m = manifests[i];
+ for (let what of ['iconURL', 'shareURL']) {
+ if (m[what]) {
+ yield promiseSocialUrlNotRemembered(m[what]);
+ }
+ }
+ }
+ for (let i = 0; i < gURLsNotRemembered.length; i++) {
+ yield promiseSocialUrlNotRemembered(gURLsNotRemembered[i]);
+ }
+ gURLsNotRemembered = [];
+ }
+
+ info("runSocialTestWithProvider: " + manifests.toSource());
+
+ let finishCount = 0;
+ function finishIfDone(callFinish) {
+ finishCount++;
+ if (finishCount == manifests.length)
+ Task.spawn(finishCleanUp).then(finishcallback || defaultFinishChecks);
+ }
+ function removeAddedProviders(cleanup) {
+ manifests.forEach(function (m) {
+ // If we're "cleaning up", don't call finish when done.
+ let callback = cleanup ? function () {} : finishIfDone;
+ // Similarly, if we're cleaning up, catch exceptions from removeProvider
+ let removeProvider = SocialService.disableProvider.bind(SocialService);
+ if (cleanup) {
+ removeProvider = function (origin, cb) {
+ try {
+ SocialService.disableProvider(origin, cb);
+ } catch (ex) {
+ // Ignore "provider doesn't exist" errors.
+ if (ex.message.indexOf("SocialService.disableProvider: no provider with origin") == 0)
+ return;
+ info("Failed to clean up provider " + origin + ": " + ex);
+ }
+ }
+ }
+ removeProvider(m.origin, callback);
+ });
+ }
+ function finishSocialTest(cleanup) {
+ removeAddedProviders(cleanup);
+ }
+
+ let providersAdded = 0;
+
+ manifests.forEach(function (m) {
+ SocialService.addProvider(m, function(provider) {
+
+ providersAdded++;
+ info("runSocialTestWithProvider: provider added");
+
+ // we want to set the first specified provider as the UI's provider
+ if (provider.origin == manifests[0].origin) {
+ firstProvider = provider;
+ }
+
+ // If we've added all the providers we need, call the callback to start
+ // the tests (and give it a callback it can call to finish them)
+ if (providersAdded == manifests.length) {
+ registerCleanupFunction(function () {
+ finishSocialTest(true);
+ });
+ BrowserTestUtils.waitForCondition(() => provider.enabled,
+ "providers added and enabled").then(() => {
+ info("provider has been enabled");
+ callback(finishSocialTest);
+ });
+ }
+ });
+ });
+}
+
+function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
+ let testIter = (function*() {
+ for (let name in tests) {
+ if (tests.hasOwnProperty(name)) {
+ yield [name, tests[name]];
+ }
+ }
+ })();
+ let providersAtStart = Social.providers.length;
+ info("runSocialTests: start test run with " + providersAtStart + " providers");
+ window.focus();
+
+
+ if (cbPreTest === undefined) {
+ cbPreTest = function(cb) { cb() };
+ }
+ if (cbPostTest === undefined) {
+ cbPostTest = function(cb) { cb() };
+ }
+
+ function runNextTest() {
+ let result = testIter.next();
+ if (result.done) {
+ // out of items:
+ (cbFinish || defaultFinishChecks)();
+ is(providersAtStart, Social.providers.length,
+ "runSocialTests: finish test run with " + Social.providers.length + " providers");
+ return;
+ }
+ let [name, func] = result.value;
+ // We run on a timeout to help keep the debug messages sane.
+ executeSoon(function() {
+ function cleanupAndRunNextTest() {
+ info("sub-test " + name + " complete");
+ cbPostTest(runNextTest);
+ }
+ cbPreTest(function() {
+ info("pre-test: starting with " + Social.providers.length + " providers");
+ info("sub-test " + name + " starting");
+ try {
+ func.call(tests, cleanupAndRunNextTest);
+ } catch (ex) {
+ ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
+ cleanupAndRunNextTest();
+ }
+ })
+ });
+ }
+ runNextTest();
+}
+
+// A fairly large hammer which checks all aspects of the SocialUI for
+// internal consistency.
+function checkSocialUI(win) {
+ let SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
+ // if we have enabled providers, we should also have instances of those
+ // providers
+ if (SocialService.hasEnabledProviders) {
+ ok(Social.providers.length > 0, "providers are enabled");
+ } else {
+ is(Social.providers.length, 0, "providers are not enabled");
+ }
+}
+
+function setManifestPref(name, manifest) {
+ let string = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ string.data = JSON.stringify(manifest);
+ Services.prefs.setComplexValue(name, Ci.nsISupportsString, string);
+}
+
+function getManifestPrefname(aManifest) {
+ // is same as the generated name in SocialServiceInternal.getManifestPrefname
+ let originUri = Services.io.newURI(aManifest.origin, null, null);
+ return "social.manifest." + originUri.hostPort.replace('.', '-');
+}
+
+function ensureFrameLoaded(frame, uri) {
+ return new Promise(resolve => {
+ if (frame.contentDocument && frame.contentDocument.readyState == "complete" &&
+ (!uri || frame.contentDocument.location.href == uri)) {
+ resolve();
+ } else {
+ frame.addEventListener("load", function handler() {
+ if (uri && frame.contentDocument.location.href != uri)
+ return;
+ frame.removeEventListener("load", handler, true);
+ resolve()
+ }, true);
+ }
+ });
+}
+
+// Support for going on and offline.
+// (via browser/base/content/test/browser_bookmark_titles.js)
+var origProxyType = Services.prefs.getIntPref('network.proxy.type');
+
+function toggleOfflineStatus(goOffline) {
+ // Bug 968887 fix. when going on/offline, wait for notification before continuing
+ return new Promise(resolve => {
+ if (!goOffline) {
+ Services.prefs.setIntPref('network.proxy.type', origProxyType);
+ }
+ if (goOffline != Services.io.offline) {
+ info("initial offline state " + Services.io.offline);
+ let expect = !Services.io.offline;
+ Services.obs.addObserver(function offlineChange(subject, topic, data) {
+ Services.obs.removeObserver(offlineChange, "network:offline-status-changed");
+ info("offline state changed to " + Services.io.offline);
+ is(expect, Services.io.offline, "network:offline-status-changed successful toggle");
+ resolve();
+ }, "network:offline-status-changed", false);
+ BrowserOffline.toggleOfflineStatus();
+ } else {
+ resolve();
+ }
+ if (goOffline) {
+ Services.prefs.setIntPref('network.proxy.type', 0);
+ // LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
+ Services.cache2.clear();
+ }
+ });
+}
+
+function goOffline() {
+ // Simulate a network outage with offline mode. (Localhost is still
+ // accessible in offline mode, so disable the test proxy as well.)
+ return toggleOfflineStatus(true);
+}
+
+function goOnline(callback) {
+ return toggleOfflineStatus(false);
+}
diff --git a/browser/base/content/test/social/microformats.html b/browser/base/content/test/social/microformats.html
new file mode 100644
index 000000000..1a0e4436b
--- /dev/null
+++ b/browser/base/content/test/social/microformats.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <head><title>Raspberry Pi Page</title></head>
+ <div class="hproduct">
+ <h2 class="fn">Raspberry Pi</h2>
+ <img class="photo" src="https://example.com/someimage.jpg" />
+ <p class="description">The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It's a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.</p>
+ <a class="url" href="https://example.com/">More info about the Raspberry Pi</a>
+ <p class="price">29.95</p>
+ <p class="review hreview"><span id="test-review" class="rating">4.5</span> out of 5</p>
+ <p>Categories:
+ <a rel="tag" href="https://example.com/wiki/computer" class="category">Computer</a>,
+ <a rel="tag" href="https://example.com/wiki/education" class="category">Education</a>
+ </p>
+ </div>
+ </body>
+</html>
diff --git a/browser/base/content/test/social/moz.png b/browser/base/content/test/social/moz.png
new file mode 100644
index 000000000..769c63634
--- /dev/null
+++ b/browser/base/content/test/social/moz.png
Binary files differ
diff --git a/browser/base/content/test/social/opengraph/og_invalid_url.html b/browser/base/content/test/social/opengraph/og_invalid_url.html
new file mode 100644
index 000000000..ad1dae2be
--- /dev/null
+++ b/browser/base/content/test/social/opengraph/og_invalid_url.html
@@ -0,0 +1,11 @@
+<html xmlns:og="http://ogp.me/ns#">
+<head>
+ <meta property="og:url" content="chrome://browser/content/aboutDialog.xul"/>
+ <meta property="og:site_name" content="Evil chrome delivering website"/>
+ <meta property="og:description"
+ content="A test corpus file for open graph tags passing a bad url"/>
+</head>
+<body>
+ Open Graph Test Page
+</body>
+</html>
diff --git a/browser/base/content/test/social/opengraph/opengraph.html b/browser/base/content/test/social/opengraph/opengraph.html
new file mode 100644
index 000000000..50b7703b8
--- /dev/null
+++ b/browser/base/content/test/social/opengraph/opengraph.html
@@ -0,0 +1,13 @@
+<html xmlns:og="http://ogp.me/ns#">
+<head>
+ <meta property="og:title" content="&gt;This is my title&lt;"/>
+ <meta property="og:url" content="https://www.mozilla.org"/>
+ <meta property="og:image" content="https://www.mozilla.org/favicon.png"/>
+ <meta property="og:site_name" content="&#62;My simple test page&#60;"/>
+ <meta property="og:description"
+ content="A test corpus file for open graph tags we care about"/>
+</head>
+<body>
+ Open Graph Test Page
+</body>
+</html>
diff --git a/browser/base/content/test/social/opengraph/shortlink_linkrel.html b/browser/base/content/test/social/opengraph/shortlink_linkrel.html
new file mode 100644
index 000000000..54c40c376
--- /dev/null
+++ b/browser/base/content/test/social/opengraph/shortlink_linkrel.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <link rel="image_src" href="http://example.com/1234/56789.jpg" id="image-src" />
+ <link id="canonicalurl" rel="canonical" href="http://www.example.com/photos/56789/" />
+ <link rel="shortlink" href="http://imshort/p/abcde" />
+</head>
+<body>
+ link[rel='shortlink']
+</body>
+</html>
diff --git a/browser/base/content/test/social/opengraph/shorturl_link.html b/browser/base/content/test/social/opengraph/shorturl_link.html
new file mode 100644
index 000000000..667122cea
--- /dev/null
+++ b/browser/base/content/test/social/opengraph/shorturl_link.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <link rel="image_src" href="http://example.com/1234/56789.jpg" id="image-src" />
+ <link id="canonicalurl" rel="canonical" href="http://www.example.com/photos/56789/" />
+ <link id="shorturl" rev="canonical" type="text/html" href="http://imshort/p/abcde" />
+</head>
+<body>
+ link id="shorturl"
+</body>
+</html>
diff --git a/browser/base/content/test/social/opengraph/shorturl_linkrel.html b/browser/base/content/test/social/opengraph/shorturl_linkrel.html
new file mode 100644
index 000000000..36533528e
--- /dev/null
+++ b/browser/base/content/test/social/opengraph/shorturl_linkrel.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+ <title>Test Image</title>
+
+ <meta name="description" content="Iron man in a tutu" />
+ <meta name="title" content="Test Image" />
+
+ <meta name="medium" content="image" />
+ <link rel="image_src" href="http://example.com/1234/56789.jpg" id="image-src" />
+ <link id="canonicalurl" rel="canonical" href="http://www.example.com/photos/56789/" />
+ <link id="shorturl" href="http://imshort/p/abcde" />
+
+ <meta property="og:title" content="TestImage" />
+ <meta property="og:type" content="photos:photo" />
+ <meta property="og:url" content="http://www.example.com/photos/56789/" />
+ <meta property="og:site_name" content="My Photo Site" />
+ <meta property="og:description" content="Iron man in a tutu" />
+ <meta property="og:image" content="http://example.com/1234/56789.jpg" />
+ <meta property="og:image:width" content="480" />
+ <meta property="og:image:height" content="640" />
+</head>
+<body>
+ link[rel='shorturl']
+</body>
+</html>
diff --git a/browser/base/content/test/social/share.html b/browser/base/content/test/social/share.html
new file mode 100644
index 000000000..55cba9844
--- /dev/null
+++ b/browser/base/content/test/social/share.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body onload="document.getElementById('testclose').focus()">
+ <p>This is a test social share window.</p>
+ <input id="testclose"/>
+ </body>
+</html>
diff --git a/browser/base/content/test/social/share_activate.html b/browser/base/content/test/social/share_activate.html
new file mode 100644
index 000000000..69707e705
--- /dev/null
+++ b/browser/base/content/test/social/share_activate.html
@@ -0,0 +1,35 @@
+<html>
+<!-- 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/. -->
+<head>
+ <title>Activation test</title>
+</head>
+<script>
+
+var data = {
+ // currently required
+ "name": "Demo Social Service",
+ // browser_share.js serves this page from "https://example.com"
+ "origin": "https://example.com",
+ "iconURL": "chrome://branding/content/icon16.png",
+ "icon32URL": "chrome://branding/content/favicon32.png",
+ "icon64URL": "chrome://branding/content/icon64.png",
+ "shareURL": "/browser/browser/base/content/test/social/share.html"
+}
+
+function activate(node) {
+ node.setAttribute("data-service", JSON.stringify(data));
+ var event = new CustomEvent("ActivateSocialFeature");
+ node.dispatchEvent(event);
+}
+
+</script>
+<body>
+
+nothing to see here
+
+<button id="activation" onclick="activate(this, true)">Activate the share provider</button>
+
+</body>
+</html>
diff --git a/browser/base/content/test/social/social_activate.html b/browser/base/content/test/social/social_activate.html
new file mode 100644
index 000000000..78da597a1
--- /dev/null
+++ b/browser/base/content/test/social/social_activate.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Activation test</title>
+</head>
+<script>
+// icons from http://findicons.com/icon/158311/firefox?id=356182 by ipapun
+var data = {
+ // currently required
+ "name": "Demo Social Service",
+ "iconURL": "chrome://branding/content/icon16.png",
+ "icon32URL": "chrome://branding/content/favicon32.png",
+ "icon64URL": "chrome://branding/content/icon64.png",
+
+ // at least one of these must be defined
+ "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
+ "description": "A short paragraph about this provider",
+ "author": "Shane Caraveo, Mozilla",
+
+ // optional
+ "version": "1.0"
+}
+
+function activate(node) {
+ node.setAttribute("data-service", JSON.stringify(data));
+ var event = new CustomEvent("ActivateSocialFeature");
+ node.dispatchEvent(event);
+}
+
+</script>
+<body>
+
+nothing to see here
+
+<button id="activation" onclick="activate(this)">Activate The Demo Provider</button>
+
+</body>
+</html>
diff --git a/browser/base/content/test/social/social_activate_basic.html b/browser/base/content/test/social/social_activate_basic.html
new file mode 100644
index 000000000..78da597a1
--- /dev/null
+++ b/browser/base/content/test/social/social_activate_basic.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Activation test</title>
+</head>
+<script>
+// icons from http://findicons.com/icon/158311/firefox?id=356182 by ipapun
+var data = {
+ // currently required
+ "name": "Demo Social Service",
+ "iconURL": "chrome://branding/content/icon16.png",
+ "icon32URL": "chrome://branding/content/favicon32.png",
+ "icon64URL": "chrome://branding/content/icon64.png",
+
+ // at least one of these must be defined
+ "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
+ "description": "A short paragraph about this provider",
+ "author": "Shane Caraveo, Mozilla",
+
+ // optional
+ "version": "1.0"
+}
+
+function activate(node) {
+ node.setAttribute("data-service", JSON.stringify(data));
+ var event = new CustomEvent("ActivateSocialFeature");
+ node.dispatchEvent(event);
+}
+
+</script>
+<body>
+
+nothing to see here
+
+<button id="activation" onclick="activate(this)">Activate The Demo Provider</button>
+
+</body>
+</html>
diff --git a/browser/base/content/test/social/social_activate_iframe.html b/browser/base/content/test/social/social_activate_iframe.html
new file mode 100644
index 000000000..bde884c9d
--- /dev/null
+++ b/browser/base/content/test/social/social_activate_iframe.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <title>Activation iframe test</title>
+</head>
+
+<body>
+
+<iframe src="social_activate_basic.html"/>
+
+</body>
+</html>
diff --git a/browser/base/content/test/social/social_crash_content_helper.js b/browser/base/content/test/social/social_crash_content_helper.js
new file mode 100644
index 000000000..4698b6957
--- /dev/null
+++ b/browser/base/content/test/social/social_crash_content_helper.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+* http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var Cu = Components.utils;
+
+// Ideally we would use CrashTestUtils.jsm, but that's only available for
+// xpcshell tests - so we just copy a ctypes crasher from it.
+Cu.import("resource://gre/modules/ctypes.jsm");
+var crash = function() { // this will crash when called.
+ let zero = new ctypes.intptr_t(8);
+ let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+ badptr.contents
+};
+
+
+var TestHelper = {
+ init: function() {
+ addMessageListener("social-test:crash", this);
+ },
+
+ receiveMessage: function(msg) {
+ switch (msg.name) {
+ case "social-test:crash":
+ privateNoteIntentionalCrash();
+ crash();
+ break;
+ }
+ },
+}
+
+TestHelper.init();
diff --git a/browser/base/content/test/social/social_postActivation.html b/browser/base/content/test/social/social_postActivation.html
new file mode 100644
index 000000000..e0a6acfdf
--- /dev/null
+++ b/browser/base/content/test/social/social_postActivation.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Post-Activation test</title>
+</head>
+
+<body>
+
+Post Activation landing page
+
+</body>
+</html>