зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1393924 - Collect description and preview image and store it into moz_places r=mak
MozReview-Commit-ID: 8AgBMOodQop --HG-- extra : rebase_source : f440e9497ee52a3b38e7bcf6dac7fee574f7885d
This commit is contained in:
Родитель
c228558347
Коммит
bb971f3f6a
|
@ -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 @@
|
|||
/* 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/. */
|
||||
|
||||
/* 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.href && 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
|
||||
const TIMEOUT_DELAY = 100;
|
||||
|
||||
// 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"
|
||||
];
|
||||
|
||||
const DEFAULT_MAP_VALUES = {
|
||||
"description": {value: null, currMaxScore: -1},
|
||||
"image": {value: null, currMaxScore: -1},
|
||||
"timeout": null
|
||||
};
|
||||
|
||||
/*
|
||||
* 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;
|
||||
let entry = this._metaTags.get(url) || Object.assign({}, DEFAULT_MAP_VALUES);
|
||||
|
||||
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',
|
||||
|
|
Загрузка…
Ссылка в новой задаче