Bug 926417 add microdata support for share/marks, r=markh, r=felipe

This commit is contained in:
Shane Caraveo 2014-07-11 08:50:34 -07:00
Родитель 01345e59a4
Коммит a69c426a8e
9 изменённых файлов: 253 добавлений и 14 удалений

Просмотреть файл

@ -600,7 +600,7 @@ SocialShare = {
sizeSocialPanelToContent(this.panel, iframe); sizeSocialPanelToContent(this.panel, iframe);
}, },
sharePage: function(providerOrigin, graphData) { sharePage: function(providerOrigin, graphData, target) {
// if providerOrigin is undefined, we use the last-used provider, or the // if providerOrigin is undefined, we use the last-used provider, or the
// current/default provider. The provider selection in the share panel // current/default provider. The provider selection in the share panel
// will call sharePage with an origin for us to switch to. // will call sharePage with an origin for us to switch to.
@ -619,7 +619,8 @@ SocialShare = {
// in mozSocial API, or via nsContentMenu calls. If it is present, it MUST // 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 // define at least url. If it is undefined, we're sharing the current url in
// the browser tab. // the browser tab.
let sharedURI = graphData ? Services.io.newURI(graphData.url, null, null) : let pageData = graphData ? graphData : this.currentShare;
let sharedURI = pageData ? Services.io.newURI(pageData.url, null, null) :
gBrowser.currentURI; gBrowser.currentURI;
if (!this.canSharePage(sharedURI)) if (!this.canSharePage(sharedURI))
return; return;
@ -628,7 +629,6 @@ SocialShare = {
// endpoints (e.g. oexchange) that do not support additional // endpoints (e.g. oexchange) that do not support additional
// socialapi functionality. One tweak is that we shoot an event // socialapi functionality. One tweak is that we shoot an event
// containing the open graph data. // containing the open graph data.
let pageData = graphData ? graphData : this.currentShare;
if (!pageData || sharedURI == gBrowser.currentURI) { if (!pageData || sharedURI == gBrowser.currentURI) {
pageData = OpenGraphBuilder.getData(gBrowser); pageData = OpenGraphBuilder.getData(gBrowser);
if (graphData) { if (graphData) {
@ -638,6 +638,10 @@ SocialShare = {
} }
} }
} }
// if this is a share of a selected item, get any microdata
if (!pageData.microdata && target) {
pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
}
this.currentShare = pageData; this.currentShare = pageData;
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData); let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
@ -1353,10 +1357,10 @@ SocialMarks = {
return this._toolbarHelper; return this._toolbarHelper;
}, },
markLink: function(aOrigin, aUrl) { markLink: function(aOrigin, aUrl, aTarget) {
// find the button for this provider, and open it // find the button for this provider, and open it
let id = this._toolbarHelper.idFromOrigin(aOrigin); let id = this._toolbarHelper.idFromOrigin(aOrigin);
document.getElementById(id).markLink(aUrl); document.getElementById(id).markLink(aUrl, aTarget);
} }
}; };

Просмотреть файл

@ -1591,22 +1591,22 @@ nsContextMenu.prototype = {
}, },
markLink: function CM_markLink(origin) { markLink: function CM_markLink(origin) {
// send link to social, if it is the page url linkURI will be null // send link to social, if it is the page url linkURI will be null
SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null); SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null, this.target);
}, },
shareLink: function CM_shareLink() { shareLink: function CM_shareLink() {
SocialShare.sharePage(null, { url: this.linkURI.spec }); SocialShare.sharePage(null, { url: this.linkURI.spec }, this.target);
}, },
shareImage: function CM_shareImage() { shareImage: function CM_shareImage() {
SocialShare.sharePage(null, { url: this.imageURL, previews: [ this.mediaURL ] }); SocialShare.sharePage(null, { url: this.imageURL, previews: [ this.mediaURL ] }, this.target);
}, },
shareVideo: function CM_shareVideo() { shareVideo: function CM_shareVideo() {
SocialShare.sharePage(null, { url: this.mediaURL, source: this.mediaURL }); SocialShare.sharePage(null, { url: this.mediaURL, source: this.mediaURL }, this.target);
}, },
shareSelect: function CM_shareSelect(selection) { shareSelect: function CM_shareSelect(selection) {
SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: selection }); SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: selection }, this.target);
}, },
savePageAs: function CM_savePageAs() { savePageAs: function CM_savePageAs() {

Просмотреть файл

@ -147,6 +147,7 @@
<method name="loadPanel"> <method name="loadPanel">
<parameter name="pageData"/> <parameter name="pageData"/>
<parameter name="target"/>
<body><![CDATA[ <body><![CDATA[
let provider = this.provider; let provider = this.provider;
let panel = this.panel; let panel = this.panel;
@ -157,7 +158,13 @@
panel.appendChild(this.content); panel.appendChild(this.content);
let URLTemplate = provider.markURL; let URLTemplate = provider.markURL;
this.pageData = pageData || OpenGraphBuilder.getData(gBrowser); pageData = pageData || OpenGraphBuilder.getData(gBrowser);
// if this is a share of a selected item, get any microdata
if (!pageData.microdata && target) {
pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
}
this.pageData = pageData;
let endpoint = OpenGraphBuilder.generateEndpointURL(URLTemplate, this.pageData); let endpoint = OpenGraphBuilder.generateEndpointURL(URLTemplate, this.pageData);
// setup listeners // setup listeners
@ -248,6 +255,7 @@
<method name="markLink"> <method name="markLink">
<parameter name="aUrl"/> <parameter name="aUrl"/>
<parameter name="aTarget"/>
<body><![CDATA[ <body><![CDATA[
if (!aUrl) { if (!aUrl) {
this.markCurrentPage(true); this.markCurrentPage(true);
@ -259,7 +267,7 @@
// link, etc. inside the page. We also "update" the iframe to the // link, etc. inside the page. We also "update" the iframe to the
// previous url when it is closed. // previous url when it is closed.
this.content.setAttribute("src", "about:blank"); this.content.setAttribute("src", "about:blank");
this.loadPanel({ url: aUrl }); this.loadPanel({ url: aUrl }, aTarget);
this.openPanel(true); this.openPanel(true);
]]></body> ]]></body>
</method> </method>

Просмотреть файл

@ -9,6 +9,7 @@ support-files =
opengraph/shortlink_linkrel.html opengraph/shortlink_linkrel.html
opengraph/shorturl_link.html opengraph/shorturl_link.html
opengraph/shorturl_linkrel.html opengraph/shorturl_linkrel.html
microdata.html
share.html share.html
social_activate.html social_activate.html
social_activate_iframe.html social_activate_iframe.html

Просмотреть файл

@ -170,5 +170,76 @@ var tests = {
} }
port.postMessage({topic: "test-init"}); port.postMessage({topic: "test-init"});
executeSoon(runOneTest); executeSoon(runOneTest);
},
testShareMicrodata: function(next) {
SocialService.addProvider(manifest, function(provider) {
let port = provider.getWorkerPort();
let target, testTab;
let expecting = JSON.stringify({
"url": "https://example.com/browser/browser/base/content/test/social/microdata.html",
"title": "My Blog",
"previews": [],
"microdata": {
"items": [{
"types": ["http://schema.org/BlogPosting"],
"properties": {
"headline": ["Progress report"],
"datePublished": ["2013-08-29"],
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html?comments=0"],
"comment": [{
"types": ["http://schema.org/UserComments"],
"properties": {
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c1"],
"creator": [{
"types": ["http://schema.org/Person"],
"properties": {
"name": ["Greg"]
}
}
],
"commentTime": ["2013-08-29"]
}
}, {
"types": ["http://schema.org/UserComments"],
"properties": {
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c2"],
"creator": [{
"types": ["http://schema.org/Person"],
"properties": {
"name": ["Charlotte"]
}
}
],
"commentTime": ["2013-08-29"]
}
}
]
}
}
]
}
});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-share-data-message":
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
gBrowser.removeTab(testTab);
SocialService.removeProvider(manifest.origin, next);
break;
}
}
port.postMessage({topic: "test-init"});
let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
addTab(url, function(tab) {
testTab = tab;
let doc = tab.linkedBrowser.contentDocument;
target = doc.getElementById("simple-hcard");
SocialShare.sharePage(manifest.origin, null, target);
});
});
} }
} }

Просмотреть файл

@ -280,6 +280,56 @@ var tests = {
}); });
}, },
testMarkMicrodata: function(next) {
let provider = Social._getProviderFromOrigin(manifest2.origin);
let port = provider.getWorkerPort();
let target, testTab;
// browser_share tests microdata on the full page, this is testing a
// specific target element.
let expecting = JSON.stringify({
"url": "https://example.com/browser/browser/base/content/test/social/microdata.html",
"microdata": {
"items": [{
"types": ["http://schema.org/UserComments"],
"properties": {
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c2"],
"creator": [{
"types": ["http://schema.org/Person"],
"properties": {
"name": ["Charlotte"]
}
}
],
"commentTime": ["2013-08-29"]
}
}
]
}
});
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "got-share-data-message":
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
gBrowser.removeTab(testTab);
port.close();
next();
break;
}
}
port.postMessage({topic: "test-init"});
let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
addTab(url, function(tab) {
testTab = tab;
let doc = tab.linkedBrowser.contentDocument;
target = doc.getElementById("test-comment");
SocialMarks.markLink(manifest2.origin, url, target);
});
},
testButtonOnDisable: function(next) { testButtonOnDisable: function(next) {
// enable the provider now // enable the provider now
let provider = Social._getProviderFromOrigin(manifest2.origin); let provider = Social._getProviderFromOrigin(manifest2.origin);

Просмотреть файл

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<head>
<title>My Blog</title>
</head>
<body>
<article itemscope itemtype="http://schema.org/BlogPosting">
<header>
<h1 itemprop="headline">Progress report</h1>
<p><time itemprop="datePublished" datetime="2013-08-29">today</time></p>
<link itemprop="url" href="?comments=0">
</header>
<p>All in all, he's doing well with his swim lessons. The biggest thing was he had trouble
putting his head in, but we got it down.</p>
<section>
<h1>Comments</h1>
<article itemprop="comment" itemscope itemtype="http://schema.org/UserComments" id="c1">
<link itemprop="url" href="#c1">
<footer>
<p>Posted by: <span itemprop="creator" itemscope itemtype="http://schema.org/Person">
<span itemprop="name">Greg</span>
</span></p>
<p><time itemprop="commentTime" datetime="2013-08-29">15 minutes ago</time></p>
</footer>
<p>Ha!</p>
</article>
<article id="test-comment" itemprop="comment" itemscope itemtype="http://schema.org/UserComments" id="c2">
<link itemprop="url" href="#c2">
<footer>
<p>Posted by: <span itemprop="creator" itemscope itemtype="http://schema.org/Person">
<span itemprop="name">Charlotte</span>
</span></p>
<p><time itemprop="commentTime" datetime="2013-08-29">5 minutes ago</time></p>
</footer>
<p>When you say "we got it down"...</p>
</article>
</section>
</article>
</body>

Просмотреть файл

@ -34,6 +34,8 @@
shareData = JSON.parse(e.detail); shareData = JSON.parse(e.detail);
updateTextNode(document.getElementById("shared"), shareData.url); updateTextNode(document.getElementById("shared"), shareData.url);
socialMarkUpdate(true); socialMarkUpdate(true);
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "share-data-message", result: shareData});
}); });
</script> </script>
</head> </head>

Просмотреть файл

@ -498,7 +498,7 @@ this.OpenGraphBuilder = {
return endpointURL; return endpointURL;
}, },
getData: function(browser) { getData: function(browser, target) {
let res = { let res = {
url: this._validateURL(browser, browser.currentURI.spec), url: this._validateURL(browser, browser.currentURI.spec),
title: browser.contentDocument.title, title: browser.contentDocument.title,
@ -507,9 +507,14 @@ this.OpenGraphBuilder = {
this._getMetaData(browser, res); this._getMetaData(browser, res);
this._getLinkData(browser, res); this._getLinkData(browser, res);
this._getPageData(browser, res); this._getPageData(browser, res);
res.microdata = this.getMicrodata(browser, target);
return res; return res;
}, },
getMicrodata: function (browser, target) {
return getMicrodata(browser.contentDocument, target);
},
_getMetaData: function(browser, o) { _getMetaData: function(browser, o) {
// query for standardized meta data // query for standardized meta data
let els = browser.contentDocument let els = browser.contentDocument
@ -522,7 +527,14 @@ this.OpenGraphBuilder = {
if (!value) if (!value)
continue; continue;
value = unescapeService.unescape(value.trim()); value = unescapeService.unescape(value.trim());
switch (el.getAttribute("property") || el.getAttribute("name")) { let key = el.getAttribute("property") || el.getAttribute("name");
if (!key)
continue;
// There are a wide array of possible meta tags, expressing articles,
// products, etc. so all meta tags are passed through but we touch up the
// most common attributes.
o[key] = value;
switch (key) {
case "title": case "title":
case "og:title": case "og:title":
o.title = value; o.title = value;
@ -577,6 +589,19 @@ this.OpenGraphBuilder = {
case "image_src": case "image_src":
o.previews.push(url); o.previews.push(url);
break; break;
case "alternate":
// expressly for oembed support but we're liberal here and will let
// other alternate links through. oembed defines an href, supplied by
// the site, where you can fetch additional meta data about a page.
// We'll let the client fetch the oembed data themselves, but they
// need the data from this link.
if (!o.alternate)
o.alternate = [];
o.alternate.push({
"type": el.getAttribute("type"),
"href": el.getAttribute("href"),
"title": el.getAttribute("title")
})
} }
} }
}, },
@ -610,3 +635,43 @@ this.OpenGraphBuilder = {
return l; return l;
} }
}; };
// getMicrodata (and getObject) based on wg algorythm to convert microdata to json
// http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata-2.html#json
function getMicrodata(document, target) {
function _getObject(item) {
let result = {};
if (item.itemType.length)
result.types = [i for (i of item.itemType)];
if (item.itemId)
result.itemId = item.itemid;
if (item.properties.length)
result.properties = {};
for (let elem of item.properties) {
let value;
if (elem.itemScope)
value = _getObject(elem);
else if (elem.itemValue)
value = elem.itemValue;
// handle mis-formatted microdata
else if (elem.hasAttribute("content"))
value = elem.getAttribute("content");
for (let prop of elem.itemProp) {
if (!result.properties[prop])
result.properties[prop] = [];
result.properties[prop].push(value);
}
}
return result;
}
let result = { items: [] };
let elms = target ? [target] : document.getItems();
for (let el of elms) {
if (el.itemScope)
result.items.push(_getObject(el));
}
return result;
}