зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1616280 - Use shadow DOM to hide Remote L10n translations for from local translations r=k88hudson
Differential Revision: https://phabricator.services.mozilla.com/D63895
This commit is contained in:
Родитель
af4a04d2c5
Коммит
d88ff7e8b9
|
@ -0,0 +1,67 @@
|
||||||
|
/* 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";
|
||||||
|
|
||||||
|
// This is loaded into all XUL windows. Wrap in a block to prevent
|
||||||
|
// leaking to window scope.
|
||||||
|
{
|
||||||
|
const { RemoteL10n } = ChromeUtils.import(
|
||||||
|
"resource://activity-stream/lib/RemoteL10n.jsm"
|
||||||
|
);
|
||||||
|
class MozTextParagraph extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fluentAttributeValues() {
|
||||||
|
const attributes = {};
|
||||||
|
for (let name of this.getAttributeNames()) {
|
||||||
|
if (name.startsWith("fluent-variable-")) {
|
||||||
|
attributes[name.replace(/^fluent-variable-/, "")] = this.getAttribute(
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.getAttribute("fluent-remote-id") && this._content) {
|
||||||
|
RemoteL10n.l10n.setAttributes(
|
||||||
|
this._content,
|
||||||
|
this.getAttribute("fluent-remote-id"),
|
||||||
|
this.fluentAttributeValues
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["fluent-remote-id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
if (this.shadowRoot) {
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shadowRoot = this.attachShadow({ mode: "open" });
|
||||||
|
this._content = document.createElement("span");
|
||||||
|
shadowRoot.appendChild(this._content);
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
RemoteL10n.l10n.translateFragment(this._content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("remote-text", MozTextParagraph);
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ browser.jar:
|
||||||
res/activity-stream/data/content/tippytop/ (./data/content/tippytop/*)
|
res/activity-stream/data/content/tippytop/ (./data/content/tippytop/*)
|
||||||
res/activity-stream/data/content/activity-stream.bundle.js (./data/content/activity-stream.bundle.js)
|
res/activity-stream/data/content/activity-stream.bundle.js (./data/content/activity-stream.bundle.js)
|
||||||
res/activity-stream/data/content/newtab-render.js (./data/content/newtab-render.js)
|
res/activity-stream/data/content/newtab-render.js (./data/content/newtab-render.js)
|
||||||
|
res/activity-stream/data/custom-elements/ (./components/CustomElements/*)
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
res/activity-stream/css/activity-stream.css (./css/activity-stream-mac.css)
|
res/activity-stream/css/activity-stream.css (./css/activity-stream-mac.css)
|
||||||
#elifdef XP_WIN
|
#elifdef XP_WIN
|
||||||
|
|
|
@ -10,7 +10,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
Services: "resource://gre/modules/Services.jsm",
|
Services: "resource://gre/modules/Services.jsm",
|
||||||
EveryWindow: "resource:///modules/EveryWindow.jsm",
|
EveryWindow: "resource:///modules/EveryWindow.jsm",
|
||||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||||
RemoteL10n: "resource://activity-stream/lib/RemoteL10n.jsm",
|
|
||||||
});
|
});
|
||||||
XPCOMUtils.defineLazyServiceGetter(
|
XPCOMUtils.defineLazyServiceGetter(
|
||||||
this,
|
this,
|
||||||
|
@ -108,6 +107,15 @@ class _ToolbarPanelHub {
|
||||||
win.MozXULElement.insertFTLIfNeeded("browser/branding/sync-brand.ftl");
|
win.MozXULElement.insertFTLIfNeeded("browser/branding/sync-brand.ftl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybeLoadCustomElement(win) {
|
||||||
|
if (!win.customElements.get("remote-text")) {
|
||||||
|
Services.scriptloader.loadSubScript(
|
||||||
|
"resource://activity-stream/data/custom-elements/paragraph.js",
|
||||||
|
win
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
|
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
|
||||||
async enableAppmenuButton() {
|
async enableAppmenuButton() {
|
||||||
if ((await this.messages).length) {
|
if ((await this.messages).length) {
|
||||||
|
@ -170,6 +178,7 @@ class _ToolbarPanelHub {
|
||||||
|
|
||||||
// Render what's new messages into the panel.
|
// Render what's new messages into the panel.
|
||||||
async renderMessages(win, doc, containerId, options = {}) {
|
async renderMessages(win, doc, containerId, options = {}) {
|
||||||
|
this.maybeLoadCustomElement(win);
|
||||||
const messages =
|
const messages =
|
||||||
(options.force && options.messages) ||
|
(options.force && options.messages) ||
|
||||||
(await this.messages).sort(this._sortWhatsNewMessages);
|
(await this.messages).sort(this._sortWhatsNewMessages);
|
||||||
|
@ -178,17 +187,13 @@ class _ToolbarPanelHub {
|
||||||
if (messages) {
|
if (messages) {
|
||||||
// Targeting attribute state might have changed making new messages
|
// Targeting attribute state might have changed making new messages
|
||||||
// available and old messages invalid, we need to refresh
|
// available and old messages invalid, we need to refresh
|
||||||
for (const prevMessageEl of container.querySelectorAll(
|
this.removeMessages(win, containerId);
|
||||||
".whatsNew-message"
|
|
||||||
)) {
|
|
||||||
container.removeChild(prevMessageEl);
|
|
||||||
}
|
|
||||||
let previousDate = 0;
|
let previousDate = 0;
|
||||||
// Get and store any variable part of the message content
|
// Get and store any variable part of the message content
|
||||||
this.state.contentArguments = await this._contentArguments();
|
this.state.contentArguments = await this._contentArguments();
|
||||||
for (let message of messages) {
|
for (let message of messages) {
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
await this._createMessageElements(win, doc, message, previousDate)
|
this._createMessageElements(win, doc, message, previousDate)
|
||||||
);
|
);
|
||||||
previousDate = message.content.published_date;
|
previousDate = message.content.published_date;
|
||||||
}
|
}
|
||||||
|
@ -267,15 +272,15 @@ class _ToolbarPanelHub {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createMessageElements(win, doc, message, previousDate) {
|
_createMessageElements(win, doc, message, previousDate) {
|
||||||
const { content } = message;
|
const { content } = message;
|
||||||
const messageEl = await this._createElement(doc, "div");
|
const messageEl = this._createElement(doc, "div");
|
||||||
messageEl.classList.add("whatsNew-message");
|
messageEl.classList.add("whatsNew-message");
|
||||||
|
|
||||||
// Only render date if it is different from the one rendered before.
|
// Only render date if it is different from the one rendered before.
|
||||||
if (content.published_date !== previousDate) {
|
if (content.published_date !== previousDate) {
|
||||||
messageEl.appendChild(
|
messageEl.appendChild(
|
||||||
await this._createElement(doc, "p", {
|
this._createElement(doc, "p", {
|
||||||
classList: "whatsNew-message-date",
|
classList: "whatsNew-message-date",
|
||||||
content: new Date(content.published_date).toLocaleDateString(
|
content: new Date(content.published_date).toLocaleDateString(
|
||||||
"default",
|
"default",
|
||||||
|
@ -289,24 +294,28 @@ class _ToolbarPanelHub {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapperEl = await this._createElement(doc, "button");
|
const wrapperEl = this._createElement(doc, "button");
|
||||||
wrapperEl.doCommand = () => this._dispatchUserAction(win, message);
|
wrapperEl.doCommand = () => this._dispatchUserAction(win, message);
|
||||||
wrapperEl.classList.add("whatsNew-message-body");
|
wrapperEl.classList.add("whatsNew-message-body");
|
||||||
messageEl.appendChild(wrapperEl);
|
messageEl.appendChild(wrapperEl);
|
||||||
|
|
||||||
if (content.icon_url) {
|
if (content.icon_url) {
|
||||||
wrapperEl.classList.add("has-icon");
|
wrapperEl.classList.add("has-icon");
|
||||||
const iconEl = await this._createElement(doc, "img");
|
const iconEl = this._createElement(doc, "img");
|
||||||
iconEl.src = content.icon_url;
|
iconEl.src = content.icon_url;
|
||||||
iconEl.classList.add("whatsNew-message-icon");
|
iconEl.classList.add("whatsNew-message-icon");
|
||||||
await this._setTextAttribute(iconEl, "alt", content.icon_alt);
|
if (content.icon_alt && content.icon_alt.string_id) {
|
||||||
|
doc.l10n.setAttributes(iconEl, content.icon_alt.string_id);
|
||||||
|
} else {
|
||||||
|
iconEl.setAttribute("alt", content.icon_alt);
|
||||||
|
}
|
||||||
wrapperEl.appendChild(iconEl);
|
wrapperEl.appendChild(iconEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapperEl.appendChild(await this._createMessageContent(win, doc, content));
|
wrapperEl.appendChild(this._createMessageContent(win, doc, content));
|
||||||
|
|
||||||
if (content.link_text) {
|
if (content.link_text) {
|
||||||
const anchorEl = await this._createElement(doc, "a", {
|
const anchorEl = this._createElement(doc, "a", {
|
||||||
classList: "text-link",
|
classList: "text-link",
|
||||||
content: content.link_text,
|
content: content.link_text,
|
||||||
});
|
});
|
||||||
|
@ -323,11 +332,11 @@ class _ToolbarPanelHub {
|
||||||
/**
|
/**
|
||||||
* Return message title (optional subtitle) and body
|
* Return message title (optional subtitle) and body
|
||||||
*/
|
*/
|
||||||
async _createMessageContent(win, doc, content) {
|
_createMessageContent(win, doc, content) {
|
||||||
const wrapperEl = new win.DocumentFragment();
|
const wrapperEl = new win.DocumentFragment();
|
||||||
|
|
||||||
wrapperEl.appendChild(
|
wrapperEl.appendChild(
|
||||||
await this._createElement(doc, "h2", {
|
this._createElement(doc, "h2", {
|
||||||
classList: "whatsNew-message-title",
|
classList: "whatsNew-message-title",
|
||||||
content: content.title,
|
content: content.title,
|
||||||
})
|
})
|
||||||
|
@ -335,14 +344,14 @@ class _ToolbarPanelHub {
|
||||||
|
|
||||||
switch (content.layout) {
|
switch (content.layout) {
|
||||||
case "tracking-protections":
|
case "tracking-protections":
|
||||||
await wrapperEl.appendChild(
|
wrapperEl.appendChild(
|
||||||
await this._createElement(doc, "h4", {
|
this._createElement(doc, "h4", {
|
||||||
classList: "whatsNew-message-subtitle",
|
classList: "whatsNew-message-subtitle",
|
||||||
content: content.subtitle,
|
content: content.subtitle,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
wrapperEl.appendChild(
|
wrapperEl.appendChild(
|
||||||
await this._createElement(doc, "h2", {
|
this._createElement(doc, "h2", {
|
||||||
classList: "whatsNew-message-title-large",
|
classList: "whatsNew-message-title-large",
|
||||||
content: this.state.contentArguments[
|
content: this.state.contentArguments[
|
||||||
content.layout_title_content_variable
|
content.layout_title_content_variable
|
||||||
|
@ -353,32 +362,40 @@ class _ToolbarPanelHub {
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapperEl.appendChild(
|
wrapperEl.appendChild(
|
||||||
await this._createElement(doc, "p", { content: content.body })
|
this._createElement(doc, "p", {
|
||||||
|
content: content.body,
|
||||||
|
classList: "whatsNew-message-content",
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return wrapperEl;
|
return wrapperEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createHeroElement(win, doc, message) {
|
_createHeroElement(win, doc, message) {
|
||||||
const messageEl = await this._createElement(doc, "div");
|
this.maybeLoadCustomElement(win);
|
||||||
|
|
||||||
|
const messageEl = this._createElement(doc, "div");
|
||||||
messageEl.setAttribute("id", "protections-popup-message");
|
messageEl.setAttribute("id", "protections-popup-message");
|
||||||
messageEl.classList.add("whatsNew-hero-message");
|
messageEl.classList.add("whatsNew-hero-message");
|
||||||
const wrapperEl = await this._createElement(doc, "div");
|
const wrapperEl = this._createElement(doc, "div");
|
||||||
wrapperEl.classList.add("whatsNew-message-body");
|
wrapperEl.classList.add("whatsNew-message-body");
|
||||||
messageEl.appendChild(wrapperEl);
|
messageEl.appendChild(wrapperEl);
|
||||||
|
|
||||||
wrapperEl.appendChild(
|
wrapperEl.appendChild(
|
||||||
await this._createElement(doc, "h2", {
|
this._createElement(doc, "h2", {
|
||||||
classList: "whatsNew-message-title",
|
classList: "whatsNew-message-title",
|
||||||
content: message.content.title,
|
content: message.content.title,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
wrapperEl.appendChild(
|
wrapperEl.appendChild(
|
||||||
await this._createElement(doc, "p", { content: message.content.body })
|
this._createElement(doc, "p", {
|
||||||
|
classList: "protections-popup-content",
|
||||||
|
content: message.content.body,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (message.content.link_text) {
|
if (message.content.link_text) {
|
||||||
let linkEl = await this._createElement(doc, "a", {
|
let linkEl = this._createElement(doc, "a", {
|
||||||
classList: "text-link",
|
classList: "text-link",
|
||||||
content: message.content.link_text,
|
content: message.content.link_text,
|
||||||
});
|
});
|
||||||
|
@ -392,14 +409,17 @@ class _ToolbarPanelHub {
|
||||||
return messageEl;
|
return messageEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createElement(doc, elem, options = {}) {
|
_createElement(doc, elem, options = {}) {
|
||||||
const node = doc.createElementNS("http://www.w3.org/1999/xhtml", elem);
|
let node;
|
||||||
|
if (options.content && options.content.string_id) {
|
||||||
|
node = doc.createElement("remote-text");
|
||||||
|
} else {
|
||||||
|
node = doc.createElementNS("http://www.w3.org/1999/xhtml", elem);
|
||||||
|
}
|
||||||
if (options.classList) {
|
if (options.classList) {
|
||||||
node.classList.add(options.classList);
|
node.classList.add(options.classList);
|
||||||
}
|
}
|
||||||
if (options.content) {
|
this._setString(node, options.content);
|
||||||
await this._setString(node, options.content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -447,41 +467,19 @@ class _ToolbarPanelHub {
|
||||||
|
|
||||||
// If `string_id` is present it means we are relying on fluent for translations.
|
// If `string_id` is present it means we are relying on fluent for translations.
|
||||||
// Otherwise, we have a vanilla string.
|
// Otherwise, we have a vanilla string.
|
||||||
async _setString(el, stringObj) {
|
_setString(el, stringObj) {
|
||||||
if (stringObj && stringObj.string_id) {
|
if (stringObj && stringObj.string_id) {
|
||||||
const [{ value }] = await RemoteL10n.l10n.formatMessages([
|
for (let [fluentId, value] of Object.entries(
|
||||||
{
|
this.state.contentArguments || {}
|
||||||
id: stringObj.string_id,
|
)) {
|
||||||
// Pass all available arguments to Fluent
|
el.setAttribute(`fluent-variable-${fluentId}`, value);
|
||||||
args: this.state.contentArguments,
|
}
|
||||||
},
|
el.setAttribute("fluent-remote-id", stringObj.string_id);
|
||||||
]);
|
|
||||||
el.textContent = value;
|
|
||||||
} else {
|
} else {
|
||||||
el.textContent = stringObj;
|
el.textContent = stringObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If `string_id` is present it means we are relying on fluent for translations.
|
|
||||||
// Otherwise, we have a vanilla string.
|
|
||||||
async _setTextAttribute(el, attr, stringObj) {
|
|
||||||
if (stringObj && stringObj.string_id) {
|
|
||||||
const [{ attributes }] = await RemoteL10n.l10n.formatMessages([
|
|
||||||
{
|
|
||||||
id: stringObj.string_id,
|
|
||||||
// Pass all available arguments to Fluent
|
|
||||||
args: this.state.contentArguments,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
if (attributes) {
|
|
||||||
const { value } = attributes.find(({ name }) => name === attr);
|
|
||||||
el.setAttribute(attr, value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
el.setAttribute(attr, stringObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _showAppmenuButton(win) {
|
async _showAppmenuButton(win) {
|
||||||
this.maybeInsertFTL(win);
|
this.maybeInsertFTL(win);
|
||||||
await this._showElement(
|
await this._showElement(
|
||||||
|
@ -505,10 +503,9 @@ class _ToolbarPanelHub {
|
||||||
this._hideElement(win.browser.ownerDocument, TOOLBAR_BUTTON_ID);
|
this._hideElement(win.browser.ownerDocument, TOOLBAR_BUTTON_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _showElement(document, id, string_id) {
|
_showElement(document, id, string_id) {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
await this._setTextAttribute(el, "label", { string_id });
|
document.l10n.setAttributes(el, string_id);
|
||||||
await this._setTextAttribute(el, "tooltiptext", { string_id });
|
|
||||||
el.removeAttribute("hidden");
|
el.removeAttribute("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +565,7 @@ class _ToolbarPanelHub {
|
||||||
triggerId: "protectionsPanelOpen",
|
triggerId: "protectionsPanelOpen",
|
||||||
});
|
});
|
||||||
if (message) {
|
if (message) {
|
||||||
const messageEl = await this._createHeroElement(win, doc, message);
|
const messageEl = this._createHeroElement(win, doc, message);
|
||||||
container.appendChild(messageEl);
|
container.appendChild(messageEl);
|
||||||
infoButton.addEventListener("click", toggleMessage);
|
infoButton.addEventListener("click", toggleMessage);
|
||||||
this.sendUserEventTelemetry(win, "IMPRESSION", message);
|
this.sendUserEventTelemetry(win, "IMPRESSION", message);
|
||||||
|
|
|
@ -77,6 +77,14 @@ add_task(async function test_with_rs_messages() {
|
||||||
"The message container was not populated with the expected number of msgs"
|
"The message container was not populated with the expected number of msgs"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await BrowserTestUtils.waitForCondition(
|
||||||
|
() =>
|
||||||
|
document.querySelector(
|
||||||
|
"#PanelUI-whatsNew-message-container .whatsNew-message-body remote-text"
|
||||||
|
).shadowRoot.innerHTML,
|
||||||
|
"Ensure messages have content"
|
||||||
|
);
|
||||||
|
|
||||||
UITour.hideMenu(window, "appMenu");
|
UITour.hideMenu(window, "appMenu");
|
||||||
// Clean up and remove messages
|
// Clean up and remove messages
|
||||||
ToolbarPanelHub.disableAppmenuButton();
|
ToolbarPanelHub.disableAppmenuButton();
|
||||||
|
|
|
@ -12,6 +12,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
let fakeWindow;
|
let fakeWindow;
|
||||||
let fakeElementById;
|
let fakeElementById;
|
||||||
let createdElements = [];
|
let createdElements = [];
|
||||||
|
let createdCustomElements = [];
|
||||||
let eventListeners = {};
|
let eventListeners = {};
|
||||||
let addObserverStub;
|
let addObserverStub;
|
||||||
let removeObserverStub;
|
let removeObserverStub;
|
||||||
|
@ -24,6 +25,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
let getEventsByDateRangeStub;
|
let getEventsByDateRangeStub;
|
||||||
let handleUserActionStub;
|
let handleUserActionStub;
|
||||||
let defaultSearchStub;
|
let defaultSearchStub;
|
||||||
|
let scriptloaderStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
|
@ -56,10 +58,34 @@ describe("ToolbarPanelHub", () => {
|
||||||
},
|
},
|
||||||
appendChild: sandbox.stub(),
|
appendChild: sandbox.stub(),
|
||||||
setAttribute: sandbox.stub(),
|
setAttribute: sandbox.stub(),
|
||||||
|
textContent: "",
|
||||||
};
|
};
|
||||||
createdElements.push(element);
|
createdElements.push(element);
|
||||||
return element;
|
return element;
|
||||||
},
|
},
|
||||||
|
createElement: tagName => {
|
||||||
|
const element = {
|
||||||
|
tagName,
|
||||||
|
classList: {},
|
||||||
|
addEventListener: (ev, fn) => {
|
||||||
|
eventListeners[ev] = fn;
|
||||||
|
},
|
||||||
|
appendChild: sandbox.stub(),
|
||||||
|
setAttribute: sandbox.stub(),
|
||||||
|
textContent: "",
|
||||||
|
};
|
||||||
|
element.classList.add = sandbox.stub();
|
||||||
|
element.classList.includes = className =>
|
||||||
|
element.classList.add.firstCall.args[0] === className;
|
||||||
|
createdCustomElements.push(element);
|
||||||
|
return element;
|
||||||
|
},
|
||||||
|
l10n: {
|
||||||
|
translateElements: sandbox.stub(),
|
||||||
|
translateFragment: sandbox.stub(),
|
||||||
|
formatMessages: sandbox.stub().resolves([{}]),
|
||||||
|
setAttributes: sandbox.stub(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
fakeWindow = {
|
fakeWindow = {
|
||||||
// eslint-disable-next-line object-shorthand
|
// eslint-disable-next-line object-shorthand
|
||||||
|
@ -79,11 +105,13 @@ describe("ToolbarPanelHub", () => {
|
||||||
panel: fakeElementById,
|
panel: fakeElementById,
|
||||||
whatsNewPanel: fakeElementById,
|
whatsNewPanel: fakeElementById,
|
||||||
},
|
},
|
||||||
|
customElements: { get: sandbox.stub() },
|
||||||
};
|
};
|
||||||
everyWindowStub = {
|
everyWindowStub = {
|
||||||
registerCallback: sandbox.stub(),
|
registerCallback: sandbox.stub(),
|
||||||
unregisterCallback: sandbox.stub(),
|
unregisterCallback: sandbox.stub(),
|
||||||
};
|
};
|
||||||
|
scriptloaderStub = { loadSubScript: sandbox.stub() };
|
||||||
addObserverStub = sandbox.stub();
|
addObserverStub = sandbox.stub();
|
||||||
removeObserverStub = sandbox.stub();
|
removeObserverStub = sandbox.stub();
|
||||||
getBoolPrefStub = sandbox.stub();
|
getBoolPrefStub = sandbox.stub();
|
||||||
|
@ -108,6 +136,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
setBoolPref: setBoolPrefStub,
|
setBoolPref: setBoolPrefStub,
|
||||||
},
|
},
|
||||||
search: defaultSearchStub,
|
search: defaultSearchStub,
|
||||||
|
scriptloader: scriptloaderStub,
|
||||||
},
|
},
|
||||||
PrivateBrowsingUtils: {
|
PrivateBrowsingUtils: {
|
||||||
isBrowserPrivate: isBrowserPrivateStub,
|
isBrowserPrivate: isBrowserPrivateStub,
|
||||||
|
@ -116,13 +145,6 @@ describe("ToolbarPanelHub", () => {
|
||||||
getEarliestRecordedDate: getEarliestRecordedDateStub,
|
getEarliestRecordedDate: getEarliestRecordedDateStub,
|
||||||
getEventsByDateRange: getEventsByDateRangeStub,
|
getEventsByDateRange: getEventsByDateRangeStub,
|
||||||
},
|
},
|
||||||
RemoteL10n: {
|
|
||||||
l10n: {
|
|
||||||
translateElements: sandbox.stub(),
|
|
||||||
translateFragment: sandbox.stub(),
|
|
||||||
formatMessages: sandbox.stub().resolves([{}]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -131,6 +153,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
globals.restore();
|
globals.restore();
|
||||||
eventListeners = {};
|
eventListeners = {};
|
||||||
createdElements = [];
|
createdElements = [];
|
||||||
|
createdCustomElements = [];
|
||||||
});
|
});
|
||||||
it("should create an instance", () => {
|
it("should create an instance", () => {
|
||||||
assert.ok(instance);
|
assert.ok(instance);
|
||||||
|
@ -290,6 +313,32 @@ describe("ToolbarPanelHub", () => {
|
||||||
handleUserAction: handleUserActionStub,
|
handleUserAction: handleUserActionStub,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("should have correct state", async () => {
|
||||||
|
const messages = (await PanelTestProvider.getMessages()).filter(
|
||||||
|
m => m.template === "whatsnew_panel_message"
|
||||||
|
);
|
||||||
|
|
||||||
|
getMessagesStub.returns(messages);
|
||||||
|
const ev1 = sandbox.stub();
|
||||||
|
ev1.withArgs("type").returns(1); // tracker
|
||||||
|
ev1.withArgs("count").returns(4);
|
||||||
|
const ev2 = sandbox.stub();
|
||||||
|
ev2.withArgs("type").returns(4); // fingerprinter
|
||||||
|
ev2.withArgs("count").returns(3);
|
||||||
|
getEventsByDateRangeStub.returns([
|
||||||
|
{ getResultByName: ev1 },
|
||||||
|
{ getResultByName: ev2 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||||
|
|
||||||
|
assert.propertyVal(instance.state.contentArguments, "trackerCount", 4);
|
||||||
|
assert.propertyVal(
|
||||||
|
instance.state.contentArguments,
|
||||||
|
"fingerprinterCount",
|
||||||
|
3
|
||||||
|
);
|
||||||
|
});
|
||||||
it("should render messages to the panel on renderMessages()", async () => {
|
it("should render messages to the panel on renderMessages()", async () => {
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
const messages = (await PanelTestProvider.getMessages()).filter(
|
||||||
m => m.template === "whatsnew_panel_message"
|
m => m.template === "whatsnew_panel_message"
|
||||||
|
@ -311,19 +360,30 @@ describe("ToolbarPanelHub", () => {
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||||
|
|
||||||
for (let message of messages) {
|
for (let message of messages) {
|
||||||
assert.ok(createdElements.find(el => el.tagName === "h2"));
|
assert.ok(
|
||||||
|
createdCustomElements.find(el =>
|
||||||
|
el.classList.includes("whatsNew-message-title")
|
||||||
|
)
|
||||||
|
);
|
||||||
if (message.content.layout === "tracking-protections") {
|
if (message.content.layout === "tracking-protections") {
|
||||||
assert.ok(createdElements.find(el => el.tagName === "h4"));
|
assert.ok(
|
||||||
|
createdCustomElements.find(el =>
|
||||||
|
el.classList.includes("whatsNew-message-subtitle")
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (message.id === "WHATS_NEW_FINGERPRINTER_COUNTER_72") {
|
if (message.id === "WHATS_NEW_FINGERPRINTER_COUNTER_72") {
|
||||||
assert.ok(createdElements.find(el => el.tagName === "h4"));
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
createdElements.find(
|
createdElements.find(
|
||||||
el => el.tagName === "h2" && el.textContent === 3
|
el => el.tagName === "h2" && el.textContent === 3
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
assert.ok(createdElements.find(el => el.tagName === "p"));
|
assert.ok(
|
||||||
|
createdCustomElements.find(el =>
|
||||||
|
el.classList.includes("whatsNew-message-content")
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Call the click handler to make coverage happy.
|
// Call the click handler to make coverage happy.
|
||||||
eventListeners.mouseup();
|
eventListeners.mouseup();
|
||||||
|
@ -333,17 +393,18 @@ describe("ToolbarPanelHub", () => {
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
const messages = (await PanelTestProvider.getMessages()).filter(
|
||||||
m => m.template === "whatsnew_panel_message"
|
m => m.template === "whatsnew_panel_message"
|
||||||
);
|
);
|
||||||
|
const removeStub = sandbox.stub();
|
||||||
fakeElementById.querySelectorAll.onCall(0).returns([]);
|
fakeElementById.querySelectorAll.onCall(0).returns([]);
|
||||||
fakeElementById.querySelectorAll.onCall(1).returns(["a", "b", "c"]);
|
fakeElementById.querySelectorAll
|
||||||
|
.onCall(1)
|
||||||
|
.returns([{ remove: removeStub }, { remove: removeStub }]);
|
||||||
|
|
||||||
getMessagesStub.returns(messages);
|
getMessagesStub.returns(messages);
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||||
|
|
||||||
assert.calledThrice(fakeElementById.removeChild);
|
assert.calledTwice(removeStub);
|
||||||
assert.equal(fakeElementById.removeChild.firstCall.args[0], "a");
|
|
||||||
assert.equal(fakeElementById.removeChild.secondCall.args[0], "b");
|
|
||||||
});
|
});
|
||||||
it("should sort based on order field value", async () => {
|
it("should sort based on order field value", async () => {
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
const messages = (await PanelTestProvider.getMessages()).filter(
|
||||||
|
@ -385,38 +446,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
"Firefox Send Logo"
|
"Firefox Send Logo"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it("should accept fluent ids for image attributes", async () => {
|
it("should set state values as data-attribute", async () => {
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
|
||||||
m => m.id === "WHATS_NEW_70_1"
|
|
||||||
);
|
|
||||||
messages[0].content.icon_alt = { string_id: "foo" };
|
|
||||||
getMessagesStub.returns(messages);
|
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
|
||||||
|
|
||||||
assert.calledWithExactly(global.RemoteL10n.l10n.formatMessages, [
|
|
||||||
{
|
|
||||||
id: "foo",
|
|
||||||
args: instance.state.contentArguments,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
it("handle fluent attributes", async () => {
|
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
|
||||||
m => m.id === "WHATS_NEW_70_1"
|
|
||||||
);
|
|
||||||
messages[0].content.icon_alt = { string_id: "foo" };
|
|
||||||
getMessagesStub.returns(messages);
|
|
||||||
global.RemoteL10n.l10n.formatMessages
|
|
||||||
.withArgs([{ id: "foo", args: sinon.match.object }])
|
|
||||||
.resolves([{ attributes: [{ name: "alt", value: "bar" }] }]);
|
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
|
||||||
const imgEl = createdElements.find(e => e.tagName === "img");
|
|
||||||
|
|
||||||
assert.calledWithExactly(imgEl.setAttribute, "alt", "bar");
|
|
||||||
});
|
|
||||||
it("should accept fluent ids for elements attributes", async () => {
|
|
||||||
const [message] = (await PanelTestProvider.getMessages()).filter(
|
const [message] = (await PanelTestProvider.getMessages()).filter(
|
||||||
m =>
|
m =>
|
||||||
m.template === "whatsnew_panel_message" &&
|
m.template === "whatsnew_panel_message" &&
|
||||||
|
@ -427,102 +457,13 @@ describe("ToolbarPanelHub", () => {
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||||
|
|
||||||
assert.calledWithExactly(global.RemoteL10n.l10n.formatMessages, [
|
// Currently this.state.contentArguments has 9 different entries
|
||||||
{
|
assert.callCount(createdCustomElements[0].setAttribute, 9);
|
||||||
id: message.content.subtitle.string_id,
|
assert.calledWithExactly(
|
||||||
args: instance.state.contentArguments,
|
createdCustomElements[0].setAttribute,
|
||||||
},
|
"fluent-variable-searchEngineName",
|
||||||
]);
|
defaultSearchStub.defaultEngine.name
|
||||||
});
|
|
||||||
it("should correctly compute blocker trackers and date", async () => {
|
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
|
||||||
m => m.template === "whatsnew_panel_message"
|
|
||||||
);
|
);
|
||||||
getMessagesStub.returns(messages);
|
|
||||||
const ev1 = sandbox.stub();
|
|
||||||
ev1.withArgs("type").returns(2); // cookie
|
|
||||||
ev1.withArgs("count").returns(4);
|
|
||||||
const ev2 = sandbox.stub();
|
|
||||||
ev2.withArgs("type").returns(2); // cookie
|
|
||||||
ev2.withArgs("count").returns(3);
|
|
||||||
getEventsByDateRangeStub.returns([
|
|
||||||
{ getResultByName: ev1 },
|
|
||||||
{ getResultByName: ev2 },
|
|
||||||
]);
|
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
|
||||||
|
|
||||||
assert.calledWithExactly(global.RemoteL10n.l10n.formatMessages, [
|
|
||||||
{
|
|
||||||
id: sinon.match.string,
|
|
||||||
args: {
|
|
||||||
blockedCount: 7,
|
|
||||||
earliestDate: getEarliestRecordedDateStub(),
|
|
||||||
cookieCount: 7,
|
|
||||||
cryptominerCount: 0,
|
|
||||||
socialCount: 0,
|
|
||||||
trackerCount: 0,
|
|
||||||
fingerprinterCount: 0,
|
|
||||||
searchEngineName: Services.search.defaultEngine.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
it("should correctly compute event counts per type", async () => {
|
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
|
||||||
m => m.template === "whatsnew_panel_message"
|
|
||||||
);
|
|
||||||
getMessagesStub.returns(messages);
|
|
||||||
const ev1 = sandbox.stub();
|
|
||||||
ev1.withArgs("type").returns(1); // tracker
|
|
||||||
ev1.withArgs("count").returns(4);
|
|
||||||
const ev2 = sandbox.stub();
|
|
||||||
ev2.withArgs("type").returns(4); // fingerprinter
|
|
||||||
ev2.withArgs("count").returns(3);
|
|
||||||
getEventsByDateRangeStub.returns([
|
|
||||||
{ getResultByName: ev1 },
|
|
||||||
{ getResultByName: ev2 },
|
|
||||||
]);
|
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
|
||||||
|
|
||||||
assert.calledWithExactly(global.RemoteL10n.l10n.formatMessages, [
|
|
||||||
{
|
|
||||||
id: sinon.match.string,
|
|
||||||
args: {
|
|
||||||
blockedCount: 7,
|
|
||||||
earliestDate: getEarliestRecordedDateStub(),
|
|
||||||
trackerCount: 4,
|
|
||||||
fingerprinterCount: 3,
|
|
||||||
cookieCount: 0,
|
|
||||||
cryptominerCount: 0,
|
|
||||||
socialCount: 0,
|
|
||||||
searchEngineName: Services.search.defaultEngine.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
it("should fallback to undefined search engine name", async () => {
|
|
||||||
globals.set("Services", {
|
|
||||||
...global.Services,
|
|
||||||
search: { defaultEngine: null },
|
|
||||||
});
|
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
|
||||||
m => m.template === "whatsnew_panel_message"
|
|
||||||
);
|
|
||||||
getMessagesStub.returns(messages);
|
|
||||||
|
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
|
||||||
|
|
||||||
assert.calledWithExactly(global.RemoteL10n.l10n.formatMessages, [
|
|
||||||
{
|
|
||||||
id: sinon.match.string,
|
|
||||||
args: {
|
|
||||||
...instance.state.contentArguments,
|
|
||||||
searchEngineName: "undefined",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
it("should only render unique dates (no duplicates)", async () => {
|
it("should only render unique dates (no duplicates)", async () => {
|
||||||
const messages = (await PanelTestProvider.getMessages()).filter(
|
const messages = (await PanelTestProvider.getMessages()).filter(
|
||||||
|
@ -745,7 +686,6 @@ describe("ToolbarPanelHub", () => {
|
||||||
it("should call removeMessages when forcing a message to show", () => {
|
it("should call removeMessages when forcing a message to show", () => {
|
||||||
instance.forceShowMessage(browser, messages);
|
instance.forceShowMessage(browser, messages);
|
||||||
|
|
||||||
assert.calledOnce(removeMessagesSpy);
|
|
||||||
assert.calledWithExactly(removeMessagesSpy, fakeWindow, panelSelector);
|
assert.calledWithExactly(removeMessagesSpy, fakeWindow, panelSelector);
|
||||||
});
|
});
|
||||||
it("should call renderMessages when forcing a message to show", () => {
|
it("should call renderMessages when forcing a message to show", () => {
|
||||||
|
|
|
@ -345,6 +345,7 @@ const TEST_GLOBAL = {
|
||||||
},
|
},
|
||||||
ww: { registerNotification() {}, unregisterNotification() {} },
|
ww: { registerNotification() {}, unregisterNotification() {} },
|
||||||
appinfo: { appBuildID: "20180710100040", version: "69.0a1" },
|
appinfo: { appBuildID: "20180710100040", version: "69.0a1" },
|
||||||
|
scriptloader: { loadSubScript: () => {} },
|
||||||
},
|
},
|
||||||
XPCOMUtils: {
|
XPCOMUtils: {
|
||||||
defineLazyGetter(object, name, f) {
|
defineLazyGetter(object, name, f) {
|
||||||
|
|
|
@ -1854,6 +1854,11 @@ toolbarpaletteitem[place="menu-panel"] > .subviewbutton-nav::after {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#protections-popup-message .protections-popup-content {
|
||||||
|
display: block;
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
panelview[mainview] #PanelUI-whatsNew-content {
|
panelview[mainview] #PanelUI-whatsNew-content {
|
||||||
height: 43em;
|
height: 43em;
|
||||||
}
|
}
|
||||||
|
@ -1868,13 +1873,23 @@ panelview[mainview] #PanelUI-whatsNew-content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#PanelUI-whatsNew .whatsNew-message:not(:first-child)::before {
|
/* The following 2 rules show a 1 pixel line separator between What's New
|
||||||
|
* messages while at the same time ensuring that the first message (which has
|
||||||
|
* a date header) will not show the separator
|
||||||
|
*/
|
||||||
|
#PanelUI-whatsNew .whatsNew-message-body::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
|
width: 104%;
|
||||||
|
margin-inline-start: -2%;
|
||||||
background: var(--panel-separator-color);
|
background: var(--panel-separator-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#PanelUI-whatsNew .whatsNew-message-date + .whatsNew-message-body::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#PanelUI-whatsNew .whatsNew-message-date {
|
#PanelUI-whatsNew .whatsNew-message-date {
|
||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
margin: 0 -12px;
|
margin: 0 -12px;
|
||||||
|
@ -1908,17 +1923,18 @@ panelview[mainview] #PanelUI-whatsNew-content {
|
||||||
inset-inline-end: 6px;
|
inset-inline-end: 6px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 16px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#PanelUI-whatsNew .whatsNew-message-title,
|
#PanelUI-whatsNew .whatsNew-message-title,
|
||||||
#protections-popup-message .whatsNew-message-title {
|
#protections-popup-message .whatsNew-message-title {
|
||||||
|
display: block;
|
||||||
padding-inline-end: 46px;
|
padding-inline-end: 46px;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
margin: 2px 0;
|
margin: 8px 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#PanelUI-whatsNew .whatsNew-message-title-large {
|
#PanelUI-whatsNew .whatsNew-message-title-large {
|
||||||
|
@ -1934,6 +1950,11 @@ panelview[mainview] #PanelUI-whatsNew-content {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#PanelUI-whatsNew .whatsNew-message-content {
|
||||||
|
display: block;
|
||||||
|
margin: 13px 0;
|
||||||
|
}
|
||||||
|
|
||||||
#PanelUI-whatsNew .text-link {
|
#PanelUI-whatsNew .text-link {
|
||||||
background: none;
|
background: none;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче