Revert "Remove the Social API"

This commit is contained in:
Moonchild 2018-05-25 20:45:48 +00:00 committed by Roy Tam
commit aa52f6bd53
52 changed files with 2895 additions and 16 deletions

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&social.directory.label;</title>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/aboutProviderDirectory.css"/>
</head>
<body>
<div id="activation-link" hidden="true">
<div id="message-box">
<p>&social.directory.text;</p>
</div>
<div id="button-box">
<button onclick="openDirectory()">&social.directory.button;</button>
</div>
</div>
<div id="activation" hidden="true">
<p>&social.directory.introText;</p>
<div><iframe id="activation-frame"/></div>
<p><a class="link" onclick="openDirectory()">&social.directory.viewmore.text;</a></p>
</div>
</body>
<script type="text/javascript;version=1.8"><![CDATA[
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
function openDirectory() {
let url = Services.prefs.getCharPref("social.directories").split(',')[0];
window.open(url);
window.close();
}
if (Services.prefs.getBoolPref("social.share.activationPanelEnabled")) {
let url = Services.prefs.getCharPref("social.shareDirectory");
document.getElementById("activation-frame").setAttribute("src", url);
document.getElementById("activation").removeAttribute("hidden");
} else {
document.getElementById("activation-link").removeAttribute("hidden");
}
]]></script>
</html>

View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
%netErrorDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&loadError.label;</title>
<link rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/aboutSocialError.css"/>
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
</head>
<body>
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<p id="errorShortDescText" >foo</p>
</div>
<div id="button-box">
<button id="btnTryAgain" onclick="tryAgainButton()"/>
</div>
</div>
</body>
<script type="text/javascript;version=1.8"><![CDATA[
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/Social.jsm");
let config = {
tryAgainCallback: reloadProvider
}
function parseQueryString() {
let searchParams = new URLSearchParams(document.documentURI.split("?")[1]);
let mode = searchParams.get("mode");
config.origin = searchParams.get("origin");
let encodedURL = searchParams.get("url");
let url = decodeURIComponent(encodedURL);
// directory does not have origin set, in that case use the url origin for
// the error message.
if (!config.origin) {
let URI = Services.io.newURI(url, null, null);
config.origin =
Services.scriptSecurityManager.createCodebasePrincipal(URI, {}).origin;
}
switch (mode) {
case "compactInfo":
document.getElementById("btnTryAgain").style.display = 'none';
break;
case "tryAgainOnly":
//intentional fall-through
case "tryAgain":
config.tryAgainCallback = loadQueryURL;
config.queryURL = url;
break;
default:
break;
}
}
function setUpStrings() {
let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
let productName = brandBundle.GetStringFromName("brandShortName");
let provider = Social._getProviderFromOrigin(config.origin);
let providerName = provider ? provider.name : config.origin;
// Sets up the error message
let msg = browserBundle.formatStringFromName("social.error.message", [productName, providerName], 2);
document.getElementById("errorShortDescText").textContent = msg;
// Sets up the buttons' labels and accesskeys
let btnTryAgain = document.getElementById("btnTryAgain");
btnTryAgain.textContent = browserBundle.GetStringFromName("social.error.tryAgain.label");
btnTryAgain.accessKey = browserBundle.GetStringFromName("social.error.tryAgain.accesskey");
}
function tryAgainButton() {
config.tryAgainCallback();
}
function loadQueryURL() {
window.location.href = config.queryURL;
}
function reloadProvider() {
let provider = Social._getProviderFromOrigin(config.origin);
provider.reload();
}
parseQueryString();
setUpStrings();
]]></script>
</html>

View file

@ -85,6 +85,10 @@
label="&bookmarkThisLinkCmd.label;"
accesskey="&bookmarkThisLinkCmd.accesskey;"
oncommand="gContextMenu.bookmarkLink();"/>
<menuitem id="context-sharelink"
label="&shareLink.label;"
accesskey="&shareLink.accesskey;"
oncommand="gContextMenu.shareLink();"/>
<menuitem id="context-savelink"
label="&saveLinkCmd.label;"
accesskey="&saveLinkCmd.accesskey;"
@ -208,6 +212,10 @@
label="&saveImageCmd.label;"
accesskey="&saveImageCmd.accesskey;"
oncommand="gContextMenu.saveMedia();"/>
<menuitem id="context-shareimage"
label="&shareImage.label;"
accesskey="&shareImage.accesskey;"
oncommand="gContextMenu.shareImage();"/>
<menuitem id="context-sendimage"
label="&emailImageCmd.label;"
accesskey="&emailImageCmd.accesskey;"
@ -229,6 +237,10 @@
label="&saveVideoCmd.label;"
accesskey="&saveVideoCmd.accesskey;"
oncommand="gContextMenu.saveMedia();"/>
<menuitem id="context-sharevideo"
label="&shareVideo.label;"
accesskey="&shareVideo.accesskey;"
oncommand="gContextMenu.shareVideo();"/>
<menuitem id="context-saveaudio"
label="&saveAudioCmd.label;"
accesskey="&saveAudioCmd.accesskey;"
@ -259,6 +271,10 @@
accesskey="&hidePluginCmd.accesskey;"
oncommand="gContextMenu.hidePlugin();"/>
<menuseparator id="context-sep-ctp"/>
<menuitem id="context-sharepage"
label="&sharePageCmd.label;"
accesskey="&sharePageCmd.accesskey;"
oncommand="SocialShare.sharePage();"/>
<menuitem id="context-savepage"
label="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey2;"
@ -318,6 +334,10 @@
<menupopup id="context-sendlinktodevice-popup"
onpopupshowing="gFxAccounts.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
</menu>
<menuitem id="context-shareselect"
label="&shareSelect.label;"
accesskey="&shareSelect.accesskey;"
oncommand="gContextMenu.shareSelect();"/>
<menuseparator id="frame-sep"/>
<menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
<menupopup>

View file

@ -105,6 +105,8 @@
oncommand="OpenBrowserWindow({private: true});" reserved="true"/>
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
<command id="Social:SharePage" oncommand="SocialShare.sharePage();"/>
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
</commandset>
<commandset id="placesCommands">
@ -115,6 +117,7 @@
</commandset>
<broadcasterset id="mainBroadcasterSet">
<broadcaster id="Social:PageShareable" disabled="true"/>
<broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
oncommand="SidebarUI.toggle('viewBookmarksSidebar');"/>

View file

@ -0,0 +1,503 @@
/* 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/. */
// the "exported" symbols
var SocialUI,
SocialShare,
SocialActivationListener;
(function() {
XPCOMUtils.defineLazyGetter(this, "OpenGraphBuilder", function() {
let tmp = {};
Cu.import("resource:///modules/Social.jsm", tmp);
return tmp.OpenGraphBuilder;
});
XPCOMUtils.defineLazyGetter(this, "DynamicResizeWatcher", function() {
let tmp = {};
Cu.import("resource:///modules/Social.jsm", tmp);
return tmp.DynamicResizeWatcher;
});
SocialUI = {
_initialized: false,
// Called on delayed startup to initialize the UI
init: function SocialUI_init() {
if (this._initialized) {
return;
}
let mm = window.getGroupMessageManager("social");
mm.loadFrameScript("chrome://browser/content/content.js", true);
mm.loadFrameScript("chrome://browser/content/social-content.js", true);
Services.obs.addObserver(this, "social:providers-changed", false);
CustomizableUI.addListener(this);
SocialActivationListener.init();
Social.init().then((update) => {
if (update)
this._providersChanged();
});
this._initialized = true;
},
// Called on window unload
uninit: function SocialUI_uninit() {
if (!this._initialized) {
return;
}
Services.obs.removeObserver(this, "social:providers-changed");
CustomizableUI.removeListener(this);
SocialActivationListener.uninit();
this._initialized = false;
},
observe: function SocialUI_observe(subject, topic, data) {
switch (topic) {
case "social:providers-changed":
this._providersChanged();
break;
}
},
_providersChanged: function() {
SocialShare.populateProviderMenu();
},
showLearnMore: function() {
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "social-api";
openUILinkIn(url, "tab");
},
closeSocialPanelForLinkTraversal: function (target, linkNode) {
// No need to close the panel if this traversal was not retargeted
if (target == "" || target == "_self")
return;
// Check to see whether this link traversal was in a social panel
let win = linkNode.ownerGlobal;
let container = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
let containerParent = container.parentNode;
if (containerParent.classList.contains("social-panel") &&
containerParent instanceof Ci.nsIDOMXULPopupElement) {
// allow the link traversal to finish before closing the panel
setTimeout(() => {
containerParent.hidePopup();
}, 0);
}
},
get _chromeless() {
// Is this a popup window that doesn't want chrome shown?
let docElem = 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");
// This property is "fixed" for a window, so avoid doing the check above
// multiple times...
delete this._chromeless;
this._chromeless = chromeless;
return chromeless;
},
get enabled() {
// Returns whether social is enabled *for this window*.
if (this._chromeless)
return false;
return Social.providers.length > 0;
},
canSharePage: function(aURI) {
return (aURI && (aURI.schemeIs('http') || aURI.schemeIs('https')));
},
onCustomizeEnd: function(aWindow) {
if (aWindow != window)
return;
// customization mode gets buttons out of sync with command updating, fix
// the disabled state
let canShare = this.canSharePage(gBrowser.currentURI);
let shareButton = SocialShare.shareButton;
if (shareButton) {
if (canShare) {
shareButton.removeAttribute("disabled")
} else {
shareButton.setAttribute("disabled", "true")
}
}
},
// called on tab/urlbar/location changes and after customization. Update
// anything that is tab specific.
updateState: function() {
goSetCommandEnabled("Social:PageShareable", this.canSharePage(gBrowser.currentURI));
}
}
// message manager handlers
SocialActivationListener = {
init: function() {
messageManager.addMessageListener("Social:Activation", this);
},
uninit: function() {
messageManager.removeMessageListener("Social:Activation", this);
},
receiveMessage: function(aMessage) {
let data = aMessage.json;
let browser = aMessage.target;
data.window = window;
// if the source if the message is the share panel, we do a one-click
// installation. The source of activations is controlled by the
// social.directories preference
let options;
if (browser == SocialShare.iframe && Services.prefs.getBoolPref("social.share.activationPanelEnabled")) {
options = { bypassContentCheck: true, bypassInstallPanel: true };
}
Social.installProvider(data, function(manifest) {
Social.activateFromOrigin(manifest.origin, function(provider) {
if (provider.shareURL) {
// Ensure that the share button is somewhere usable.
// SocialShare.shareButton may return null if it is in the menu-panel
// and has never been visible, so we check the widget directly. If
// there is no area for the widget we move it into the toolbar.
let widget = CustomizableUI.getWidget("social-share-button");
// If the panel is already open, we can be sure that the provider can
// already be accessed, possibly anchored to another toolbar button.
// In that case we don't move the widget.
if (!widget.areaType && SocialShare.panel.state != "open") {
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
// Ensure correct state.
SocialUI.onCustomizeEnd(window);
}
// make this new provider the selected provider. If the panel hasn't
// been opened, we need to make the frame first.
SocialShare._createFrame();
SocialShare.iframe.setAttribute('src', 'data:text/plain;charset=utf8,');
SocialShare.iframe.setAttribute('origin', provider.origin);
// get the right button selected
SocialShare.populateProviderMenu();
if (SocialShare.panel.state == "open") {
SocialShare.sharePage(provider.origin);
}
}
if (provider.postActivationURL) {
// if activated from an open share panel, we load the landing page in
// a background tab
gBrowser.loadOneTab(provider.postActivationURL, {inBackground: SocialShare.panel.state == "open"});
}
});
}, options);
}
}
SocialShare = {
get _dynamicResizer() {
delete this._dynamicResizer;
this._dynamicResizer = new DynamicResizeWatcher();
return this._dynamicResizer;
},
// Share panel may be attached to the overflow or menu button depending on
// customization, we need to manage open state of the anchor.
get anchor() {
let widget = CustomizableUI.getWidget("social-share-button");
return widget.forWindow(window).anchor;
},
// Holds the anchor node in use whilst the panel is open, because it may vary.
_currentAnchor: null,
get panel() {
return document.getElementById("social-share-panel");
},
get iframe() {
// panel.firstChild is our toolbar hbox, panel.lastChild is the iframe
// container hbox used for an interstitial "loading" graphic
return this.panel.lastChild.firstChild;
},
uninit: function () {
if (this.iframe) {
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();
}
},
_createFrame: function() {
let panel = this.panel;
if (this.iframe)
return;
this.panel.hidden = false;
// create and initialize the panel for this window
let iframe = document.createElement("browser");
iframe.setAttribute("type", "content");
iframe.setAttribute("class", "social-share-frame");
iframe.setAttribute("context", "contentAreaContextMenu");
iframe.setAttribute("tooltip", "aHTMLTooltip");
iframe.setAttribute("disableglobalhistory", "true");
iframe.setAttribute("flex", "1");
iframe.setAttribute("message", "true");
iframe.setAttribute("messagemanagergroup", "social");
panel.lastChild.appendChild(iframe);
let mm = this.messageManager;
mm.addMessageListener("PageVisibility:Show", this);
mm.addMessageListener("PageVisibility:Hide", this);
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();
},
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;
},
receiveMessage: function(aMessage) {
let iframe = this.iframe;
switch(aMessage.name) {
case "PageVisibility:Show":
SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
break;
case "PageVisibility:Hide":
SocialShare._dynamicResizer.stop();
break;
case "Social:DOMWindowClose":
this.panel.hidePopup();
break;
}
},
handleEvent: function(event) {
switch (event.type) {
case "load": {
this.iframe.parentNode.removeAttribute("loading");
if (this.currentShare)
SocialShare.messageManager.sendAsyncMessage("Social:OpenGraphData", this.currentShare);
}
}
},
getSelectedProvider: function() {
let provider;
let lastProviderOrigin = this.iframe && this.iframe.getAttribute("origin");
if (lastProviderOrigin) {
provider = Social._getProviderFromOrigin(lastProviderOrigin);
}
return provider;
},
createTooltip: function(event) {
let tt = event.target;
let provider = Social._getProviderFromOrigin(tt.triggerNode.getAttribute("origin"));
tt.firstChild.setAttribute("value", provider.name);
tt.lastChild.setAttribute("value", provider.origin);
},
populateProviderMenu: function() {
if (!this.iframe)
return;
let providers = Social.providers.filter(p => p.shareURL);
let hbox = document.getElementById("social-share-provider-buttons");
// remove everything before the add-share-provider button (which should also
// be lastChild if any share providers were added)
let addButton = document.getElementById("add-share-provider");
while (hbox.lastChild != addButton) {
hbox.removeChild(hbox.lastChild);
}
let selectedProvider = this.getSelectedProvider();
for (let provider of providers) {
let button = document.createElement("toolbarbutton");
button.setAttribute("class", "toolbarbutton-1 share-provider-button");
button.setAttribute("type", "radio");
button.setAttribute("group", "share-providers");
button.setAttribute("image", provider.iconURL);
button.setAttribute("tooltip", "share-button-tooltip");
button.setAttribute("origin", provider.origin);
button.setAttribute("label", provider.name);
button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin'));");
if (provider == selectedProvider) {
this.defaultButton = button;
}
hbox.appendChild(button);
}
if (!this.defaultButton) {
this.defaultButton = addButton;
}
this.defaultButton.setAttribute("checked", "true");
},
get shareButton() {
// web-panels (bookmark/sidebar) don't include customizableui, so
// nsContextMenu fails when accessing shareButton, breaking
// browser_bug409481.js.
if (!window.CustomizableUI)
return null;
let widget = CustomizableUI.getWidget("social-share-button");
if (!widget || !widget.areaType)
return null;
return widget.forWindow(window).node;
},
_onclick: function() {
Services.telemetry.getHistogramById("SOCIAL_PANEL_CLICKS").add(0);
},
onShowing: function() {
(this._currentAnchor || this.anchor).setAttribute("open", "true");
this.iframe.addEventListener("click", this._onclick, true);
},
onHidden: function() {
(this._currentAnchor || this.anchor).removeAttribute("open");
this._currentAnchor = null;
this.iframe.docShellIsActive = false;
this.iframe.removeEventListener("click", this._onclick, true);
this.iframe.setAttribute("src", "data:text/plain;charset=utf8,");
// make sure that the frame is unloaded after it is hidden
this.messageManager.sendAsyncMessage("Social:ClearFrame");
this.currentShare = null;
// share panel use is over, purge any history
this.iframe.purgeSessionHistory();
},
sharePage: function(providerOrigin, graphData, target, anchor) {
// if providerOrigin is undefined, we use the last-used provider, or the
// current/default provider. The provider selection in the share panel
// will call sharePage with an origin for us to switch to.
this._createFrame();
let iframe = this.iframe;
// graphData is an optional param that either defines the full set of data
// to be shared, or partial data about the current page. It is set by a call
// in mozSocial API, or via nsContentMenu calls. If it is present, it MUST
// define at least url. If it is undefined, we're sharing the current url in
// the browser tab.
let pageData = graphData ? graphData : this.currentShare;
let sharedURI = pageData ? Services.io.newURI(pageData.url, null, null) :
gBrowser.currentURI;
if (!SocialUI.canSharePage(sharedURI))
return;
let browserMM = gBrowser.selectedBrowser.messageManager;
// the point of this action type is that we can use existing share
// endpoints (e.g. oexchange) that do not support additional
// socialapi functionality. One tweak is that we shoot an event
// containing the open graph data.
let _dataFn;
if (!pageData || sharedURI == gBrowser.currentURI) {
browserMM.addMessageListener("PageMetadata:PageDataResult", _dataFn = (msg) => {
browserMM.removeMessageListener("PageMetadata:PageDataResult", _dataFn);
let pageData = msg.json;
if (graphData) {
// overwrite data retreived from page with data given to us as a param
for (let p in graphData) {
pageData[p] = graphData[p];
}
}
this.sharePage(providerOrigin, pageData, target, anchor);
});
browserMM.sendAsyncMessage("PageMetadata:GetPageData", null, { target });
return;
}
// if this is a share of a selected item, get any microformats
if (!pageData.microformats && target) {
browserMM.addMessageListener("PageMetadata:MicroformatsResult", _dataFn = (msg) => {
browserMM.removeMessageListener("PageMetadata:MicroformatsResult", _dataFn);
pageData.microformats = msg.data;
this.sharePage(providerOrigin, pageData, target, anchor);
});
browserMM.sendAsyncMessage("PageMetadata:GetMicroformats", null, { target });
return;
}
this.currentShare = pageData;
let provider;
if (providerOrigin)
provider = Social._getProviderFromOrigin(providerOrigin);
else
provider = this.getSelectedProvider();
if (!provider || !provider.shareURL) {
this.showDirectory(anchor);
return;
}
// check the menu button
let hbox = document.getElementById("social-share-provider-buttons");
let btn = hbox.querySelector("[origin='" + provider.origin + "']");
if (btn)
btn.checked = true;
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
this._dynamicResizer.stop();
let size = provider.getPageSize("share");
if (size) {
// let the css on the share panel define width, but height
// calculations dont work on all sites, so we allow that to be
// defined.
delete size.width;
}
// if we've already loaded this provider/page share endpoint, we don't want
// to add another load event listener.
let endpointMatch = shareEndpoint == iframe.getAttribute("src");
if (endpointMatch) {
this._dynamicResizer.start(iframe.parentNode, iframe, size);
iframe.docShellIsActive = true;
SocialShare.messageManager.sendAsyncMessage("Social:OpenGraphData", this.currentShare);
} else {
iframe.parentNode.setAttribute("loading", "true");
}
// if the user switched between share providers we do not want that history
// available.
iframe.purgeSessionHistory();
// always ensure that origin belongs to the endpoint
let uri = Services.io.newURI(shareEndpoint, null, null);
iframe.setAttribute("origin", provider.origin);
iframe.setAttribute("src", shareEndpoint);
this._openPanel(anchor);
},
showDirectory: function(anchor) {
this._createFrame();
let iframe = this.iframe;
if (iframe.getAttribute("src") == "about:providerdirectory")
return;
iframe.removeAttribute("origin");
iframe.parentNode.setAttribute("loading", "true");
iframe.setAttribute("src", "about:providerdirectory");
this._openPanel(anchor);
},
_openPanel: function(anchor) {
this._currentAnchor = anchor || this.anchor;
anchor = document.getAnonymousElementByAttribute(this._currentAnchor, "class", "toolbarbutton-icon");
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
Services.telemetry.getHistogramById("SOCIAL_TOOLBAR_BUTTONS").add(0);
}
};
})();

View file

@ -933,6 +933,11 @@ html|*#gcli-output-frame,
transition: none;
}
panelview > .social-panel-frame {
width: auto;
height: auto;
}
/* Translation */
notification[value="translation"] {
-moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");

View file

@ -44,6 +44,7 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
["ShortcutUtils", "resource://gre/modules/ShortcutUtils.jsm"],
["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
["SitePermissions", "resource:///modules/SitePermissions.jsm"],
["Social", "resource:///modules/Social.jsm"],
["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
["Task", "resource://gre/modules/Task.jsm"],
["TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"],
@ -1409,6 +1410,8 @@ var gBrowserInit = {
// Enable the Restore Last Session command if needed
RestoreLastSessionObserver.init();
SocialUI.init();
// Start monitoring slow add-ons
AddonWatcher.init();
@ -1539,6 +1542,7 @@ var gBrowserInit = {
gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
ctrlTab.uninit();
SocialUI.uninit();
gBrowserThumbnails.uninit();
FullZoom.destroy();
@ -4300,7 +4304,9 @@ var XULBrowserWindow = {
// Called before links are navigated to to allow us to retarget them if needed.
onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
let target = BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
SocialUI.closeSocialPanelForLinkTraversal(target, linkNode);
return target;
},
// Check whether this URI should load in the current process
@ -4481,6 +4487,8 @@ var XULBrowserWindow = {
gIdentityHandler.onLocationChange();
SocialUI.updateState();
UITour.onLocationChange(location);
gTabletModePageCounter.inc();

View file

@ -268,6 +268,27 @@
<box id="UITourHighlight"></box>
</panel>
<panel id="social-share-panel"
class="social-panel"
type="arrow"
orient="vertical"
onpopupshowing="SocialShare.onShowing()"
onpopuphidden="SocialShare.onHidden()"
hidden="true">
<hbox class="social-share-toolbar">
<toolbarbutton id="manage-share-providers" class="share-provider-button"
tooltiptext="&social.addons.label;"
oncommand="BrowserOpenAddonsMgr('addons://list/service');
this.parentNode.parentNode.hidePopup();"/>
<arrowscrollbox id="social-share-provider-buttons" orient="horizontal" flex="1" pack="end">
<toolbarbutton id="add-share-provider" class="share-provider-button" type="radio"
group="share-providers" tooltiptext="&findShareServices.label;"
oncommand="SocialShare.showDirectory()"/>
</arrowscrollbox>
</hbox>
<hbox id="share-container" flex="1"/>
</panel>
<menupopup id="toolbar-context-menu"
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
<menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
@ -405,6 +426,11 @@
#endif
</tooltip>
<tooltip id="share-button-tooltip" onpopupshowing="SocialShare.createTooltip(event);">
<label class="tooltip-label"/>
<label class="tooltip-label"/>
</tooltip>
#include popup-notifications.inc
#include ../../components/customizableui/content/panelUI.inc.xul

View file

@ -698,6 +698,37 @@ var PageMetadataMessenger = {
}
PageMetadataMessenger.init();
addEventListener("ActivateSocialFeature", function (aEvent) {
let document = content.document;
let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (!dwu.isHandlingUserInput) {
Cu.reportError("attempt to activate provider without user input from " + document.nodePrincipal.origin);
return;
}
let node = aEvent.target;
let ownerDocument = node.ownerDocument;
let data = node.getAttribute("data-service");
if (data) {
try {
data = JSON.parse(data);
} catch (e) {
Cu.reportError("Social Service manifest parse error: " + e);
return;
}
} else {
Cu.reportError("Social Service manifest not available");
return;
}
sendAsyncMessage("Social:Activation", {
url: ownerDocument.location.href,
origin: ownerDocument.nodePrincipal.origin,
manifest: data
});
}, true, true);
addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => {
let video = message.objects.target;
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");

View file

@ -174,15 +174,15 @@ nsContextMenu.prototype = {
initNavigationItems: function CM_initNavigationItems() {
var shouldShow = !(this.isContentSelected || this.onLink || this.onImage ||
this.onCanvas || this.onVideo || this.onAudio ||
this.onTextInput);
this.onTextInput || this.onSocial);
this.showItem("context-navigation", shouldShow);
this.showItem("context-sep-navigation", shouldShow);
let stopped = XULBrowserWindow.stopCommand.getAttribute("disabled") == "true";
let stopReloadItem = "";
if (shouldShow) {
stopReloadItem = (stopped) ? "reload" : "stop";
if (shouldShow || this.onSocial) {
stopReloadItem = (stopped || this.onSocial) ? "reload" : "stop";
}
this.showItem("context-reload", stopReloadItem == "reload");
@ -249,7 +249,7 @@ nsContextMenu.prototype = {
this.onImage || this.onCanvas ||
this.onVideo || this.onAudio ||
this.onLink || this.onTextInput);
var showInspect = gPrefService.getBoolPref("devtools.inspector.enabled");
var showInspect = !this.onSocial && gPrefService.getBoolPref("devtools.inspector.enabled");
this.showItem("context-viewsource", shouldShow);
this.showItem("context-viewinfo", shouldShow);
this.showItem("inspect-separator", showInspect);
@ -306,11 +306,12 @@ nsContextMenu.prototype = {
let bookmarkPage = document.getElementById("context-bookmarkpage");
this.showItem(bookmarkPage,
!(this.isContentSelected || this.onTextInput || this.onLink ||
this.onImage || this.onVideo || this.onAudio || this.onCanvas));
this.onImage || this.onVideo || this.onAudio || this.onSocial ||
this.onCanvas));
bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext"));
this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink) ||
this.onPlainTextLink);
this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink &&
!this.onSocial) || this.onPlainTextLink);
this.showItem("context-keywordfield",
this.onTextInput && this.onKeywordField);
this.showItem("frame", this.inFrame);
@ -348,6 +349,19 @@ nsContextMenu.prototype = {
this.onTextInput && !this.onNumeric && top.gBidiUI);
this.showItem("context-bidi-page-direction-toggle",
!this.onTextInput && top.gBidiUI);
// SocialShare
let shareButton = SocialShare.shareButton;
let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
let pageShare = shareEnabled && !(this.isContentSelected ||
this.onTextInput || this.onLink || this.onImage ||
this.onVideo || this.onAudio || this.onCanvas);
this.showItem("context-sharepage", pageShare);
this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
this.showItem("context-shareimage", shareEnabled && this.onImage);
this.showItem("context-sharevideo", shareEnabled && this.onVideo);
this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL || this.mediaURL.startsWith("blob:"));
},
initSpellingItems: function() {
@ -667,6 +681,7 @@ nsContextMenu.prototype = {
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
}
this.onSocial = !!this.browser.getAttribute("origin");
// Check if we are in a synthetic document (stand alone image, video, etc.).
this.inSyntheticDoc = ownerDoc.mozSyntheticDocument;
@ -1711,6 +1726,22 @@ nsContextMenu.prototype = {
mm.sendAsyncMessage("ContextMenu:BookmarkFrame", null, { target: this.target });
},
shareLink: function CM_shareLink() {
SocialShare.sharePage(null, { url: this.linkURI.spec }, this.target);
},
shareImage: function CM_shareImage() {
SocialShare.sharePage(null, { url: this.imageURL, previews: [ this.mediaURL ] }, this.target);
},
shareVideo: function CM_shareVideo() {
SocialShare.sharePage(null, { url: this.mediaURL, source: this.mediaURL }, this.target);
},
shareSelect: function CM_shareSelect() {
SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: this.textSelected }, this.target);
},
savePageAs: function CM_savePageAs() {
saveBrowser(this.browser);
},
@ -1825,7 +1856,7 @@ nsContextMenu.prototype = {
_getTelemetryPageContextInfo: function() {
let rv = [];
for (let k of ["isContentSelected", "onLink", "onImage", "onCanvas", "onVideo", "onAudio",
"onTextInput"]) {
"onTextInput", "onSocial"]) {
if (this[k]) {
rv.push(k.replace(/^(?:is|on)(.)/, (match, firstLetter) => firstLetter.toLowerCase()));
}

View file

@ -0,0 +1,172 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
/* This content script is intended for use by iframes in the share panel. */
var {interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// social frames are always treated as app tabs
docShell.isAppTab = true;
addEventListener("DOMContentLoaded", function(event) {
if (event.target != content.document)
return;
// Some share panels (e.g. twitter and facebook) check content.opener, and if
// it doesn't exist they act like they are in a browser tab. We want them to
// act like they are in a dialog (which is the typical case).
if (content && !content.opener) {
content.opener = content;
}
hookWindowClose();
disableDialogs();
});
addMessageListener("Social:OpenGraphData", (message) => {
let ev = new content.CustomEvent("OpenGraphData", { detail: JSON.stringify(message.data) });
content.dispatchEvent(ev);
});
addMessageListener("Social:ClearFrame", () => {
docShell.createAboutBlankContentViewer(null);
});
addEventListener("DOMWindowClose", (evt) => {
// 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();
// Tells the SocialShare class to close the panel
sendAsyncMessage("Social:DOMWindowClose");
});
function hookWindowClose() {
// Allow scripts to close the "window". Because we are in a panel and not
// in a full dialog, the DOMWindowClose listener above will only receive the
// event if we do this.
let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
dwu.allowScriptsToClose();
}
function disableDialogs() {
let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
windowUtils.disableDialogs();
}
// Error handling class used to listen for network errors in the social frames
// and replace them with a social-specific error page
const SocialErrorListener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
Ci.nsISupports]),
defaultTemplate: "about:socialerror?mode=tryAgainOnly&url=%{url}&origin=%{origin}",
urlTemplate: null,
init() {
addMessageListener("Social:SetErrorURL", this);
let webProgress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
Ci.nsIWebProgress.NOTIFY_LOCATION);
},
receiveMessage(message) {
switch (message.name) {
case "Social:SetErrorURL":
// Either a url or null to reset to default template.
this.urlTemplate = message.data.template;
break;
}
},
setErrorPage() {
// if this is about:providerdirectory, use the directory iframe
let frame = docShell.chromeEventHandler;
let origin = frame.getAttribute("origin");
let src = frame.getAttribute("src");
if (src == "about:providerdirectory") {
frame = content.document.getElementById("activation-frame");
src = frame.getAttribute("src");
}
let url = this.urlTemplate || this.defaultTemplate;
url = url.replace("%{url}", encodeURIComponent(src));
url = url.replace("%{origin}", encodeURIComponent(origin));
if (frame != docShell.chromeEventHandler) {
// Unable to access frame.docShell here. This is our own frame and doesn't
// provide reload, so we'll just set the src.
frame.setAttribute("src", url);
} else {
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.loadURI(url, null, null, null, null);
}
sendAsyncMessage("Social:ErrorPageNotify", {
origin: origin,
url: src
});
},
onStateChange(aWebProgress, aRequest, aState, aStatus) {
let failure = false;
if ((aState & Ci.nsIWebProgressListener.STATE_IS_REQUEST))
return;
if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
if (aRequest instanceof Ci.nsIHttpChannel) {
try {
// Change the frame to an error page on 4xx (client errors)
// and 5xx (server errors). responseStatus throws if it is not set.
failure = aRequest.responseStatus >= 400 &&
aRequest.responseStatus < 600;
} catch (e) {
failure = aStatus != Components.results.NS_OK;
}
}
}
// Calling cancel() will raise some OnStateChange notifications by itself,
// so avoid doing that more than once
if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
// if tp is enabled and we get a failure, ignore failures (ie. STATE_STOP)
// on child resources since they *may* have been blocked. We don't have an
// easy way to know if a particular url is blocked by TP, only that
// something was.
if (docShell.hasTrackingContentBlocked) {
let frame = docShell.chromeEventHandler;
let src = frame.getAttribute("src");
if (aRequest && aRequest.name != src) {
Cu.reportError("SocialErrorListener ignoring blocked content error for " + aRequest.name);
return;
}
}
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
this.setErrorPage();
}
},
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
if (aRequest && aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
this.setErrorPage();
}
},
onProgressChange() {},
onStatusChange() {},
onSecurityChange() {},
};
SocialErrorListener.init();

View file

@ -64,6 +64,8 @@ browser.jar:
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
content/browser/aboutSocialError.xhtml (content/aboutSocialError.xhtml)
content/browser/aboutProviderDirectory.xhtml (content/aboutProviderDirectory.xhtml)
content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css)
content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js)
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
@ -89,6 +91,7 @@ browser.jar:
content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js)
#endif
content/browser/browser-sidebar.js (content/browser-sidebar.js)
content/browser/browser-social.js (content/browser-social.js)
* content/browser/browser-syncui.js (content/browser-syncui.js)
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
#ifdef CAN_DRAW_IN_TITLEBAR
@ -100,6 +103,7 @@ browser.jar:
content/browser/browser-trackingprotection.js (content/browser-trackingprotection.js)
* 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)
content/browser/defaultthemes/1.footer.jpg (content/defaultthemes/1.footer.jpg)
content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg)
content/browser/defaultthemes/1.icon.jpg (content/defaultthemes/1.icon.jpg)