mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
bug 891219 new social bookmarks, part 2, new implementation, r=markh
This commit is contained in:
parent
967bf9736a
commit
fbbdb70eaf
@ -45,6 +45,10 @@
|
|||||||
label="&saveLinkCmd.label;"
|
label="&saveLinkCmd.label;"
|
||||||
accesskey="&saveLinkCmd.accesskey;"
|
accesskey="&saveLinkCmd.accesskey;"
|
||||||
oncommand="gContextMenu.saveLink();"/>
|
oncommand="gContextMenu.saveLink();"/>
|
||||||
|
<menu id="context-marklinkMenu" label="&social.marklinkMenu.label;"
|
||||||
|
accesskey="&social.marklink.accesskey;">
|
||||||
|
<menupopup/>
|
||||||
|
</menu>
|
||||||
<menuitem id="context-copyemail"
|
<menuitem id="context-copyemail"
|
||||||
label="©EmailCmd.label;"
|
label="©EmailCmd.label;"
|
||||||
accesskey="©EmailCmd.accesskey;"
|
accesskey="©EmailCmd.accesskey;"
|
||||||
@ -247,6 +251,10 @@
|
|||||||
label="&savePageCmd.label;"
|
label="&savePageCmd.label;"
|
||||||
accesskey="&savePageCmd.accesskey2;"
|
accesskey="&savePageCmd.accesskey2;"
|
||||||
oncommand="gContextMenu.savePageAs();"/>
|
oncommand="gContextMenu.savePageAs();"/>
|
||||||
|
<menu id="context-markpageMenu" label="&social.markpageMenu.label;"
|
||||||
|
accesskey="&social.markpage.accesskey;">
|
||||||
|
<menupopup/>
|
||||||
|
</menu>
|
||||||
<menuseparator id="context-sep-viewbgimage"/>
|
<menuseparator id="context-sep-viewbgimage"/>
|
||||||
<menuitem id="context-viewbgimage"
|
<menuitem id="context-viewbgimage"
|
||||||
label="&viewBGImageCmd.label;"
|
label="&viewBGImageCmd.label;"
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
let SocialUI,
|
let SocialUI,
|
||||||
SocialChatBar,
|
SocialChatBar,
|
||||||
SocialFlyout,
|
SocialFlyout,
|
||||||
|
SocialMarks,
|
||||||
SocialShare,
|
SocialShare,
|
||||||
SocialMenu,
|
SocialMenu,
|
||||||
SocialToolbar,
|
SocialToolbar,
|
||||||
@ -26,6 +27,18 @@ XPCOMUtils.defineLazyGetter(this, "OpenGraphBuilder", function() {
|
|||||||
return tmp.OpenGraphBuilder;
|
return tmp.OpenGraphBuilder;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "DynamicResizeWatcher", function() {
|
||||||
|
let tmp = {};
|
||||||
|
Cu.import("resource:///modules/Social.jsm", tmp);
|
||||||
|
return tmp.DynamicResizeWatcher;
|
||||||
|
});
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "sizeSocialPanelToContent", function() {
|
||||||
|
let tmp = {};
|
||||||
|
Cu.import("resource:///modules/Social.jsm", tmp);
|
||||||
|
return tmp.sizeSocialPanelToContent;
|
||||||
|
});
|
||||||
|
|
||||||
SocialUI = {
|
SocialUI = {
|
||||||
// Called on delayed startup to initialize the UI
|
// Called on delayed startup to initialize the UI
|
||||||
init: function SocialUI_init() {
|
init: function SocialUI_init() {
|
||||||
@ -44,6 +57,10 @@ SocialUI = {
|
|||||||
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
||||||
|
|
||||||
gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
|
gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
|
||||||
|
window.addEventListener("aftercustomization", function() {
|
||||||
|
if (SocialUI.enabled)
|
||||||
|
SocialMarks.populateContextMenu(SocialMarks);
|
||||||
|
}, false);
|
||||||
|
|
||||||
if (!Social.initialized) {
|
if (!Social.initialized) {
|
||||||
Social.init();
|
Social.init();
|
||||||
@ -82,15 +99,19 @@ SocialUI = {
|
|||||||
try {
|
try {
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
case "social:provider-installed":
|
case "social:provider-installed":
|
||||||
|
SocialMarks.setPosition(data);
|
||||||
SocialStatus.setPosition(data);
|
SocialStatus.setPosition(data);
|
||||||
break;
|
break;
|
||||||
case "social:provider-uninstalled":
|
case "social:provider-uninstalled":
|
||||||
|
SocialMarks.removePosition(data);
|
||||||
SocialStatus.removePosition(data);
|
SocialStatus.removePosition(data);
|
||||||
break;
|
break;
|
||||||
case "social:provider-enabled":
|
case "social:provider-enabled":
|
||||||
|
SocialMarks.populateToolbarPalette();
|
||||||
SocialStatus.populateToolbarPalette();
|
SocialStatus.populateToolbarPalette();
|
||||||
break;
|
break;
|
||||||
case "social:provider-disabled":
|
case "social:provider-disabled":
|
||||||
|
SocialMarks.removeProvider(data);
|
||||||
SocialStatus.removeProvider(data);
|
SocialStatus.removeProvider(data);
|
||||||
break;
|
break;
|
||||||
case "social:provider-reload":
|
case "social:provider-reload":
|
||||||
@ -115,6 +136,7 @@ SocialUI = {
|
|||||||
SocialSidebar.update();
|
SocialSidebar.update();
|
||||||
SocialToolbar.update();
|
SocialToolbar.update();
|
||||||
SocialStatus.populateToolbarPalette();
|
SocialStatus.populateToolbarPalette();
|
||||||
|
SocialMarks.populateToolbarPalette();
|
||||||
SocialMenu.populate();
|
SocialMenu.populate();
|
||||||
break;
|
break;
|
||||||
case "social:providers-changed":
|
case "social:providers-changed":
|
||||||
@ -124,6 +146,7 @@ SocialUI = {
|
|||||||
SocialToolbar.populateProviderMenus();
|
SocialToolbar.populateProviderMenus();
|
||||||
SocialShare.populateProviderMenu();
|
SocialShare.populateProviderMenu();
|
||||||
SocialStatus.populateToolbarPalette();
|
SocialStatus.populateToolbarPalette();
|
||||||
|
SocialMarks.populateToolbarPalette();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Provider-specific notifications
|
// Provider-specific notifications
|
||||||
@ -137,6 +160,7 @@ SocialUI = {
|
|||||||
case "social:profile-changed":
|
case "social:profile-changed":
|
||||||
if (this._matchesCurrentProvider(data)) {
|
if (this._matchesCurrentProvider(data)) {
|
||||||
SocialToolbar.updateProvider();
|
SocialToolbar.updateProvider();
|
||||||
|
SocialMarks.update();
|
||||||
SocialChatBar.update();
|
SocialChatBar.update();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -363,6 +387,14 @@ SocialUI = {
|
|||||||
return !!Social.provider;
|
return !!Social.provider;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// called on tab/urlbar/location changes and after customization. Update
|
||||||
|
// anything that is tab specific.
|
||||||
|
updateState: function() {
|
||||||
|
if (!this.enabled)
|
||||||
|
return;
|
||||||
|
SocialMarks.update();
|
||||||
|
SocialShare.update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SocialChatBar = {
|
SocialChatBar = {
|
||||||
@ -403,63 +435,6 @@ SocialChatBar = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sizeSocialPanelToContent(panel, iframe) {
|
|
||||||
// FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
|
|
||||||
let doc = iframe.contentDocument;
|
|
||||||
if (!doc || !doc.body) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We need an element to use for sizing our panel. See if the body defines
|
|
||||||
// an id for that element, otherwise use the body itself.
|
|
||||||
let body = doc.body;
|
|
||||||
let bodyId = body.getAttribute("contentid");
|
|
||||||
if (bodyId) {
|
|
||||||
body = doc.getElementById(bodyId) || doc.body;
|
|
||||||
}
|
|
||||||
// offsetHeight/Width don't include margins, so account for that.
|
|
||||||
let cs = doc.defaultView.getComputedStyle(body);
|
|
||||||
let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
|
|
||||||
let height = Math.max(computedHeight, PANEL_MIN_HEIGHT);
|
|
||||||
let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
|
|
||||||
let width = Math.max(computedWidth, PANEL_MIN_WIDTH);
|
|
||||||
iframe.style.width = width + "px";
|
|
||||||
iframe.style.height = height + "px";
|
|
||||||
// since we do not use panel.sizeTo, we need to adjust the arrow ourselves
|
|
||||||
if (panel.state == "open")
|
|
||||||
panel.adjustArrowPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
function DynamicResizeWatcher() {
|
|
||||||
this._mutationObserver = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicResizeWatcher.prototype = {
|
|
||||||
start: function DynamicResizeWatcher_start(panel, iframe) {
|
|
||||||
this.stop(); // just in case...
|
|
||||||
let doc = iframe.contentDocument;
|
|
||||||
this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
|
|
||||||
sizeSocialPanelToContent(panel, iframe);
|
|
||||||
});
|
|
||||||
// Observe anything that causes the size to change.
|
|
||||||
let config = {attributes: true, characterData: true, childList: true, subtree: true};
|
|
||||||
this._mutationObserver.observe(doc, config);
|
|
||||||
// and since this may be setup after the load event has fired we do an
|
|
||||||
// initial resize now.
|
|
||||||
sizeSocialPanelToContent(panel, iframe);
|
|
||||||
},
|
|
||||||
stop: function DynamicResizeWatcher_stop() {
|
|
||||||
if (this._mutationObserver) {
|
|
||||||
try {
|
|
||||||
this._mutationObserver.disconnect();
|
|
||||||
} catch (ex) {
|
|
||||||
// may get "TypeError: can't access dead object" which seems strange,
|
|
||||||
// but doesn't seem to indicate a real problem, so ignore it...
|
|
||||||
}
|
|
||||||
this._mutationObserver = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SocialFlyout = {
|
SocialFlyout = {
|
||||||
get panel() {
|
get panel() {
|
||||||
return document.getElementById("social-flyout-panel");
|
return document.getElementById("social-flyout-panel");
|
||||||
@ -765,7 +740,7 @@ SocialShare = {
|
|||||||
}
|
}
|
||||||
this.currentShare = pageData;
|
this.currentShare = pageData;
|
||||||
|
|
||||||
let shareEndpoint = this._generateShareEndpointURL(provider.shareURL, pageData);
|
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
|
||||||
|
|
||||||
this._dynamicResizer = new DynamicResizeWatcher();
|
this._dynamicResizer = new DynamicResizeWatcher();
|
||||||
// if we've already loaded this provider/page share endpoint, we don't want
|
// if we've already loaded this provider/page share endpoint, we don't want
|
||||||
@ -810,44 +785,6 @@ SocialShare = {
|
|||||||
document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
|
document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
|
||||||
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||||
Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
|
Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
|
||||||
},
|
|
||||||
|
|
||||||
_generateShareEndpointURL: function(shareURL, pageData) {
|
|
||||||
// support for existing share endpoints by supporting their querystring
|
|
||||||
// arguments. parse the query string template and do replacements where
|
|
||||||
// necessary the query names may be different than ours, so we could see
|
|
||||||
// u=%{url} or url=%{url}
|
|
||||||
let [shareEndpoint, queryString] = shareURL.split("?");
|
|
||||||
let query = {};
|
|
||||||
if (queryString) {
|
|
||||||
queryString.split('&').forEach(function (val) {
|
|
||||||
let [name, value] = val.split('=');
|
|
||||||
let p = /%\{(.+)\}/.exec(value);
|
|
||||||
if (!p) {
|
|
||||||
// preserve non-template query vars
|
|
||||||
query[name] = value;
|
|
||||||
} else if (pageData[p[1]]) {
|
|
||||||
query[name] = pageData[p[1]];
|
|
||||||
} else if (p[1] == "body") {
|
|
||||||
// build a body for emailers
|
|
||||||
let body = "";
|
|
||||||
if (pageData.title)
|
|
||||||
body += pageData.title + "\n\n";
|
|
||||||
if (pageData.description)
|
|
||||||
body += pageData.description + "\n\n";
|
|
||||||
if (pageData.text)
|
|
||||||
body += pageData.text + "\n\n";
|
|
||||||
body += pageData.url;
|
|
||||||
query["body"] = body;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var str = [];
|
|
||||||
for (let p in query)
|
|
||||||
str.push(p + "=" + encodeURIComponent(query[p]));
|
|
||||||
if (str.length)
|
|
||||||
shareEndpoint = shareEndpoint + "?" + str.join("&");
|
|
||||||
return shareEndpoint;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1450,7 +1387,7 @@ ToolbarHelper.prototype = {
|
|||||||
for (let provider of Social.providers) {
|
for (let provider of Social.providers) {
|
||||||
let id = this.idFromOrgin(provider.origin);
|
let id = this.idFromOrgin(provider.origin);
|
||||||
if (this._getExistingButton(id))
|
if (this._getExistingButton(id))
|
||||||
return;
|
continue;
|
||||||
let button = this._createButton(provider);
|
let button = this._createButton(provider);
|
||||||
if (button && persistedById.hasOwnProperty(id)) {
|
if (button && persistedById.hasOwnProperty(id)) {
|
||||||
let parent = persistedById[id];
|
let parent = persistedById[id];
|
||||||
@ -1684,4 +1621,130 @@ SocialStatus = {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SocialMarks
|
||||||
|
*
|
||||||
|
* Handles updates to toolbox and signals all buttons to update when necessary.
|
||||||
|
*/
|
||||||
|
SocialMarks = {
|
||||||
|
update: function() {
|
||||||
|
// signal each button to update itself
|
||||||
|
let currentButtons = document.querySelectorAll('toolbarbutton[type="socialmark"]');
|
||||||
|
for (let elt of currentButtons)
|
||||||
|
elt.update();
|
||||||
|
},
|
||||||
|
|
||||||
|
getProviders: function() {
|
||||||
|
// only rely on providers that the user has placed in the UI somewhere. This
|
||||||
|
// also means that populateToolbarPalette must be called prior to using this
|
||||||
|
// method, otherwise you get a big fat zero. For our use case with context
|
||||||
|
// menu's, this is ok.
|
||||||
|
let tbh = this._toolbarHelper;
|
||||||
|
return [p for (p of Social.providers) if (p.markURL &&
|
||||||
|
document.getElementById(tbh.idFromOrgin(p.origin)))];
|
||||||
|
},
|
||||||
|
|
||||||
|
populateContextMenu: function() {
|
||||||
|
// only show a selection if enabled and there is more than one
|
||||||
|
let providers = this.getProviders();
|
||||||
|
|
||||||
|
// remove all previous entries by class
|
||||||
|
let menus = [m for (m of document.getElementsByClassName("context-socialmarks"))];
|
||||||
|
[m.parentNode.removeChild(m) for (m of menus)];
|
||||||
|
|
||||||
|
let contextMenus = [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
id: "context-marklinkMenu",
|
||||||
|
label: "social.marklink.label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "page",
|
||||||
|
id: "context-markpageMenu",
|
||||||
|
label: "social.markpage.label"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
for (let cfg of contextMenus) {
|
||||||
|
this._populateContextPopup(cfg, providers);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MENU_LIMIT: 3, // adjustable for testing
|
||||||
|
_populateContextPopup: function(menuInfo, providers) {
|
||||||
|
let menu = document.getElementById(menuInfo.id);
|
||||||
|
let popup = menu.firstChild;
|
||||||
|
for (let provider of providers) {
|
||||||
|
// We show up to MENU_LIMIT providers as single menuitems's at the top
|
||||||
|
// level of the context menu, if we have more than that, dump them *all*
|
||||||
|
// into the menu popup.
|
||||||
|
let mi = document.createElement("menuitem");
|
||||||
|
mi.setAttribute("oncommand", "gContextMenu.markLink(this.getAttribute('origin'));");
|
||||||
|
mi.setAttribute("origin", provider.origin);
|
||||||
|
mi.setAttribute("image", provider.iconURL);
|
||||||
|
if (providers.length <= this.MENU_LIMIT) {
|
||||||
|
// an extra class to make enable/disable easy
|
||||||
|
mi.setAttribute("class", "menuitem-iconic context-socialmarks context-mark"+menuInfo.type);
|
||||||
|
let menuLabel = gNavigatorBundle.getFormattedString(menuInfo.label, [provider.name]);
|
||||||
|
mi.setAttribute("label", menuLabel);
|
||||||
|
menu.parentNode.insertBefore(mi, menu);
|
||||||
|
} else {
|
||||||
|
mi.setAttribute("class", "menuitem-iconic context-socialmarks");
|
||||||
|
mi.setAttribute("label", provider.name);
|
||||||
|
popup.appendChild(mi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
populateToolbarPalette: function() {
|
||||||
|
this._toolbarHelper.populatePalette();
|
||||||
|
this.populateContextMenu();
|
||||||
|
},
|
||||||
|
|
||||||
|
setPosition: function(origin) {
|
||||||
|
// this is called during install, before the provider is enabled so we have
|
||||||
|
// to use the manifest rather than the provider instance as we do elsewhere.
|
||||||
|
let manifest = Social.getManifestByOrigin(origin);
|
||||||
|
if (!manifest.markURL)
|
||||||
|
return;
|
||||||
|
let tbh = this._toolbarHelper;
|
||||||
|
tbh.setPersistentPosition(tbh.idFromOrgin(origin));
|
||||||
|
},
|
||||||
|
|
||||||
|
removePosition: function(origin) {
|
||||||
|
let tbh = this._toolbarHelper;
|
||||||
|
tbh.removePersistence(tbh.idFromOrgin(origin));
|
||||||
|
},
|
||||||
|
|
||||||
|
removeProvider: function(origin) {
|
||||||
|
this._toolbarHelper.removeProviderButton(origin);
|
||||||
|
},
|
||||||
|
|
||||||
|
get _toolbarHelper() {
|
||||||
|
delete this._toolbarHelper;
|
||||||
|
this._toolbarHelper = new ToolbarHelper("social-mark-button", this._createButton.bind(this));
|
||||||
|
return this._toolbarHelper;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createButton: function(provider) {
|
||||||
|
if (!provider.markURL)
|
||||||
|
return null;
|
||||||
|
let palette = document.getElementById("navigator-toolbox").palette;
|
||||||
|
let button = document.createElement("toolbarbutton");
|
||||||
|
button.setAttribute("type", "socialmark");
|
||||||
|
button.setAttribute("class", "toolbarbutton-1 social-mark-button");
|
||||||
|
button.style.listStyleImage = "url(" + provider.iconURL + ")";
|
||||||
|
button.setAttribute("origin", provider.origin);
|
||||||
|
button.setAttribute("id", this._toolbarHelper.idFromOrgin(provider.origin));
|
||||||
|
palette.appendChild(button);
|
||||||
|
return button
|
||||||
|
},
|
||||||
|
|
||||||
|
markLink: function(aOrigin, aUrl) {
|
||||||
|
// find the button for this provider, and open it
|
||||||
|
let id = this._toolbarHelper.idFromOrgin(aOrigin);
|
||||||
|
document.getElementById(id).markLink(aUrl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -656,6 +656,14 @@ toolbarbutton[type="badged"] {
|
|||||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#toolbarbutton-badged");
|
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#toolbarbutton-badged");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toolbarbutton[type="socialmark"] {
|
||||||
|
-moz-binding: url("chrome://browser/content/socialmarks.xml#toolbarbutton-marks");
|
||||||
|
}
|
||||||
|
toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Note the chatbox 'width' values are duplicated in socialchat.xml */
|
/* Note the chatbox 'width' values are duplicated in socialchat.xml */
|
||||||
chatbox {
|
chatbox {
|
||||||
-moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
|
-moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
|
||||||
|
@ -3445,7 +3445,7 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) {
|
|||||||
URLBarSetURI();
|
URLBarSetURI();
|
||||||
XULBrowserWindow.asyncUpdateUI();
|
XULBrowserWindow.asyncUpdateUI();
|
||||||
BookmarkingUI.updateStarState();
|
BookmarkingUI.updateStarState();
|
||||||
SocialShare.update();
|
SocialUI.updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
TabsInTitlebar.allowedBy("customizing-toolbars", true);
|
TabsInTitlebar.allowedBy("customizing-toolbars", true);
|
||||||
@ -3913,9 +3913,7 @@ var XULBrowserWindow = {
|
|||||||
|
|
||||||
// Update starring UI
|
// Update starring UI
|
||||||
BookmarkingUI.updateStarState();
|
BookmarkingUI.updateStarState();
|
||||||
if (SocialUI.enabled) {
|
SocialUI.updateState();
|
||||||
SocialShare.update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show or hide browser chrome based on the whitelist
|
// Show or hide browser chrome based on the whitelist
|
||||||
|
@ -309,6 +309,24 @@ nsContextMenu.prototype = {
|
|||||||
this.showItem("context-bidi-page-direction-toggle",
|
this.showItem("context-bidi-page-direction-toggle",
|
||||||
!this.onTextInput && top.gBidiUI);
|
!this.onTextInput && top.gBidiUI);
|
||||||
|
|
||||||
|
// SocialMarks. Marks does not work with text selections, only links. If
|
||||||
|
// there is more than MENU_LIMIT providers, we show a submenu for them,
|
||||||
|
// otherwise we have a menuitem per provider (added in SocialMarks class).
|
||||||
|
let markProviders = SocialMarks.getProviders();
|
||||||
|
let enablePageMarks = markProviders.length > 0 && !(this.onLink || this.onImage
|
||||||
|
|| this.onVideo || this.onAudio);
|
||||||
|
this.showItem("context-markpageMenu", enablePageMarks && markProviders.length > SocialMarks.MENU_LIMIT);
|
||||||
|
let enablePageMarkItems = enablePageMarks && markProviders.length <= SocialMarks.MENU_LIMIT;
|
||||||
|
let linkmenus = document.getElementsByClassName("context-markpage");
|
||||||
|
[m.hidden = !enablePageMarkItems for (m of linkmenus)];
|
||||||
|
|
||||||
|
let enableLinkMarks = markProviders.length > 0 &&
|
||||||
|
((this.onLink && !this.onMailtoLink) || this.onPlainTextLink);
|
||||||
|
this.showItem("context-marklinkMenu", enableLinkMarks && markProviders.length > SocialMarks.MENU_LIMIT);
|
||||||
|
let enableLinkMarkItems = enableLinkMarks && markProviders.length <= SocialMarks.MENU_LIMIT;
|
||||||
|
linkmenus = document.getElementsByClassName("context-marklink");
|
||||||
|
[m.hidden = !enableLinkMarkItems for (m of linkmenus)];
|
||||||
|
|
||||||
// SocialShare
|
// SocialShare
|
||||||
let shareButton = SocialShare.shareButton;
|
let shareButton = SocialShare.shareButton;
|
||||||
let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
|
let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
|
||||||
@ -1571,6 +1589,10 @@ nsContextMenu.prototype = {
|
|||||||
}, window.top);
|
}, window.top);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
markLink: function CM_markLink(origin) {
|
||||||
|
// send link to social, if it is the page url linkURI will be null
|
||||||
|
SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null);
|
||||||
|
},
|
||||||
shareLink: function CM_shareLink() {
|
shareLink: function CM_shareLink() {
|
||||||
SocialShare.sharePage(null, { url: this.linkURI.spec });
|
SocialShare.sharePage(null, { url: this.linkURI.spec });
|
||||||
},
|
},
|
||||||
|
236
browser/base/content/socialmarks.xml
Normal file
236
browser/base/content/socialmarks.xml
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<bindings id="socialMarkBindings"
|
||||||
|
xmlns="http://www.mozilla.org/xbl"
|
||||||
|
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||||
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||||
|
|
||||||
|
|
||||||
|
<binding id="toolbarbutton-marks" display="xul:button"
|
||||||
|
extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
|
||||||
|
<content disabled="true">
|
||||||
|
<xul:panel anonid="panel" hidden="true" type="arrow" class="social-panel">
|
||||||
|
<xul:iframe type="content" flex="1" tooltip="aHTMLTooltip"
|
||||||
|
class="social-panel-frame" context="contentAreaContextMenu"
|
||||||
|
xbl:inherits="src,origin"/>
|
||||||
|
</xul:panel>
|
||||||
|
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
|
||||||
|
<xul:label class="toolbarbutton-text" crop="right" flex="1"
|
||||||
|
xbl:inherits="value=label,accesskey,crop"/>
|
||||||
|
</content>
|
||||||
|
<implementation implements="nsIDOMEventListener, nsIObserver">
|
||||||
|
<field name="panel" readonly="true">
|
||||||
|
document.getAnonymousElementByAttribute(this, "anonid", "panel");
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<field name="content" readonly="true">
|
||||||
|
this.panel.firstChild;
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<property name="contentWindow">
|
||||||
|
<getter>
|
||||||
|
return this.content.contentWindow;
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="contentDocument">
|
||||||
|
<getter>
|
||||||
|
return this.content.contentDocument;
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="provider">
|
||||||
|
<getter>
|
||||||
|
return Social._getProviderFromOrigin(this.getAttribute("origin"));
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="isMarked">
|
||||||
|
<setter>
|
||||||
|
this._isMarked = val;
|
||||||
|
let provider = this.provider;
|
||||||
|
// we cannot size the image when we apply it via listStyleImage, so
|
||||||
|
// use the toolbar image
|
||||||
|
if (val)
|
||||||
|
this.setAttribute("image", provider.unmarkedIcon || provider.iconURL);
|
||||||
|
else
|
||||||
|
this.setAttribute("image", provider.markedIcon || provider.iconURL);
|
||||||
|
</setter>
|
||||||
|
<getter>
|
||||||
|
return this._isMarked;
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<method name="update">
|
||||||
|
<body><![CDATA[
|
||||||
|
// update the button for use with the current tab
|
||||||
|
let provider = this.provider;
|
||||||
|
if (this._dynamicResizer) {
|
||||||
|
this._dynamicResizer.stop();
|
||||||
|
this._dynamicResizer = null;
|
||||||
|
}
|
||||||
|
this.setAttribute("src", "about:blank");
|
||||||
|
|
||||||
|
// do we have a savable page loaded?
|
||||||
|
let aURI = gBrowser.currentURI;
|
||||||
|
this.disabled = !aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https'));
|
||||||
|
|
||||||
|
if (this.disabled) {
|
||||||
|
this.isMarked = false;
|
||||||
|
} else {
|
||||||
|
Social.isURIMarked(provider.origin, aURI, (isMarked) => {
|
||||||
|
this.isMarked = isMarked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.setAttribute("label", provider.name);
|
||||||
|
this.setAttribute("tooltiptext", provider.name);
|
||||||
|
this.setAttribute("origin", provider.origin);
|
||||||
|
this.panel.hidePopup();
|
||||||
|
this.panel.hidden = true;
|
||||||
|
this.pageData = null;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="loadPanel">
|
||||||
|
<parameter name="pageData"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
let provider = this.provider;
|
||||||
|
this.panel.hidden = false;
|
||||||
|
let URLTemplate = provider.markURL;
|
||||||
|
this.pageData = pageData || OpenGraphBuilder.getData(gBrowser);
|
||||||
|
let endpoint = OpenGraphBuilder.generateEndpointURL(URLTemplate, this.pageData);
|
||||||
|
|
||||||
|
// setup listeners
|
||||||
|
this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
|
||||||
|
if (event.target != this.contentDocument)
|
||||||
|
return;
|
||||||
|
this._loading = false;
|
||||||
|
this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
|
||||||
|
// add our resizer after the dom is ready
|
||||||
|
let DynamicResizeWatcher = Cu.import("resource:///modules/Social.jsm", {}).DynamicResizeWatcher;
|
||||||
|
this._dynamicResizer = new DynamicResizeWatcher();
|
||||||
|
this._dynamicResizer.start(this.panel, this.content);
|
||||||
|
// send the opengraph data
|
||||||
|
let evt = this.contentDocument.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent("OpenGraphData", true, true, JSON.stringify(this.pageData));
|
||||||
|
this.contentDocument.documentElement.dispatchEvent(evt);
|
||||||
|
|
||||||
|
let contentWindow = this.contentWindow;
|
||||||
|
let markUpdate = function(event) {
|
||||||
|
// update the annotation based on this event, then update the
|
||||||
|
// icon as well
|
||||||
|
this.isMarked = JSON.parse(event.detail).marked;
|
||||||
|
let uri = Services.io.newURI(this.pageData.url, null, null);
|
||||||
|
if (this.isMarked) {
|
||||||
|
Social.markURI(provider.origin, uri);
|
||||||
|
} else {
|
||||||
|
Social.unmarkURI(provider.origin, uri, () => {
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
contentWindow.addEventListener("socialMarkUpdate", markUpdate);
|
||||||
|
contentWindow.addEventListener("unload", function unload() {
|
||||||
|
contentWindow.removeEventListener("unload", unload);
|
||||||
|
contentWindow.removeEventListener("socialMarkUpdate", markUpdate);
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
this._loading = true;
|
||||||
|
this.setAttribute("src", endpoint);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="markCurrentPage">
|
||||||
|
<parameter name="aOpenPanel"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
// we always set the src on click if it has not been set for this tab,
|
||||||
|
// but we only want to open the panel if it was previously annotated.
|
||||||
|
let openPanel = this.isMarked || aOpenPanel;
|
||||||
|
let src = this.getAttribute("src");
|
||||||
|
if (!src || src == "about:blank") {
|
||||||
|
this.loadPanel();
|
||||||
|
this.isMarked = true;
|
||||||
|
}
|
||||||
|
if (openPanel)
|
||||||
|
this.panel.openPopup(this, "bottomcenter topright", 0, 0, false, false);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="markLink">
|
||||||
|
<parameter name="aUrl"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (!aUrl) {
|
||||||
|
this.markCurrentPage(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// initiated form an external source, such as gContextMenu, where
|
||||||
|
// pageData is passed into us. In this case, we always load the iframe
|
||||||
|
// and show it since the url may not be the browser tab, but an image,
|
||||||
|
// link, etc. inside the page. We also "update" the iframe to the
|
||||||
|
// previous url when it is closed.
|
||||||
|
this.setAttribute("src", "about:blank");
|
||||||
|
this.loadPanel({ url: aUrl });
|
||||||
|
this.isMarked = true;
|
||||||
|
this.panel.openPopup(this, "bottomcenter topright", 0, 0, false, false);
|
||||||
|
this.panel.addEventListener("popuphidden", function _hidden() {
|
||||||
|
this.panel.removeEventListener("popuphidden", _hidden);
|
||||||
|
this.update();
|
||||||
|
}.bind(this), false);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="dispatchPanelEvent">
|
||||||
|
<parameter name="name"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
let evt = this.contentDocument.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent(name, true, true, {});
|
||||||
|
this.contentDocument.documentElement.dispatchEvent(evt);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</implementation>
|
||||||
|
<handlers>
|
||||||
|
<handler event="popupshown"><![CDATA[
|
||||||
|
// because the panel may be preloaded, we need to size the panel when
|
||||||
|
// showing as well as after load
|
||||||
|
let sizeSocialPanelToContent = Cu.import("resource:///modules/Social.jsm", {}).sizeSocialPanelToContent;
|
||||||
|
if (!this._loading && this.contentDocument.readyState == "complete") {
|
||||||
|
this.dispatchPanelEvent("socialFrameShow");
|
||||||
|
sizeSocialPanelToContent(this.panel, this.content);
|
||||||
|
} else {
|
||||||
|
this.content.addEventListener("load", function panelBrowserOnload(e) {
|
||||||
|
this.content.removeEventListener("load", panelBrowserOnload, true);
|
||||||
|
this.dispatchPanelEvent("socialFrameShow");
|
||||||
|
sizeSocialPanelToContent(this.panel, this.content);
|
||||||
|
}.bind(this), true);
|
||||||
|
}
|
||||||
|
]]></handler>
|
||||||
|
<handler event="popuphidden"><![CDATA[
|
||||||
|
this.dispatchPanelEvent("socialFrameHide");
|
||||||
|
]]></handler>
|
||||||
|
<handler event="command"><![CDATA[
|
||||||
|
this.markCurrentPage();
|
||||||
|
]]></handler>
|
||||||
|
<handler event="DOMLinkAdded"><![CDATA[
|
||||||
|
// much of this logic is from DOMLinkHandler in browser.js, this sets
|
||||||
|
// the presence icon for a chat user, we simply use favicon style
|
||||||
|
// updating
|
||||||
|
let link = event.originalTarget;
|
||||||
|
let rel = link.rel && link.rel.toLowerCase();
|
||||||
|
if (!link || !link.ownerDocument || !rel || !link.href)
|
||||||
|
return;
|
||||||
|
if (link.rel.indexOf("icon") < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let uri = DOMLinkHandler.getLinkIconURI(link);
|
||||||
|
if (!uri)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we cannot size the image when we apply it via listStyleImage, so
|
||||||
|
// use the toolbar image
|
||||||
|
this.setAttribute("image", uri.spec);
|
||||||
|
]]></handler>
|
||||||
|
</handlers>
|
||||||
|
</binding>
|
||||||
|
|
||||||
|
</bindings>
|
@ -22,17 +22,20 @@ MOCHITEST_BROWSER_FILES = \
|
|||||||
browser_social_multiprovider.js \
|
browser_social_multiprovider.js \
|
||||||
browser_social_multiworker.js \
|
browser_social_multiworker.js \
|
||||||
browser_social_errorPage.js \
|
browser_social_errorPage.js \
|
||||||
|
browser_social_marks.js \
|
||||||
browser_social_status.js \
|
browser_social_status.js \
|
||||||
browser_social_window.js \
|
browser_social_window.js \
|
||||||
social_activate.html \
|
social_activate.html \
|
||||||
social_activate_iframe.html \
|
social_activate_iframe.html \
|
||||||
browser_share.js \
|
browser_share.js \
|
||||||
social_panel.html \
|
social_panel.html \
|
||||||
social_mark_image.png \
|
social_mark.html \
|
||||||
social_sidebar.html \
|
social_sidebar.html \
|
||||||
social_chat.html \
|
social_chat.html \
|
||||||
social_flyout.html \
|
social_flyout.html \
|
||||||
social_window.html \
|
social_window.html \
|
||||||
social_worker.js \
|
social_worker.js \
|
||||||
share.html \
|
share.html \
|
||||||
|
checked.jpg \
|
||||||
|
unchecked.jpg \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
340
browser/base/content/test/social/browser_social_marks.js
Normal file
340
browser/base/content/test/social/browser_social_marks.js
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
|
||||||
|
|
||||||
|
let manifest = { // builtin provider
|
||||||
|
name: "provider example.com",
|
||||||
|
origin: "https://example.com",
|
||||||
|
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||||
|
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||||
|
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||||
|
};
|
||||||
|
let manifest2 = { // used for testing install
|
||||||
|
name: "provider test1",
|
||||||
|
origin: "https://test1.example.com",
|
||||||
|
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||||
|
markURL: "https://test1.example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
|
||||||
|
markedIcon: "https://test1.example.com/browser/browser/base/content/test/social/unchecked.jpg",
|
||||||
|
unmarkedIcon: "https://test1.example.com/browser/browser/base/content/test/social/checked.jpg",
|
||||||
|
|
||||||
|
iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png",
|
||||||
|
version: 1
|
||||||
|
};
|
||||||
|
let manifest3 = { // used for testing install
|
||||||
|
name: "provider test2",
|
||||||
|
origin: "https://test2.example.com",
|
||||||
|
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||||
|
iconURL: "https://test2.example.com/browser/browser/base/content/test/moz.png",
|
||||||
|
version: 1
|
||||||
|
};
|
||||||
|
function makeMarkProvider(origin) {
|
||||||
|
return { // used for testing install
|
||||||
|
name: "mark provider " + origin,
|
||||||
|
origin: "https://" + origin + ".example.com",
|
||||||
|
workerURL: "https://" + origin + ".example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||||
|
markURL: "https://" + origin + ".example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
|
||||||
|
markedIcon: "https://" + origin + ".example.com/browser/browser/base/content/test/social/unchecked.jpg",
|
||||||
|
unmarkedIcon: "https://" + origin + ".example.com/browser/browser/base/content/test/social/checked.jpg",
|
||||||
|
|
||||||
|
iconURL: "https://" + origin + ".example.com/browser/browser/base/content/test/moz.png",
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openWindowAndWaitForInit(callback) {
|
||||||
|
let topic = "browser-delayed-startup-finished";
|
||||||
|
let w = OpenBrowserWindow();
|
||||||
|
Services.obs.addObserver(function providerSet(subject, topic, data) {
|
||||||
|
Services.obs.removeObserver(providerSet, topic);
|
||||||
|
executeSoon(() => callback(w));
|
||||||
|
}, topic, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
let currentsetAtStart = toolbar.currentSet;
|
||||||
|
runSocialTestWithProvider(manifest, function () {
|
||||||
|
runSocialTests(tests, undefined, undefined, function () {
|
||||||
|
Services.prefs.clearUserPref("social.remote-install.enabled");
|
||||||
|
// just in case the tests failed, clear these here as well
|
||||||
|
Services.prefs.clearUserPref("social.allowMultipleWorkers");
|
||||||
|
Services.prefs.clearUserPref("social.whitelist");
|
||||||
|
|
||||||
|
// This post-test test ensures that a new window maintains the same
|
||||||
|
// toolbar button set as when we started. That means our insert/removal of
|
||||||
|
// persistent id's is working correctly
|
||||||
|
is(currentsetAtStart, toolbar.currentSet, "toolbar currentset unchanged");
|
||||||
|
openWindowAndWaitForInit(function(w1) {
|
||||||
|
checkSocialUI(w1);
|
||||||
|
// Sometimes the new window adds other buttons to currentSet that are
|
||||||
|
// outside the scope of what we're checking. So we verify that all
|
||||||
|
// buttons from startup are in currentSet for a new window, and that the
|
||||||
|
// provider buttons are properly removed. (e.g on try, window-controls
|
||||||
|
// was not present in currentsetAtStart, but present on the second
|
||||||
|
// window)
|
||||||
|
let tb1 = w1.document.getElementById("nav-bar");
|
||||||
|
let startupSet = Set(toolbar.currentSet.split(','));
|
||||||
|
let newSet = Set(tb1.currentSet.split(','));
|
||||||
|
let intersect = Set([x for (x of startupSet) if (newSet.has(x))]);
|
||||||
|
let difference = Set([x for (x of newSet) if (!startupSet.has(x))]);
|
||||||
|
is(startupSet.size, intersect.size, "new window toolbar same as old");
|
||||||
|
// verify that our provider buttons are not in difference
|
||||||
|
let id = SocialMarks._toolbarHelper.idFromOrgin(manifest2.origin);
|
||||||
|
ok(!difference.has(id), "mark button not persisted at end");
|
||||||
|
w1.close();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = {
|
||||||
|
testNoButtonOnInstall: function(next) {
|
||||||
|
// we expect the addon install dialog to appear, we need to accept the
|
||||||
|
// install from the dialog.
|
||||||
|
info("Waiting for install dialog");
|
||||||
|
let panel = document.getElementById("servicesInstall-notification");
|
||||||
|
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
|
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||||
|
info("servicesInstall-notification panel opened");
|
||||||
|
panel.button.click();
|
||||||
|
})
|
||||||
|
|
||||||
|
let id = "social-mark-button-" + manifest3.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
ok(currentset.indexOf(id) < 0, "button is not part of currentset at start");
|
||||||
|
|
||||||
|
let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||||
|
addTab(activationURL, function(tab) {
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
Social.installProvider(doc, manifest3, function(addonManifest) {
|
||||||
|
// enable the provider so we know the button would have appeared
|
||||||
|
SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
|
||||||
|
ok(provider, "provider is installed");
|
||||||
|
currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
ok(currentset.indexOf(id) < 0, "button was not added to currentset");
|
||||||
|
Social.uninstallProvider(manifest3.origin, function() {
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testButtonOnInstall: function(next) {
|
||||||
|
// we expect the addon install dialog to appear, we need to accept the
|
||||||
|
// install from the dialog.
|
||||||
|
info("Waiting for install dialog");
|
||||||
|
let panel = document.getElementById("servicesInstall-notification");
|
||||||
|
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
|
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||||
|
info("servicesInstall-notification panel opened");
|
||||||
|
panel.button.click();
|
||||||
|
})
|
||||||
|
|
||||||
|
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||||
|
addTab(activationURL, function(tab) {
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||||
|
// at this point, we should have a button id in the currentset for our provider
|
||||||
|
let id = "social-mark-button-" + manifest2.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
|
||||||
|
waitForCondition(function() {
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
return currentset.indexOf(id) >= 0;
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
// no longer need the tab
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
next();
|
||||||
|
}, "mark button added to currentset");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testButtonOnEnable: function(next) {
|
||||||
|
// enable the provider now
|
||||||
|
SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
|
||||||
|
ok(provider, "provider is installed");
|
||||||
|
let id = "social-mark-button-" + manifest2.origin;
|
||||||
|
waitForCondition(function() { return document.getElementById(id) },
|
||||||
|
function() {
|
||||||
|
checkSocialUI(window);
|
||||||
|
next();
|
||||||
|
}, "button exists after enabling social");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testMarkPanel: function(next) {
|
||||||
|
// click on panel to open and wait for visibility
|
||||||
|
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||||
|
ok(provider.enabled, "provider is enabled");
|
||||||
|
let id = "social-mark-button-" + provider.origin;
|
||||||
|
let btn = document.getElementById(id)
|
||||||
|
ok(btn, "got a mark button");
|
||||||
|
let port = provider.getWorkerPort();
|
||||||
|
ok(port, "got a port");
|
||||||
|
|
||||||
|
// verify markbutton is disabled when there is no browser url
|
||||||
|
ok(btn.disabled, "button is disabled");
|
||||||
|
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||||
|
addTab(activationURL, function(tab) {
|
||||||
|
ok(!btn.disabled, "button is enabled");
|
||||||
|
port.onmessage = function (e) {
|
||||||
|
let topic = e.data.topic;
|
||||||
|
switch (topic) {
|
||||||
|
case "test-init-done":
|
||||||
|
ok(true, "test-init-done received");
|
||||||
|
ok(provider.profile.userName, "profile was set by test worker");
|
||||||
|
// first click marks the page, second click opens the page. We have to
|
||||||
|
// synthesize so the command event happens
|
||||||
|
EventUtils.synthesizeMouseAtCenter(btn, {});
|
||||||
|
// wait for the button to be marked, click to open panel
|
||||||
|
waitForCondition(function() btn.isMarked, function() {
|
||||||
|
EventUtils.synthesizeMouseAtCenter(btn, {});
|
||||||
|
}, "button is marked");
|
||||||
|
break;
|
||||||
|
case "got-social-panel-visibility":
|
||||||
|
ok(true, "got the panel message " + e.data.result);
|
||||||
|
if (e.data.result == "shown") {
|
||||||
|
// unmark the page via the button in the page
|
||||||
|
let doc = btn.contentDocument;
|
||||||
|
let unmarkBtn = doc.getElementById("unmark");
|
||||||
|
ok(unmarkBtn, "got the panel unmark button");
|
||||||
|
EventUtils.sendMouseEvent({type: "click"}, unmarkBtn, btn.contentWindow);
|
||||||
|
} else {
|
||||||
|
// page should no longer be marked
|
||||||
|
port.close();
|
||||||
|
waitForCondition(function() !btn.isMarked, function() {
|
||||||
|
// after closing the addons tab, verify provider is still installed
|
||||||
|
gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
|
||||||
|
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
|
||||||
|
executeSoon(function () {
|
||||||
|
ok(btn.disabled, "button is disabled");
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
}, "button unmarked");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
port.postMessage({topic: "test-init"});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testButtonOnDisable: function(next) {
|
||||||
|
// enable the provider now
|
||||||
|
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||||
|
ok(provider, "provider is installed");
|
||||||
|
SocialService.removeProvider(manifest2.origin, function() {
|
||||||
|
let id = "social-mark-button-" + manifest2.origin;
|
||||||
|
waitForCondition(function() { return !document.getElementById(id) },
|
||||||
|
function() {
|
||||||
|
checkSocialUI(window);
|
||||||
|
next();
|
||||||
|
}, "button does not exist after disabling the provider");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testButtonOnUninstall: function(next) {
|
||||||
|
Social.uninstallProvider(manifest2.origin, function() {
|
||||||
|
// test that the button is no longer persisted
|
||||||
|
let id = "social-mark-button-" + manifest2.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
is(currentset.indexOf(id), -1, "button no longer in currentset");
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testContextSubmenu: function(next) {
|
||||||
|
// install 4 providers to test that the menu's are added as submenus
|
||||||
|
let manifests = [
|
||||||
|
makeMarkProvider("sub1.test1"),
|
||||||
|
makeMarkProvider("sub2.test1"),
|
||||||
|
makeMarkProvider("sub1.test2"),
|
||||||
|
makeMarkProvider("sub2.test2")
|
||||||
|
];
|
||||||
|
let installed = [];
|
||||||
|
let markLinkMenu = document.getElementById("context-marklinkMenu").firstChild;
|
||||||
|
let markPageMenu = document.getElementById("context-markpageMenu").firstChild;
|
||||||
|
|
||||||
|
function addProviders(callback) {
|
||||||
|
let manifest = manifests.pop();
|
||||||
|
if (!manifest) {
|
||||||
|
info("INSTALLATION FINISHED");
|
||||||
|
executeSoon(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info("INSTALLING " + manifest.origin);
|
||||||
|
let panel = document.getElementById("servicesInstall-notification");
|
||||||
|
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||||
|
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||||
|
info("servicesInstall-notification panel opened");
|
||||||
|
panel.button.click();
|
||||||
|
})
|
||||||
|
|
||||||
|
let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||||
|
let id = "social-mark-button-" + manifest.origin;
|
||||||
|
let toolbar = document.getElementById("nav-bar");
|
||||||
|
addTab(activationURL, function(tab) {
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
Social.installProvider(doc, manifest, function(addonManifest) {
|
||||||
|
|
||||||
|
waitForCondition(function() {
|
||||||
|
let currentset = toolbar.getAttribute("currentset").split(',');
|
||||||
|
return currentset.indexOf(id) >= 0;
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
// enable the provider so we know the button would have appeared
|
||||||
|
SocialService.addBuiltinProvider(manifest.origin, function(provider) {
|
||||||
|
waitForCondition(function() { return document.getElementById(id) },
|
||||||
|
function() {
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
installed.push(manifest.origin);
|
||||||
|
// checkSocialUI will properly check where the menus are located
|
||||||
|
checkSocialUI(window);
|
||||||
|
executeSoon(function() {
|
||||||
|
addProviders(callback);
|
||||||
|
});
|
||||||
|
}, "button exists after enabling social");
|
||||||
|
});
|
||||||
|
}, "mark button added to currentset");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeProviders(callback) {
|
||||||
|
let origin = installed.pop();
|
||||||
|
if (!origin) {
|
||||||
|
executeSoon(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Social.uninstallProvider(origin, function(provider) {
|
||||||
|
executeSoon(function() {
|
||||||
|
removeProviders(callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addProviders(function() {
|
||||||
|
removeProviders(function() {
|
||||||
|
is(SocialMarks.getProviders().length, 0, "mark providers removed");
|
||||||
|
is(markLinkMenu.childNodes.length, 0, "marklink menu ok");
|
||||||
|
is(markPageMenu.childNodes.length, 0, "markpage menu ok");
|
||||||
|
checkSocialUI(window);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
BIN
browser/base/content/test/social/checked.jpg
Normal file
BIN
browser/base/content/test/social/checked.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 785 B |
@ -247,6 +247,42 @@ function checkSocialUI(win) {
|
|||||||
if (provider) {
|
if (provider) {
|
||||||
for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
|
for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
|
||||||
_is(document.getElementById(id).getAttribute("label"), Social.provider.name, "element has the provider name");
|
_is(document.getElementById(id).getAttribute("label"), Social.provider.name, "element has the provider name");
|
||||||
|
|
||||||
|
let contextMenus = [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
id: "context-marklinkMenu",
|
||||||
|
label: "social.marklink.label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "page",
|
||||||
|
id: "context-markpageMenu",
|
||||||
|
label: "social.markpage.label"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let c of contextMenus) {
|
||||||
|
let leMenu = document.getElementById(c.id);
|
||||||
|
let parent, menus;
|
||||||
|
let markProviders = SocialMarks.getProviders();
|
||||||
|
if (markProviders.length > SocialMarks.MENU_LIMIT) {
|
||||||
|
// menus should be in a submenu, not in the top level of the context menu
|
||||||
|
parent = leMenu.firstChild;
|
||||||
|
menus = document.getElementsByClassName("context-mark" + c.type);
|
||||||
|
_is(menus.length, 0, "menu's are not in main context menu\n");
|
||||||
|
menus = parent.childNodes;
|
||||||
|
_is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
|
||||||
|
} else {
|
||||||
|
// menus should be in the top level of the context menu, not in a submenu
|
||||||
|
parent = leMenu.parentNode;
|
||||||
|
menus = document.getElementsByClassName("context-mark" + c.type);
|
||||||
|
_is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
|
||||||
|
menus = leMenu.firstChild.childNodes;
|
||||||
|
_is(menus.length, 0, "menu's are not in context submenu\n");
|
||||||
|
}
|
||||||
|
for (let m of menus)
|
||||||
|
_is(m.parentNode, parent, "menu has correct parent");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// and for good measure, check all the social commands.
|
// and for good measure, check all the social commands.
|
||||||
|
49
browser/base/content/test/social/social_mark.html
Normal file
49
browser/base/content/test/social/social_mark.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link id="siteicon" rel="icon" href="./icon.png"/>
|
||||||
|
<title>Demo Mark Window</title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.addEventListener("socialFrameShow", function(e) {
|
||||||
|
var port = navigator.mozSocial.getWorker().port;
|
||||||
|
port.postMessage({topic: "status-panel-visibility", result: "shown"});
|
||||||
|
}, false);
|
||||||
|
window.addEventListener("socialFrameHide", function(e) {
|
||||||
|
var port = navigator.mozSocial.getWorker().port;
|
||||||
|
port.postMessage({topic: "status-panel-visibility", result: "hidden"});
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
function updateTextNode(parent, text) {
|
||||||
|
var textNode = parent.childNodes[0];
|
||||||
|
if (textNode)
|
||||||
|
parent.removeChild(textNode);
|
||||||
|
textNode = document.createTextNode(text);
|
||||||
|
parent.appendChild(textNode);
|
||||||
|
}
|
||||||
|
function onLoad() {
|
||||||
|
updateTextNode(document.getElementById("shared"), location.search);
|
||||||
|
socialMarkUpdate(true);
|
||||||
|
}
|
||||||
|
function socialMarkUpdate(isMarked) {
|
||||||
|
var evt = document.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent("socialMarkUpdate", true, true, JSON.stringify({marked: isMarked}));
|
||||||
|
document.documentElement.dispatchEvent(evt);
|
||||||
|
}
|
||||||
|
var shareData;
|
||||||
|
addEventListener("OpenGraphData", function(e) {
|
||||||
|
shareData = JSON.parse(e.detail);
|
||||||
|
updateTextNode(document.getElementById("shared"), shareData.url);
|
||||||
|
socialMarkUpdate(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="onLoad()">
|
||||||
|
<div id="content">
|
||||||
|
<h3>This window shows the mark data</h3>
|
||||||
|
<div>Page Marked: <div id="shared" class="textbox"></div></div>
|
||||||
|
<button id="unmark" onclick="socialMarkUpdate(false); window.close()">Unmark</button>
|
||||||
|
<button onclick="window.close();">Close</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 934 B |
BIN
browser/base/content/test/social/unchecked.jpg
Normal file
BIN
browser/base/content/test/social/unchecked.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 779 B |
@ -121,6 +121,7 @@ browser.jar:
|
|||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
content/browser/win6BrowserOverlay.xul (content/win6BrowserOverlay.xul)
|
content/browser/win6BrowserOverlay.xul (content/win6BrowserOverlay.xul)
|
||||||
#endif
|
#endif
|
||||||
|
content/browser/socialmarks.xml (content/socialmarks.xml)
|
||||||
content/browser/socialchat.xml (content/socialchat.xml)
|
content/browser/socialchat.xml (content/socialchat.xml)
|
||||||
# the following files are browser-specific overrides
|
# the following files are browser-specific overrides
|
||||||
* content/browser/license.html (/toolkit/content/license.html)
|
* content/browser/license.html (/toolkit/content/license.html)
|
||||||
|
@ -645,6 +645,11 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
|||||||
<!ENTITY social.chatBar.label "Focus chats">
|
<!ENTITY social.chatBar.label "Focus chats">
|
||||||
<!ENTITY social.chatBar.accesskey "c">
|
<!ENTITY social.chatBar.accesskey "c">
|
||||||
|
|
||||||
|
<!ENTITY social.markpage.accesskey "P">
|
||||||
|
<!ENTITY social.markpageMenu.label "Save Page To…">
|
||||||
|
<!ENTITY social.marklink.accesskey "L">
|
||||||
|
<!ENTITY social.marklinkMenu.label "Save Link To…">
|
||||||
|
|
||||||
<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
|
<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
|
||||||
<!ENTITY getUserMedia.selectCamera.accesskey "C">
|
<!ENTITY getUserMedia.selectCamera.accesskey "C">
|
||||||
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
|
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
|
||||||
|
@ -429,6 +429,11 @@ social.turnOff.accesskey=T
|
|||||||
social.turnOn.label=Turn on %S
|
social.turnOn.label=Turn on %S
|
||||||
social.turnOn.accesskey=T
|
social.turnOn.accesskey=T
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (social.markpage.label): %S is the name of the social provider
|
||||||
|
social.markpage.label=Save Page to %S
|
||||||
|
# LOCALIZATION NOTE (social.marklink.label): %S is the name of the social provider
|
||||||
|
social.marklink.label=Save Link to %S
|
||||||
|
|
||||||
# LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
|
# LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
|
||||||
social.error.message=%1$S is unable to connect with %2$S right now.
|
social.error.message=%1$S is unable to connect with %2$S right now.
|
||||||
social.error.tryAgain.label=Try Again
|
social.error.tryAgain.label=Try Again
|
||||||
|
@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["Social", "OpenGraphBuilder"];
|
this.EXPORTED_SYMBOLS = ["Social", "OpenGraphBuilder", "DynamicResizeWatcher", "sizeSocialPanelToContent"];
|
||||||
|
|
||||||
const Ci = Components.interfaces;
|
const Ci = Components.interfaces;
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
// The minimum sizes for the auto-resize panel code.
|
||||||
|
const PANEL_MIN_HEIGHT = 100;
|
||||||
|
const PANEL_MIN_WIDTH = 330;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
@ -303,45 +307,25 @@ this.Social = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Page Marking functionality
|
// Page Marking functionality
|
||||||
_getMarkablePageUrl: function Social_getMarkablePageUrl(aURI) {
|
isURIMarked: function(origin, aURI, aCallback) {
|
||||||
let uri = aURI.clone();
|
|
||||||
try {
|
|
||||||
// Setting userPass on about:config throws.
|
|
||||||
uri.userPass = "";
|
|
||||||
} catch (e) {}
|
|
||||||
return uri.spec;
|
|
||||||
},
|
|
||||||
|
|
||||||
isURIMarked: function(aURI, aCallback) {
|
|
||||||
promiseGetAnnotation(aURI).then(function(val) {
|
promiseGetAnnotation(aURI).then(function(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
let providerList = JSON.parse(val);
|
let providerList = JSON.parse(val);
|
||||||
val = providerList.indexOf(this.provider.origin) >= 0;
|
val = providerList.indexOf(origin) >= 0;
|
||||||
}
|
}
|
||||||
aCallback(!!val);
|
aCallback(!!val);
|
||||||
}.bind(this));
|
}).then(null, Cu.reportError);
|
||||||
},
|
},
|
||||||
|
|
||||||
markURI: function(aURI, aCallback) {
|
markURI: function(origin, aURI, aCallback) {
|
||||||
// this should not be called if this.provider or the port is null
|
|
||||||
if (!this.provider) {
|
|
||||||
Cu.reportError("Can't mark a page when no provider is current");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let port = this.provider.getWorkerPort();
|
|
||||||
if (!port) {
|
|
||||||
Cu.reportError("Can't mark page as no provider port is available");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update or set our annotation
|
// update or set our annotation
|
||||||
promiseGetAnnotation(aURI).then(function(val) {
|
promiseGetAnnotation(aURI).then(function(val) {
|
||||||
|
|
||||||
let providerList = val ? JSON.parse(val) : [];
|
let providerList = val ? JSON.parse(val) : [];
|
||||||
let marked = providerList.indexOf(this.provider.origin) >= 0;
|
let marked = providerList.indexOf(origin) >= 0;
|
||||||
if (marked)
|
if (marked)
|
||||||
return;
|
return;
|
||||||
providerList.push(this.provider.origin);
|
providerList.push(origin);
|
||||||
// we allow marking links in a page that may not have been visited yet.
|
// we allow marking links in a page that may not have been visited yet.
|
||||||
// make sure there is a history entry for the uri, then annotate it.
|
// make sure there is a history entry for the uri, then annotate it.
|
||||||
let place = {
|
let place = {
|
||||||
@ -355,52 +339,30 @@ this.Social = {
|
|||||||
handleError: function () Cu.reportError("couldn't update history for socialmark annotation"),
|
handleError: function () Cu.reportError("couldn't update history for socialmark annotation"),
|
||||||
handleResult: function () {},
|
handleResult: function () {},
|
||||||
handleCompletion: function () {
|
handleCompletion: function () {
|
||||||
promiseSetAnnotation(aURI, providerList).then();
|
promiseSetAnnotation(aURI, providerList).then(function() {
|
||||||
// post to the provider
|
if (aCallback)
|
||||||
let url = this._getMarkablePageUrl(aURI);
|
schedule(function() { aCallback(true); } );
|
||||||
port.postMessage({
|
}).then(null, Cu.reportError);
|
||||||
topic: "social.page-mark",
|
}
|
||||||
data: { url: url, 'marked': true }
|
|
||||||
});
|
|
||||||
port.close();
|
|
||||||
if (aCallback)
|
|
||||||
schedule(function() { aCallback(true); } );
|
|
||||||
}.bind(this)
|
|
||||||
});
|
});
|
||||||
}.bind(this));
|
}).then(null, Cu.reportError);
|
||||||
},
|
},
|
||||||
|
|
||||||
unmarkURI: function(aURI, aCallback) {
|
|
||||||
// this should not be called if this.provider or the port is null
|
|
||||||
if (!this.provider) {
|
|
||||||
Cu.reportError("Can't mark a page when no provider is current");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let port = this.provider.getWorkerPort();
|
|
||||||
if (!port) {
|
|
||||||
Cu.reportError("Can't mark page as no provider port is available");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
unmarkURI: function(origin, aURI, aCallback) {
|
||||||
|
// this should not be called if this.provider or the port is null
|
||||||
// set our annotation
|
// set our annotation
|
||||||
promiseGetAnnotation(aURI).then(function(val) {
|
promiseGetAnnotation(aURI).then(function(val) {
|
||||||
let providerList = val ? JSON.parse(val) : [];
|
let providerList = val ? JSON.parse(val) : [];
|
||||||
let marked = providerList.indexOf(this.provider.origin) >= 0;
|
let marked = providerList.indexOf(origin) >= 0;
|
||||||
if (marked) {
|
if (marked) {
|
||||||
// remove the annotation
|
// remove the annotation
|
||||||
providerList.splice(providerList.indexOf(this.provider.origin), 1);
|
providerList.splice(providerList.indexOf(origin), 1);
|
||||||
promiseSetAnnotation(aURI, providerList).then();
|
promiseSetAnnotation(aURI, providerList).then(function() {
|
||||||
|
if (aCallback)
|
||||||
|
schedule(function() { aCallback(false); } );
|
||||||
|
}).then(null, Cu.reportError);
|
||||||
}
|
}
|
||||||
// post to the provider regardless
|
}).then(null, Cu.reportError);
|
||||||
let url = this._getMarkablePageUrl(aURI);
|
|
||||||
port.postMessage({
|
|
||||||
topic: "social.page-mark",
|
|
||||||
data: { url: url, 'marked': false }
|
|
||||||
});
|
|
||||||
port.close();
|
|
||||||
if (aCallback)
|
|
||||||
schedule(function() { aCallback(false); } );
|
|
||||||
}.bind(this));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setErrorListener: function(iframe, errorHandler) {
|
setErrorListener: function(iframe, errorHandler) {
|
||||||
@ -481,7 +443,108 @@ SocialErrorListener.prototype = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function sizeSocialPanelToContent(panel, iframe) {
|
||||||
|
let doc = iframe.contentDocument;
|
||||||
|
if (!doc || !doc.body) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We need an element to use for sizing our panel. See if the body defines
|
||||||
|
// an id for that element, otherwise use the body itself.
|
||||||
|
let body = doc.body;
|
||||||
|
let bodyId = body.getAttribute("contentid");
|
||||||
|
if (bodyId) {
|
||||||
|
body = doc.getElementById(bodyId) || doc.body;
|
||||||
|
}
|
||||||
|
// offsetHeight/Width don't include margins, so account for that.
|
||||||
|
let cs = doc.defaultView.getComputedStyle(body);
|
||||||
|
let width = PANEL_MIN_WIDTH;
|
||||||
|
let height = PANEL_MIN_HEIGHT;
|
||||||
|
// if the panel is preloaded prior to being shown, cs will be null. in that
|
||||||
|
// case use the minimum size for the panel until it is shown.
|
||||||
|
if (cs) {
|
||||||
|
let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
|
||||||
|
height = Math.max(computedHeight, height);
|
||||||
|
let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
|
||||||
|
width = Math.max(computedWidth, width);
|
||||||
|
}
|
||||||
|
iframe.style.width = width + "px";
|
||||||
|
iframe.style.height = height + "px";
|
||||||
|
// since we do not use panel.sizeTo, we need to adjust the arrow ourselves
|
||||||
|
if (panel.state == "open")
|
||||||
|
panel.adjustArrowPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
function DynamicResizeWatcher() {
|
||||||
|
this._mutationObserver = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicResizeWatcher.prototype = {
|
||||||
|
start: function DynamicResizeWatcher_start(panel, iframe) {
|
||||||
|
this.stop(); // just in case...
|
||||||
|
let doc = iframe.contentDocument;
|
||||||
|
this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
|
||||||
|
sizeSocialPanelToContent(panel, iframe);
|
||||||
|
});
|
||||||
|
// Observe anything that causes the size to change.
|
||||||
|
let config = {attributes: true, characterData: true, childList: true, subtree: true};
|
||||||
|
this._mutationObserver.observe(doc, config);
|
||||||
|
// and since this may be setup after the load event has fired we do an
|
||||||
|
// initial resize now.
|
||||||
|
sizeSocialPanelToContent(panel, iframe);
|
||||||
|
},
|
||||||
|
stop: function DynamicResizeWatcher_stop() {
|
||||||
|
if (this._mutationObserver) {
|
||||||
|
try {
|
||||||
|
this._mutationObserver.disconnect();
|
||||||
|
} catch (ex) {
|
||||||
|
// may get "TypeError: can't access dead object" which seems strange,
|
||||||
|
// but doesn't seem to indicate a real problem, so ignore it...
|
||||||
|
}
|
||||||
|
this._mutationObserver = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.OpenGraphBuilder = {
|
this.OpenGraphBuilder = {
|
||||||
|
generateEndpointURL: function(URLTemplate, pageData) {
|
||||||
|
// support for existing oexchange style endpoints by supporting their
|
||||||
|
// querystring arguments. parse the query string template and do
|
||||||
|
// replacements where necessary the query names may be different than ours,
|
||||||
|
// so we could see u=%{url} or url=%{url}
|
||||||
|
let [endpointURL, queryString] = URLTemplate.split("?");
|
||||||
|
let query = {};
|
||||||
|
if (queryString) {
|
||||||
|
queryString.split('&').forEach(function (val) {
|
||||||
|
let [name, value] = val.split('=');
|
||||||
|
let p = /%\{(.+)\}/.exec(value);
|
||||||
|
if (!p) {
|
||||||
|
// preserve non-template query vars
|
||||||
|
query[name] = value;
|
||||||
|
} else if (pageData[p[1]]) {
|
||||||
|
query[name] = pageData[p[1]];
|
||||||
|
} else if (p[1] == "body") {
|
||||||
|
// build a body for emailers
|
||||||
|
let body = "";
|
||||||
|
if (pageData.title)
|
||||||
|
body += pageData.title + "\n\n";
|
||||||
|
if (pageData.description)
|
||||||
|
body += pageData.description + "\n\n";
|
||||||
|
if (pageData.text)
|
||||||
|
body += pageData.text + "\n\n";
|
||||||
|
body += pageData.url;
|
||||||
|
query["body"] = body;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var str = [];
|
||||||
|
for (let p in query)
|
||||||
|
str.push(p + "=" + encodeURIComponent(query[p]));
|
||||||
|
if (str.length)
|
||||||
|
endpointURL = endpointURL + "?" + str.join("&");
|
||||||
|
return endpointURL;
|
||||||
|
},
|
||||||
|
|
||||||
getData: function(browser) {
|
getData: function(browser) {
|
||||||
let res = {
|
let res = {
|
||||||
url: this._validateURL(browser, browser.currentURI.spec),
|
url: this._validateURL(browser, browser.currentURI.spec),
|
||||||
|
@ -230,10 +230,10 @@ function attachToWindow(provider, targetWindow) {
|
|||||||
.QueryInterface(Ci.nsIDocShell)
|
.QueryInterface(Ci.nsIDocShell)
|
||||||
.chromeEventHandler;
|
.chromeEventHandler;
|
||||||
while (elt) {
|
while (elt) {
|
||||||
if (elt.nodeName == "panel") {
|
if (elt.localName == "panel") {
|
||||||
elt.hidePopup();
|
elt.hidePopup();
|
||||||
break;
|
break;
|
||||||
} else if (elt.nodeName == "chatbox") {
|
} else if (elt.localName == "chatbox") {
|
||||||
elt.close();
|
elt.close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,7 @@ this.SocialService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_manifestFromData: function(type, data, principal) {
|
_manifestFromData: function(type, data, principal) {
|
||||||
let sameOriginRequired = ['workerURL', 'sidebarURL', 'shareURL', 'statusURL'];
|
let sameOriginRequired = ['workerURL', 'sidebarURL', 'shareURL', 'statusURL', 'markURL'];
|
||||||
|
|
||||||
if (type == 'directory') {
|
if (type == 'directory') {
|
||||||
// directory provided manifests must have origin in manifest, use that
|
// directory provided manifests must have origin in manifest, use that
|
||||||
@ -728,6 +728,9 @@ function SocialProvider(input) {
|
|||||||
this.sidebarURL = input.sidebarURL;
|
this.sidebarURL = input.sidebarURL;
|
||||||
this.shareURL = input.shareURL;
|
this.shareURL = input.shareURL;
|
||||||
this.statusURL = input.statusURL;
|
this.statusURL = input.statusURL;
|
||||||
|
this.markURL = input.markURL;
|
||||||
|
this.markedIcon = input.markedIcon;
|
||||||
|
this.unmarkedIcon = input.unmarkedIcon;
|
||||||
this.origin = input.origin;
|
this.origin = input.origin;
|
||||||
let originUri = Services.io.newURI(input.origin, null, null);
|
let originUri = Services.io.newURI(input.origin, null, null);
|
||||||
this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
|
||||||
|
Loading…
Reference in New Issue
Block a user