Bug 1393924 - Collect description and preview image and store it into moz_places r=mak,Mardak

MozReview-Commit-ID: 4ZPGMpz21S9

--HG--
extra : rebase_source : 1ec64bb45fe6a8f0d034bab7b7e6a5d3a81ce276
This commit is contained in:
Ursula Sarracini 2017-09-07 16:51:02 -04:00
Родитель 376fd336af
Коммит 49c86b15ec
10 изменённых файлов: 222 добавлений и 3 удалений

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

@ -1290,7 +1290,7 @@ var gBrowserInit = {
// loading the frame script to ensure that we don't miss any
// message sent between when the frame script is loaded and when
// the listener is registered.
DOMLinkHandler.init();
DOMEventHandler.init();
gPageStyleMenu.init();
LanguageDetectionListener.init();
BrowserOnClick.init();
@ -3704,13 +3704,13 @@ var newWindowButtonObserver = {
}
}
}
const DOMLinkHandler = {
const DOMEventHandler = {
init() {
let mm = window.messageManager;
mm.addMessageListener("Link:AddFeed", this);
mm.addMessageListener("Link:SetIcon", this);
mm.addMessageListener("Link:AddSearch", this);
mm.addMessageListener("Meta:SetPageInfo", this);
},
receiveMessage(aMsg) {
@ -3727,9 +3727,19 @@ const DOMLinkHandler = {
case "Link:AddSearch":
this.addSearch(aMsg.target, aMsg.data.engine, aMsg.data.url);
break;
case "Meta:SetPageInfo":
this.setPageInfo(aMsg.data);
break;
}
},
setPageInfo(aData) {
const {url, description, previewImageURL} = aData;
gBrowser.setPageInfo(url, description, previewImageURL);
return true;
},
setIcon(aBrowser, aURL, aLoadingPrincipal) {
if (gBrowser.isFailedIcon(aURL))
return false;

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

@ -17,6 +17,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
E10SUtils: "resource:///modules/E10SUtils.jsm",
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
@ -769,6 +770,7 @@ var ClickEventHandler = {
ClickEventHandler.init();
ContentLinkHandler.init(this);
ContentMetaHandler.init(this);
// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
var pluginContent = new PluginContent(global);

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

@ -1022,6 +1022,20 @@
</body>
</method>
<method name="setPageInfo">
<parameter name="aURL"/>
<parameter name="aDescription"/>
<parameter name="aPreviewImage"/>
<body>
<![CDATA[
if (aURL) {
let pageInfo = {url: aURL, description: aDescription, previewImageURL: aPreviewImage}
PlacesUtils.history.update(pageInfo).catch(Components.utils.reportError);
}
]]>
</body>
</method>
<method name="shouldLoadFavIcon">
<parameter name="aURI"/>
<body>

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

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/browser-test",
]
};

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

@ -0,0 +1,4 @@
[DEFAULT]
support-files =
meta_tags.html
[browser_meta_tags.js]

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

@ -0,0 +1,30 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* globals gBrowser */
/* This tests that with the page meta_tags.html, ContentMetaHandler.jsm parses out
* the meta tags avilable and only stores the best one for description and one for
* preview image url. In the case of this test, the best defined meta tags are
* "og:description" and "og:image:url". The list of meta tags and their order of
* preference is found in ContentMetaHandler.jsm. Because there is debounce logic
* in ContentLinkHandler.jsm to only make one single SQL update, we have to wait
* for some time before checking that the page info was stored correctly.
*/
add_task(async function test() {
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
const URL = "https://example.com/browser/browser/base/content/test/metaTags/meta_tags.html";
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
// Wait until places has stored the page info
let pageInfo;
await BrowserTestUtils.waitForCondition(async () => {
pageInfo = await PlacesUtils.history.fetch(URL, {"includeMeta": true});
const {previewImageURL, description} = pageInfo;
return previewImageURL && description;
});
is(pageInfo.description, "og:description", "got the correct description");
is(pageInfo.previewImageURL.href, "og:image:url", "got the correct preview image");
await BrowserTestUtils.removeTab(tab);
});

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

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8" />
<title>MetaTags</title>
<meta property="twitter:description" content="twitter:description" />
<meta property="og:description" content="og:description" />
<meta name="description" content="description" />
<meta name="unknown:tag" content="unknown:tag" />
<meta property="og:image" content="og:image" />
<meta property="twitter:image" content="twitter:image" />
<meta property="og:image:url" content="og:image:url" />
<meta name="thumbnail" content="thumbnail" />
</head>
<body>
</body>
</html>

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

@ -24,6 +24,7 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/contextMenu/browser.ini',
'content/test/forms/browser.ini',
'content/test/general/browser.ini',
'content/test/metaTags/browser.ini',
'content/test/newtab/browser.ini',
'content/test/pageinfo/browser.ini',
'content/test/performance/browser.ini',

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

@ -0,0 +1,132 @@
/* 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/. */
"use strict";
const {utils: Cu, interfaces: Ci, classes: Cc} = Components;
Cu.importGlobalProperties(["URL"]);
// Debounce time in milliseconds - this should be long enough to account for
// sync script tags that could appear between desired meta tags
const TIMEOUT_DELAY = 1000;
// Possible description tags, listed in order from least favourable to most favourable
const DESCRIPTION_RULES = [
"twitter:description",
"description",
"og:description"
];
// Possible image tags, listed in order from least favourable to most favourable
const PREVIEW_IMAGE_RULES = [
"thumbnail",
"twitter:image",
"og:image",
"og:image:url",
"og:image:secure_url"
];
/*
* Checks if the incoming meta tag has a greater score than the current best
* score by checking the index of the meta tag in the list of rules provided.
*
* @param {Array} aRules
* The list of rules for a given type of meta tag
* @param {String} aTag
* The name or property of the incoming meta tag
* @param {String} aEntry
* The current best entry for the given meta tag
*
* @returns {Boolean} true if the incoming meta tag is better than the current
* best meta tag of that same kind, false otherwise
*/
function shouldExtractMetadata(aRules, aTag, aEntry) {
return aRules.indexOf(aTag) > aEntry.currMaxScore;
}
this.EXPORTED_SYMBOLS = [ "ContentMetaHandler" ];
/*
* This listens to DOMMetaAdded events and collects relevant metadata about the
* meta tag received. Then, it sends the metadata gathered from the meta tags
* and the url of the page as it's payload to be inserted into moz_places.
*/
this.ContentMetaHandler = {
init(chromeGlobal) {
chromeGlobal.addEventListener("DOMMetaAdded", event => {
const metaTag = event.originalTarget;
const window = metaTag.ownerGlobal;
// If there's no meta tag, or we're in a sub-frame, ignore this
if (!metaTag || !metaTag.ownerDocument || window != window.top) {
return;
}
this.handleMetaTag(metaTag, chromeGlobal);
});
// Stores a mapping of the best description and preview image collected so far
// for a given URL
this._metaTags = new Map();
},
handleMetaTag(metaTag, chromeGlobal) {
const url = metaTag.ownerDocument.documentURI;
let name = metaTag.name;
let prop = metaTag.getAttributeNS(null, "property");
if (!name && !prop) {
return;
}
let tag = name || prop;
const entry = this._metaTags.get(url) || {
description: {value: null, currMaxScore: -1},
image: {value: null, currMaxScore: -1},
timeout: null
};
if (shouldExtractMetadata(DESCRIPTION_RULES, tag, entry.description)) {
// Extract the description
const value = metaTag.getAttributeNS(null, "content");
if (value) {
entry.description.value = value;
entry.description.currMaxScore = DESCRIPTION_RULES.indexOf(tag);
}
} else if (shouldExtractMetadata(PREVIEW_IMAGE_RULES, tag, entry.image)) {
// Extract the preview image
const value = metaTag.getAttributeNS(null, "content");
if (value) {
entry.image.value = new URL(value, url).href;
entry.image.currMaxScore = PREVIEW_IMAGE_RULES.indexOf(tag);
}
} else {
// We don't care about other meta tags
return;
}
if (!this._metaTags.has(url)) {
this._metaTags.set(url, entry);
}
if (entry.timeout) {
entry.timeout.delay = TIMEOUT_DELAY;
} else {
// We want to debounce incoming meta tags until we're certain we have the
// best one for description and preview image, and only store that one
entry.timeout = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
entry.timeout.initWithCallback(() => {
entry.timeout = null;
// Save description and preview image to moz_places
chromeGlobal.sendAsyncMessage("Meta:SetPageInfo", {
url,
description: entry.description.value,
previewImageURL: entry.image.value
});
this._metaTags.delete(url);
}, TIMEOUT_DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
}
}
};

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

@ -132,6 +132,7 @@ EXTRA_JS_MODULES += [
'ContentClick.jsm',
'ContentCrashHandlers.jsm',
'ContentLinkHandler.jsm',
'ContentMetaHandler.jsm',
'ContentObservers.js',
'ContentSearch.jsm',
'ContentWebRTC.jsm',