From d88ff7e8b9ff6047f8891d890abc58e470e97dfc Mon Sep 17 00:00:00 2001 From: Andrei Oprea Date: Sat, 18 Apr 2020 11:24:44 +0000 Subject: [PATCH] Bug 1616280 - Use shadow DOM to hide Remote L10n translations for from local translations r=k88hudson Differential Revision: https://phabricator.services.mozilla.com/D63895 --- .../components/CustomElements/paragraph.js | 67 ++++++ browser/components/newtab/jar.mn | 1 + .../components/newtab/lib/ToolbarPanelHub.jsm | 127 +++++----- .../browser/browser_asrouter_whatsnewpanel.js | 8 + .../test/unit/lib/ToolbarPanelHub.test.js | 226 +++++++----------- .../components/newtab/test/unit/unit-entry.js | 1 + .../shared/customizableui/panelUI.inc.css | 27 ++- 7 files changed, 246 insertions(+), 211 deletions(-) create mode 100644 browser/components/newtab/components/CustomElements/paragraph.js diff --git a/browser/components/newtab/components/CustomElements/paragraph.js b/browser/components/newtab/components/CustomElements/paragraph.js new file mode 100644 index 000000000000..483f709ea908 --- /dev/null +++ b/browser/components/newtab/components/CustomElements/paragraph.js @@ -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); +} diff --git a/browser/components/newtab/jar.mn b/browser/components/newtab/jar.mn index 2eac4d44691e..c6f94e21ad32 100644 --- a/browser/components/newtab/jar.mn +++ b/browser/components/newtab/jar.mn @@ -26,6 +26,7 @@ browser.jar: 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/newtab-render.js (./data/content/newtab-render.js) + res/activity-stream/data/custom-elements/ (./components/CustomElements/*) #ifdef XP_MACOSX res/activity-stream/css/activity-stream.css (./css/activity-stream-mac.css) #elifdef XP_WIN diff --git a/browser/components/newtab/lib/ToolbarPanelHub.jsm b/browser/components/newtab/lib/ToolbarPanelHub.jsm index d05ea64fabf7..ad7c2456b011 100644 --- a/browser/components/newtab/lib/ToolbarPanelHub.jsm +++ b/browser/components/newtab/lib/ToolbarPanelHub.jsm @@ -10,7 +10,6 @@ XPCOMUtils.defineLazyModuleGetters(this, { Services: "resource://gre/modules/Services.jsm", EveryWindow: "resource:///modules/EveryWindow.jsm", PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm", - RemoteL10n: "resource://activity-stream/lib/RemoteL10n.jsm", }); XPCOMUtils.defineLazyServiceGetter( this, @@ -108,6 +107,15 @@ class _ToolbarPanelHub { 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. async enableAppmenuButton() { if ((await this.messages).length) { @@ -170,6 +178,7 @@ class _ToolbarPanelHub { // Render what's new messages into the panel. async renderMessages(win, doc, containerId, options = {}) { + this.maybeLoadCustomElement(win); const messages = (options.force && options.messages) || (await this.messages).sort(this._sortWhatsNewMessages); @@ -178,17 +187,13 @@ class _ToolbarPanelHub { if (messages) { // Targeting attribute state might have changed making new messages // available and old messages invalid, we need to refresh - for (const prevMessageEl of container.querySelectorAll( - ".whatsNew-message" - )) { - container.removeChild(prevMessageEl); - } + this.removeMessages(win, containerId); let previousDate = 0; // Get and store any variable part of the message content this.state.contentArguments = await this._contentArguments(); for (let message of messages) { container.appendChild( - await this._createMessageElements(win, doc, message, previousDate) + this._createMessageElements(win, doc, message, previousDate) ); 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 messageEl = await this._createElement(doc, "div"); + const messageEl = this._createElement(doc, "div"); messageEl.classList.add("whatsNew-message"); // Only render date if it is different from the one rendered before. if (content.published_date !== previousDate) { messageEl.appendChild( - await this._createElement(doc, "p", { + this._createElement(doc, "p", { classList: "whatsNew-message-date", content: new Date(content.published_date).toLocaleDateString( "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.classList.add("whatsNew-message-body"); messageEl.appendChild(wrapperEl); if (content.icon_url) { wrapperEl.classList.add("has-icon"); - const iconEl = await this._createElement(doc, "img"); + const iconEl = this._createElement(doc, "img"); iconEl.src = content.icon_url; 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(await this._createMessageContent(win, doc, content)); + wrapperEl.appendChild(this._createMessageContent(win, doc, content)); if (content.link_text) { - const anchorEl = await this._createElement(doc, "a", { + const anchorEl = this._createElement(doc, "a", { classList: "text-link", content: content.link_text, }); @@ -323,11 +332,11 @@ class _ToolbarPanelHub { /** * Return message title (optional subtitle) and body */ - async _createMessageContent(win, doc, content) { + _createMessageContent(win, doc, content) { const wrapperEl = new win.DocumentFragment(); wrapperEl.appendChild( - await this._createElement(doc, "h2", { + this._createElement(doc, "h2", { classList: "whatsNew-message-title", content: content.title, }) @@ -335,14 +344,14 @@ class _ToolbarPanelHub { switch (content.layout) { case "tracking-protections": - await wrapperEl.appendChild( - await this._createElement(doc, "h4", { + wrapperEl.appendChild( + this._createElement(doc, "h4", { classList: "whatsNew-message-subtitle", content: content.subtitle, }) ); wrapperEl.appendChild( - await this._createElement(doc, "h2", { + this._createElement(doc, "h2", { classList: "whatsNew-message-title-large", content: this.state.contentArguments[ content.layout_title_content_variable @@ -353,32 +362,40 @@ class _ToolbarPanelHub { } wrapperEl.appendChild( - await this._createElement(doc, "p", { content: content.body }) + this._createElement(doc, "p", { + content: content.body, + classList: "whatsNew-message-content", + }) ); return wrapperEl; } - async _createHeroElement(win, doc, message) { - const messageEl = await this._createElement(doc, "div"); + _createHeroElement(win, doc, message) { + this.maybeLoadCustomElement(win); + + const messageEl = this._createElement(doc, "div"); messageEl.setAttribute("id", "protections-popup-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"); messageEl.appendChild(wrapperEl); wrapperEl.appendChild( - await this._createElement(doc, "h2", { + this._createElement(doc, "h2", { classList: "whatsNew-message-title", content: message.content.title, }) ); 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) { - let linkEl = await this._createElement(doc, "a", { + let linkEl = this._createElement(doc, "a", { classList: "text-link", content: message.content.link_text, }); @@ -392,14 +409,17 @@ class _ToolbarPanelHub { return messageEl; } - async _createElement(doc, elem, options = {}) { - const node = doc.createElementNS("http://www.w3.org/1999/xhtml", elem); + _createElement(doc, elem, options = {}) { + 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) { node.classList.add(options.classList); } - if (options.content) { - await this._setString(node, options.content); - } + this._setString(node, options.content); return node; } @@ -447,41 +467,19 @@ class _ToolbarPanelHub { // If `string_id` is present it means we are relying on fluent for translations. // Otherwise, we have a vanilla string. - async _setString(el, stringObj) { + _setString(el, stringObj) { if (stringObj && stringObj.string_id) { - const [{ value }] = await RemoteL10n.l10n.formatMessages([ - { - id: stringObj.string_id, - // Pass all available arguments to Fluent - args: this.state.contentArguments, - }, - ]); - el.textContent = value; + for (let [fluentId, value] of Object.entries( + this.state.contentArguments || {} + )) { + el.setAttribute(`fluent-variable-${fluentId}`, value); + } + el.setAttribute("fluent-remote-id", stringObj.string_id); } else { 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) { this.maybeInsertFTL(win); await this._showElement( @@ -505,10 +503,9 @@ class _ToolbarPanelHub { this._hideElement(win.browser.ownerDocument, TOOLBAR_BUTTON_ID); } - async _showElement(document, id, string_id) { + _showElement(document, id, string_id) { const el = document.getElementById(id); - await this._setTextAttribute(el, "label", { string_id }); - await this._setTextAttribute(el, "tooltiptext", { string_id }); + document.l10n.setAttributes(el, string_id); el.removeAttribute("hidden"); } @@ -568,7 +565,7 @@ class _ToolbarPanelHub { triggerId: "protectionsPanelOpen", }); if (message) { - const messageEl = await this._createHeroElement(win, doc, message); + const messageEl = this._createHeroElement(win, doc, message); container.appendChild(messageEl); infoButton.addEventListener("click", toggleMessage); this.sendUserEventTelemetry(win, "IMPRESSION", message); diff --git a/browser/components/newtab/test/browser/browser_asrouter_whatsnewpanel.js b/browser/components/newtab/test/browser/browser_asrouter_whatsnewpanel.js index d5f548119cd2..9cb4d92a6e5b 100644 --- a/browser/components/newtab/test/browser/browser_asrouter_whatsnewpanel.js +++ b/browser/components/newtab/test/browser/browser_asrouter_whatsnewpanel.js @@ -77,6 +77,14 @@ add_task(async function test_with_rs_messages() { "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"); // Clean up and remove messages ToolbarPanelHub.disableAppmenuButton(); diff --git a/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js b/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js index cd12fe1f0a94..705407e13856 100644 --- a/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js +++ b/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js @@ -12,6 +12,7 @@ describe("ToolbarPanelHub", () => { let fakeWindow; let fakeElementById; let createdElements = []; + let createdCustomElements = []; let eventListeners = {}; let addObserverStub; let removeObserverStub; @@ -24,6 +25,7 @@ describe("ToolbarPanelHub", () => { let getEventsByDateRangeStub; let handleUserActionStub; let defaultSearchStub; + let scriptloaderStub; beforeEach(async () => { sandbox = sinon.createSandbox(); @@ -56,10 +58,34 @@ describe("ToolbarPanelHub", () => { }, appendChild: sandbox.stub(), setAttribute: sandbox.stub(), + textContent: "", }; createdElements.push(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 = { // eslint-disable-next-line object-shorthand @@ -79,11 +105,13 @@ describe("ToolbarPanelHub", () => { panel: fakeElementById, whatsNewPanel: fakeElementById, }, + customElements: { get: sandbox.stub() }, }; everyWindowStub = { registerCallback: sandbox.stub(), unregisterCallback: sandbox.stub(), }; + scriptloaderStub = { loadSubScript: sandbox.stub() }; addObserverStub = sandbox.stub(); removeObserverStub = sandbox.stub(); getBoolPrefStub = sandbox.stub(); @@ -108,6 +136,7 @@ describe("ToolbarPanelHub", () => { setBoolPref: setBoolPrefStub, }, search: defaultSearchStub, + scriptloader: scriptloaderStub, }, PrivateBrowsingUtils: { isBrowserPrivate: isBrowserPrivateStub, @@ -116,13 +145,6 @@ describe("ToolbarPanelHub", () => { getEarliestRecordedDate: getEarliestRecordedDateStub, getEventsByDateRange: getEventsByDateRangeStub, }, - RemoteL10n: { - l10n: { - translateElements: sandbox.stub(), - translateFragment: sandbox.stub(), - formatMessages: sandbox.stub().resolves([{}]), - }, - }, }); }); afterEach(() => { @@ -131,6 +153,7 @@ describe("ToolbarPanelHub", () => { globals.restore(); eventListeners = {}; createdElements = []; + createdCustomElements = []; }); it("should create an instance", () => { assert.ok(instance); @@ -290,6 +313,32 @@ describe("ToolbarPanelHub", () => { 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 () => { const messages = (await PanelTestProvider.getMessages()).filter( m => m.template === "whatsnew_panel_message" @@ -311,19 +360,30 @@ describe("ToolbarPanelHub", () => { await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); 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") { - 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") { - assert.ok(createdElements.find(el => el.tagName === "h4")); assert.ok( createdElements.find( 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. eventListeners.mouseup(); @@ -333,17 +393,18 @@ describe("ToolbarPanelHub", () => { const messages = (await PanelTestProvider.getMessages()).filter( m => m.template === "whatsnew_panel_message" ); + const removeStub = sandbox.stub(); 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); await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - assert.calledThrice(fakeElementById.removeChild); - assert.equal(fakeElementById.removeChild.firstCall.args[0], "a"); - assert.equal(fakeElementById.removeChild.secondCall.args[0], "b"); + assert.calledTwice(removeStub); }); it("should sort based on order field value", async () => { const messages = (await PanelTestProvider.getMessages()).filter( @@ -385,38 +446,7 @@ describe("ToolbarPanelHub", () => { "Firefox Send Logo" ); }); - it("should accept fluent ids for image 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); - - 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 () => { + it("should set state values as data-attribute", async () => { const [message] = (await PanelTestProvider.getMessages()).filter( m => m.template === "whatsnew_panel_message" && @@ -427,102 +457,13 @@ describe("ToolbarPanelHub", () => { await instance.renderMessages(fakeWindow, fakeDocument, "container-id"); - assert.calledWithExactly(global.RemoteL10n.l10n.formatMessages, [ - { - id: message.content.subtitle.string_id, - args: instance.state.contentArguments, - }, - ]); - }); - it("should correctly compute blocker trackers and date", async () => { - const messages = (await PanelTestProvider.getMessages()).filter( - m => m.template === "whatsnew_panel_message" + // Currently this.state.contentArguments has 9 different entries + assert.callCount(createdCustomElements[0].setAttribute, 9); + assert.calledWithExactly( + createdCustomElements[0].setAttribute, + "fluent-variable-searchEngineName", + defaultSearchStub.defaultEngine.name ); - 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 () => { const messages = (await PanelTestProvider.getMessages()).filter( @@ -745,7 +686,6 @@ describe("ToolbarPanelHub", () => { it("should call removeMessages when forcing a message to show", () => { instance.forceShowMessage(browser, messages); - assert.calledOnce(removeMessagesSpy); assert.calledWithExactly(removeMessagesSpy, fakeWindow, panelSelector); }); it("should call renderMessages when forcing a message to show", () => { diff --git a/browser/components/newtab/test/unit/unit-entry.js b/browser/components/newtab/test/unit/unit-entry.js index 3c867fbdf31b..f380b39e7ea3 100644 --- a/browser/components/newtab/test/unit/unit-entry.js +++ b/browser/components/newtab/test/unit/unit-entry.js @@ -345,6 +345,7 @@ const TEST_GLOBAL = { }, ww: { registerNotification() {}, unregisterNotification() {} }, appinfo: { appBuildID: "20180710100040", version: "69.0a1" }, + scriptloader: { loadSubScript: () => {} }, }, XPCOMUtils: { defineLazyGetter(object, name, f) { diff --git a/browser/themes/shared/customizableui/panelUI.inc.css b/browser/themes/shared/customizableui/panelUI.inc.css index ea2644318ecd..66f6178d0787 100644 --- a/browser/themes/shared/customizableui/panelUI.inc.css +++ b/browser/themes/shared/customizableui/panelUI.inc.css @@ -1854,6 +1854,11 @@ toolbarpaletteitem[place="menu-panel"] > .subviewbutton-nav::after { text-decoration: underline; } +#protections-popup-message .protections-popup-content { + display: block; + margin: 12px 0; +} + panelview[mainview] #PanelUI-whatsNew-content { height: 43em; } @@ -1868,13 +1873,23 @@ panelview[mainview] #PanelUI-whatsNew-content { 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: ""; display: block; height: 1px; + width: 104%; + margin-inline-start: -2%; background: var(--panel-separator-color); } +#PanelUI-whatsNew .whatsNew-message-date + .whatsNew-message-body::before { + display: none; +} + #PanelUI-whatsNew .whatsNew-message-date { font-size: .85em; margin: 0 -12px; @@ -1908,17 +1923,18 @@ panelview[mainview] #PanelUI-whatsNew-content { inset-inline-end: 6px; height: 32px; position: absolute; - top: 10px; + top: 16px; width: 32px; } #PanelUI-whatsNew .whatsNew-message-title, #protections-popup-message .whatsNew-message-title { + display: block; padding-inline-end: 46px; font-size: 1.3em; font-weight: 600; line-height: 1.4em; - margin: 2px 0; + margin: 8px 0 2px; } #PanelUI-whatsNew .whatsNew-message-title-large { @@ -1934,6 +1950,11 @@ panelview[mainview] #PanelUI-whatsNew-content { font-weight: normal; } +#PanelUI-whatsNew .whatsNew-message-content { + display: block; + margin: 13px 0; +} + #PanelUI-whatsNew .text-link { background: none; border: 0;