2019-07-12 00:50:17 +03:00
|
|
|
/* 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";
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
2019-07-12 00:50:17 +03:00
|
|
|
);
|
2019-08-24 09:35:25 +03:00
|
|
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
|
|
|
Services: "resource://gre/modules/Services.jsm",
|
|
|
|
EveryWindow: "resource:///modules/EveryWindow.jsm",
|
|
|
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
2020-05-07 19:42:15 +03:00
|
|
|
Preferences: "resource://gre/modules/Preferences.jsm",
|
2020-05-20 18:26:31 +03:00
|
|
|
SpecialMessageActions:
|
|
|
|
"resource://messaging-system/lib/SpecialMessageActions.jsm",
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n: "resource://activity-stream/lib/RemoteL10n.jsm",
|
2019-08-24 09:35:25 +03:00
|
|
|
});
|
2020-07-09 03:35:30 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"PanelMultiView",
|
|
|
|
"resource:///modules/PanelMultiView.jsm"
|
|
|
|
);
|
2019-08-24 09:35:25 +03:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(
|
2019-07-29 09:55:43 +03:00
|
|
|
this,
|
2019-08-24 09:35:25 +03:00
|
|
|
"TrackingDBService",
|
|
|
|
"@mozilla.org/tracking-db-service;1",
|
|
|
|
"nsITrackingDBService"
|
2019-07-29 09:55:43 +03:00
|
|
|
);
|
2019-07-12 00:50:17 +03:00
|
|
|
|
2019-11-26 18:18:55 +03:00
|
|
|
const idToTextMap = new Map([
|
2019-11-28 20:14:21 +03:00
|
|
|
[Ci.nsITrackingDBService.TRACKERS_ID, "trackerCount"],
|
|
|
|
[Ci.nsITrackingDBService.TRACKING_COOKIES_ID, "cookieCount"],
|
|
|
|
[Ci.nsITrackingDBService.CRYPTOMINERS_ID, "cryptominerCount"],
|
|
|
|
[Ci.nsITrackingDBService.FINGERPRINTERS_ID, "fingerprinterCount"],
|
|
|
|
[Ci.nsITrackingDBService.SOCIAL_ID, "socialCount"],
|
2019-11-26 18:18:55 +03:00
|
|
|
]);
|
|
|
|
|
2019-07-12 00:50:17 +03:00
|
|
|
const WHATSNEW_ENABLED_PREF = "browser.messaging-system.whatsNewPanel.enabled";
|
2019-08-07 20:57:48 +03:00
|
|
|
const PROTECTIONS_PANEL_INFOMSG_PREF =
|
|
|
|
"browser.protections_panel.infoMessage.seen";
|
2019-07-12 00:50:17 +03:00
|
|
|
|
|
|
|
const TOOLBAR_BUTTON_ID = "whats-new-menu-button";
|
|
|
|
const APPMENU_BUTTON_ID = "appMenu-whatsnew-button";
|
|
|
|
|
|
|
|
const BUTTON_STRING_ID = "cfr-whatsnew-button";
|
2019-09-14 01:57:40 +03:00
|
|
|
const WHATS_NEW_PANEL_SELECTOR = "PanelUI-whatsNew-message-container";
|
2019-07-12 00:50:17 +03:00
|
|
|
|
|
|
|
class _ToolbarPanelHub {
|
|
|
|
constructor() {
|
2019-07-29 09:55:43 +03:00
|
|
|
this.triggerId = "whatsNewPanelOpened";
|
2019-07-12 00:50:17 +03:00
|
|
|
this._showAppmenuButton = this._showAppmenuButton.bind(this);
|
|
|
|
this._hideAppmenuButton = this._hideAppmenuButton.bind(this);
|
|
|
|
this._showToolbarButton = this._showToolbarButton.bind(this);
|
|
|
|
this._hideToolbarButton = this._hideToolbarButton.bind(this);
|
2019-08-07 20:57:48 +03:00
|
|
|
this.insertProtectionPanelMessage = this.insertProtectionPanelMessage.bind(
|
|
|
|
this
|
|
|
|
);
|
|
|
|
|
2019-11-20 22:45:18 +03:00
|
|
|
this.state = {};
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2020-05-20 18:26:31 +03:00
|
|
|
async init(waitForInitialized, { getMessages, dispatch }) {
|
2019-07-12 00:50:17 +03:00
|
|
|
this._getMessages = getMessages;
|
2019-07-29 09:55:43 +03:00
|
|
|
this._dispatch = dispatch;
|
2019-07-19 02:53:04 +03:00
|
|
|
// Wait for ASRouter messages to become available in order to know
|
|
|
|
// if we can show the What's New panel
|
|
|
|
await waitForInitialized;
|
2020-05-07 19:42:15 +03:00
|
|
|
// Enable the application menu button so that the user can access
|
|
|
|
// the panel outside of the toolbar button
|
2020-05-13 23:02:30 +03:00
|
|
|
await this.enableAppmenuButton();
|
2019-08-07 20:57:48 +03:00
|
|
|
|
|
|
|
this.state = {
|
|
|
|
protectionPanelMessageSeen: Services.prefs.getBoolPref(
|
|
|
|
PROTECTIONS_PANEL_INFOMSG_PREF,
|
|
|
|
false
|
|
|
|
),
|
|
|
|
};
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
uninit() {
|
|
|
|
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
|
2020-04-25 02:43:50 +03:00
|
|
|
EveryWindow.unregisterCallback(APPMENU_BUTTON_ID);
|
2019-07-19 02:53:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
get messages() {
|
|
|
|
return this._getMessages({
|
|
|
|
template: "whatsnew_panel_message",
|
|
|
|
triggerId: "whatsNewPanelOpened",
|
|
|
|
returnAll: true,
|
|
|
|
});
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2020-05-07 19:42:15 +03:00
|
|
|
toggleWhatsNewPref(event) {
|
2020-05-21 12:45:37 +03:00
|
|
|
Preferences.set(WHATSNEW_ENABLED_PREF, event.target.checked);
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
maybeInsertFTL(win) {
|
|
|
|
win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl");
|
2019-08-29 17:12:59 +03:00
|
|
|
win.MozXULElement.insertFTLIfNeeded("browser/branding/brandings.ftl");
|
|
|
|
win.MozXULElement.insertFTLIfNeeded("browser/branding/sync-brand.ftl");
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2020-04-18 14:24:44 +03:00
|
|
|
maybeLoadCustomElement(win) {
|
|
|
|
if (!win.customElements.get("remote-text")) {
|
|
|
|
Services.scriptloader.loadSubScript(
|
|
|
|
"resource://activity-stream/data/custom-elements/paragraph.js",
|
|
|
|
win
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:50:17 +03:00
|
|
|
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
|
2019-07-19 02:53:04 +03:00
|
|
|
async enableAppmenuButton() {
|
|
|
|
if ((await this.messages).length) {
|
|
|
|
EveryWindow.registerCallback(
|
|
|
|
APPMENU_BUTTON_ID,
|
|
|
|
this._showAppmenuButton,
|
|
|
|
this._hideAppmenuButton
|
|
|
|
);
|
|
|
|
}
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2020-02-12 17:49:53 +03:00
|
|
|
// Removes the button from the Appmenu.
|
|
|
|
// Only used in tests.
|
|
|
|
disableAppmenuButton() {
|
|
|
|
EveryWindow.unregisterCallback(APPMENU_BUTTON_ID);
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:50:17 +03:00
|
|
|
// Turns on the Toolbar button for all open windows and future windows.
|
2019-07-19 02:53:04 +03:00
|
|
|
async enableToolbarButton() {
|
|
|
|
if ((await this.messages).length) {
|
|
|
|
EveryWindow.registerCallback(
|
|
|
|
TOOLBAR_BUTTON_ID,
|
|
|
|
this._showToolbarButton,
|
|
|
|
this._hideToolbarButton
|
|
|
|
);
|
|
|
|
}
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2019-07-16 16:52:26 +03:00
|
|
|
// When the panel is hidden we want to run some cleanup
|
|
|
|
_onPanelHidden(win) {
|
|
|
|
const panelContainer = win.document.getElementById(
|
|
|
|
"customizationui-widget-panel"
|
|
|
|
);
|
|
|
|
// When the panel is hidden we want to remove any toolbar buttons that
|
|
|
|
// might have been added as an entry point to the panel
|
|
|
|
const removeToolbarButton = () => {
|
|
|
|
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
|
|
|
|
};
|
|
|
|
if (!panelContainer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
panelContainer.addEventListener("popuphidden", removeToolbarButton, {
|
|
|
|
once: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
// Newer messages first and use `order` field to decide between messages
|
|
|
|
// with the same timestamp
|
|
|
|
_sortWhatsNewMessages(m1, m2) {
|
|
|
|
// Sort by published_date in descending order.
|
|
|
|
if (m1.content.published_date === m2.content.published_date) {
|
|
|
|
// Ascending order
|
|
|
|
return m1.order - m2.order;
|
|
|
|
}
|
|
|
|
if (m1.content.published_date > m2.content.published_date) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:50:17 +03:00
|
|
|
// Render what's new messages into the panel.
|
2019-09-14 01:57:40 +03:00
|
|
|
async renderMessages(win, doc, containerId, options = {}) {
|
2020-05-07 19:42:15 +03:00
|
|
|
// Set the checked status of the footer checkbox
|
|
|
|
let value = Preferences.get(WHATSNEW_ENABLED_PREF);
|
|
|
|
let checkbox = win.document.getElementById("panelMenu-toggleWhatsNew");
|
|
|
|
|
|
|
|
checkbox.checked = value;
|
|
|
|
|
2020-04-18 14:24:44 +03:00
|
|
|
this.maybeLoadCustomElement(win);
|
2019-09-14 01:57:40 +03:00
|
|
|
const messages =
|
|
|
|
(options.force && options.messages) ||
|
|
|
|
(await this.messages).sort(this._sortWhatsNewMessages);
|
2020-07-09 03:35:30 +03:00
|
|
|
const container = PanelMultiView.getViewNode(doc, containerId);
|
2019-07-12 00:50:17 +03:00
|
|
|
|
2019-10-04 20:06:01 +03:00
|
|
|
if (messages) {
|
|
|
|
// Targeting attribute state might have changed making new messages
|
|
|
|
// available and old messages invalid, we need to refresh
|
2020-04-18 14:24:44 +03:00
|
|
|
this.removeMessages(win, containerId);
|
2019-07-12 00:50:17 +03:00
|
|
|
let previousDate = 0;
|
2019-08-24 09:35:25 +03:00
|
|
|
// Get and store any variable part of the message content
|
|
|
|
this.state.contentArguments = await this._contentArguments();
|
2019-07-29 09:55:43 +03:00
|
|
|
for (let message of messages) {
|
2019-07-12 00:50:17 +03:00
|
|
|
container.appendChild(
|
2020-04-18 14:24:44 +03:00
|
|
|
this._createMessageElements(win, doc, message, previousDate)
|
2019-07-12 00:50:17 +03:00
|
|
|
);
|
2019-07-29 09:55:43 +03:00
|
|
|
previousDate = message.content.published_date;
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-16 16:52:26 +03:00
|
|
|
this._onPanelHidden(win);
|
2019-07-29 09:55:43 +03:00
|
|
|
|
|
|
|
// Panel impressions are not associated with one particular message
|
|
|
|
// but with a set of messages. We concatenate message ids and send them
|
|
|
|
// back for every impression.
|
|
|
|
const eventId = {
|
|
|
|
id: messages
|
|
|
|
.map(({ id }) => id)
|
|
|
|
.sort()
|
|
|
|
.join(","),
|
|
|
|
};
|
|
|
|
// Check `mainview` attribute to determine if the panel is shown as a
|
|
|
|
// subview (inside the application menu) or as a toolbar dropdown.
|
|
|
|
// https://searchfox.org/mozilla-central/rev/07f7390618692fa4f2a674a96b9b677df3a13450/browser/components/customizableui/PanelMultiView.jsm#1268
|
|
|
|
const mainview = win.PanelUI.whatsNewPanel.hasAttribute("mainview");
|
|
|
|
this.sendUserEventTelemetry(win, "IMPRESSION", eventId, {
|
|
|
|
value: { view: mainview ? "toolbar_dropdown" : "application_menu" },
|
|
|
|
});
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2019-09-14 01:57:40 +03:00
|
|
|
removeMessages(win, containerId) {
|
|
|
|
const doc = win.document;
|
2020-07-09 03:35:30 +03:00
|
|
|
const messageNodes = PanelMultiView.getViewNode(
|
|
|
|
doc,
|
|
|
|
containerId
|
|
|
|
).querySelectorAll(".whatsNew-message");
|
2019-09-14 01:57:40 +03:00
|
|
|
for (const messageNode of messageNodes) {
|
|
|
|
messageNode.remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
/**
|
2019-09-26 23:01:28 +03:00
|
|
|
* Dispatch the action defined in the message and user telemetry event.
|
2019-08-24 09:35:25 +03:00
|
|
|
*/
|
2019-09-26 23:01:28 +03:00
|
|
|
_dispatchUserAction(win, message) {
|
2019-11-23 03:08:00 +03:00
|
|
|
let url;
|
|
|
|
try {
|
|
|
|
// Set platform specific path variables for SUMO articles
|
|
|
|
url = Services.urlFormatter.formatURL(message.content.cta_url);
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError(e);
|
|
|
|
url = message.content.cta_url;
|
|
|
|
}
|
2020-05-20 18:26:31 +03:00
|
|
|
SpecialMessageActions.handleAction(
|
|
|
|
{
|
2019-09-26 23:01:28 +03:00
|
|
|
type: message.content.cta_type,
|
2019-08-24 09:35:25 +03:00
|
|
|
data: {
|
2019-11-23 03:08:00 +03:00
|
|
|
args: url,
|
2020-06-17 16:50:12 +03:00
|
|
|
where: message.content.cta_where || "tabshifted",
|
2019-08-24 09:35:25 +03:00
|
|
|
},
|
2019-09-26 23:01:28 +03:00
|
|
|
},
|
2020-05-20 18:26:31 +03:00
|
|
|
win.browser
|
|
|
|
);
|
2019-09-26 23:01:28 +03:00
|
|
|
|
|
|
|
this.sendUserEventTelemetry(win, "CLICK", message);
|
|
|
|
}
|
2019-08-24 09:35:25 +03:00
|
|
|
|
2019-09-26 23:01:28 +03:00
|
|
|
/**
|
|
|
|
* Attach event listener to dispatch message defined action.
|
|
|
|
*/
|
2020-01-11 01:39:17 +03:00
|
|
|
_attachCommandListener(win, element, message) {
|
2019-09-26 23:01:28 +03:00
|
|
|
// Add event listener for `mouseup` not to overlap with the
|
|
|
|
// `mousedown` & `click` events dispatched from PanelMultiView.jsm
|
|
|
|
// https://searchfox.org/mozilla-central/rev/7531325c8660cfa61bf71725f83501028178cbb9/browser/components/customizableui/PanelMultiView.jsm#1830-1837
|
|
|
|
element.addEventListener("mouseup", () => {
|
|
|
|
this._dispatchUserAction(win, message);
|
2019-08-24 09:35:25 +03:00
|
|
|
});
|
2020-01-11 01:39:17 +03:00
|
|
|
element.addEventListener("keyup", e => {
|
|
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
|
|
this._dispatchUserAction(win, message);
|
|
|
|
}
|
|
|
|
});
|
2019-08-24 09:35:25 +03:00
|
|
|
}
|
|
|
|
|
2020-04-18 14:24:44 +03:00
|
|
|
_createMessageElements(win, doc, message, previousDate) {
|
2019-07-29 09:55:43 +03:00
|
|
|
const { content } = message;
|
2020-06-02 13:02:32 +03:00
|
|
|
const messageEl = RemoteL10n.createElement(doc, "div");
|
2019-07-12 00:50:17 +03:00
|
|
|
messageEl.classList.add("whatsNew-message");
|
|
|
|
|
2019-08-26 23:14:50 +03:00
|
|
|
// Only render date if it is different from the one rendered before.
|
|
|
|
if (content.published_date !== previousDate) {
|
|
|
|
messageEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "p", {
|
2019-08-26 23:14:50 +03:00
|
|
|
classList: "whatsNew-message-date",
|
|
|
|
content: new Date(content.published_date).toLocaleDateString(
|
|
|
|
"default",
|
|
|
|
{
|
|
|
|
month: "long",
|
|
|
|
day: "numeric",
|
|
|
|
year: "numeric",
|
|
|
|
}
|
|
|
|
),
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-06-02 13:02:32 +03:00
|
|
|
const wrapperEl = RemoteL10n.createElement(doc, "button");
|
2019-09-26 23:01:28 +03:00
|
|
|
wrapperEl.doCommand = () => this._dispatchUserAction(win, message);
|
2019-07-12 00:50:17 +03:00
|
|
|
wrapperEl.classList.add("whatsNew-message-body");
|
|
|
|
messageEl.appendChild(wrapperEl);
|
|
|
|
|
|
|
|
if (content.icon_url) {
|
|
|
|
wrapperEl.classList.add("has-icon");
|
2020-06-02 13:02:32 +03:00
|
|
|
const iconEl = RemoteL10n.createElement(doc, "img");
|
2019-07-12 00:50:17 +03:00
|
|
|
iconEl.src = content.icon_url;
|
|
|
|
iconEl.classList.add("whatsNew-message-icon");
|
2020-04-18 14:24:44 +03:00
|
|
|
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);
|
|
|
|
}
|
2019-07-12 00:50:17 +03:00
|
|
|
wrapperEl.appendChild(iconEl);
|
|
|
|
}
|
|
|
|
|
2020-04-18 14:24:44 +03:00
|
|
|
wrapperEl.appendChild(this._createMessageContent(win, doc, content));
|
2019-07-12 00:50:17 +03:00
|
|
|
|
|
|
|
if (content.link_text) {
|
2020-06-02 13:02:32 +03:00
|
|
|
const anchorEl = RemoteL10n.createElement(doc, "a", {
|
2019-09-17 21:56:03 +03:00
|
|
|
classList: "text-link",
|
|
|
|
content: content.link_text,
|
|
|
|
});
|
2019-09-26 23:01:28 +03:00
|
|
|
anchorEl.doCommand = () => this._dispatchUserAction(win, message);
|
2019-09-17 21:56:03 +03:00
|
|
|
wrapperEl.appendChild(anchorEl);
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
// Attach event listener on entire message container
|
2020-01-11 01:39:17 +03:00
|
|
|
this._attachCommandListener(win, messageEl, message);
|
2019-08-24 09:35:25 +03:00
|
|
|
|
2019-07-12 00:50:17 +03:00
|
|
|
return messageEl;
|
|
|
|
}
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
/**
|
|
|
|
* Return message title (optional subtitle) and body
|
|
|
|
*/
|
2020-04-18 14:24:44 +03:00
|
|
|
_createMessageContent(win, doc, content) {
|
2019-08-24 09:35:25 +03:00
|
|
|
const wrapperEl = new win.DocumentFragment();
|
|
|
|
|
|
|
|
wrapperEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "h2", {
|
2019-08-24 09:35:25 +03:00
|
|
|
classList: "whatsNew-message-title",
|
|
|
|
content: content.title,
|
2020-06-02 13:02:32 +03:00
|
|
|
attributes: this.state.contentArguments,
|
2019-08-24 09:35:25 +03:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
switch (content.layout) {
|
|
|
|
case "tracking-protections":
|
2020-04-18 14:24:44 +03:00
|
|
|
wrapperEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "h4", {
|
2019-08-24 09:35:25 +03:00
|
|
|
classList: "whatsNew-message-subtitle",
|
|
|
|
content: content.subtitle,
|
2020-06-02 13:02:32 +03:00
|
|
|
attributes: this.state.contentArguments,
|
2019-08-24 09:35:25 +03:00
|
|
|
})
|
|
|
|
);
|
|
|
|
wrapperEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "h2", {
|
2019-08-24 09:35:25 +03:00
|
|
|
classList: "whatsNew-message-title-large",
|
2019-11-28 20:14:21 +03:00
|
|
|
content: this.state.contentArguments[
|
|
|
|
content.layout_title_content_variable
|
|
|
|
],
|
2020-06-02 13:02:32 +03:00
|
|
|
attributes: this.state.contentArguments,
|
2019-08-24 09:35:25 +03:00
|
|
|
})
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wrapperEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "p", {
|
2020-04-18 14:24:44 +03:00
|
|
|
content: content.body,
|
|
|
|
classList: "whatsNew-message-content",
|
2020-06-02 13:02:32 +03:00
|
|
|
attributes: this.state.contentArguments,
|
2020-04-18 14:24:44 +03:00
|
|
|
})
|
2019-08-24 09:35:25 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
return wrapperEl;
|
|
|
|
}
|
|
|
|
|
2020-04-18 14:24:44 +03:00
|
|
|
_createHeroElement(win, doc, message) {
|
|
|
|
this.maybeLoadCustomElement(win);
|
|
|
|
|
2020-06-02 13:02:32 +03:00
|
|
|
const messageEl = RemoteL10n.createElement(doc, "div");
|
2019-08-07 20:57:48 +03:00
|
|
|
messageEl.setAttribute("id", "protections-popup-message");
|
|
|
|
messageEl.classList.add("whatsNew-hero-message");
|
2020-06-02 13:02:32 +03:00
|
|
|
const wrapperEl = RemoteL10n.createElement(doc, "div");
|
2019-08-07 20:57:48 +03:00
|
|
|
wrapperEl.classList.add("whatsNew-message-body");
|
|
|
|
messageEl.appendChild(wrapperEl);
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
wrapperEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "h2", {
|
2019-08-24 09:35:25 +03:00
|
|
|
classList: "whatsNew-message-title",
|
|
|
|
content: message.content.title,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
wrapperEl.appendChild(
|
2020-06-02 13:02:32 +03:00
|
|
|
RemoteL10n.createElement(doc, "p", {
|
2020-04-18 14:24:44 +03:00
|
|
|
classList: "protections-popup-content",
|
|
|
|
content: message.content.body,
|
|
|
|
})
|
2019-08-24 09:35:25 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
if (message.content.link_text) {
|
2020-06-02 13:02:32 +03:00
|
|
|
let linkEl = RemoteL10n.createElement(doc, "a", {
|
2019-09-30 14:39:34 +03:00
|
|
|
classList: "text-link",
|
|
|
|
content: message.content.link_text,
|
|
|
|
});
|
2020-01-11 01:39:17 +03:00
|
|
|
linkEl.disabled = true;
|
2019-09-30 14:39:34 +03:00
|
|
|
wrapperEl.appendChild(linkEl);
|
2020-01-11 01:39:17 +03:00
|
|
|
this._attachCommandListener(win, linkEl, message);
|
2019-09-30 14:39:34 +03:00
|
|
|
} else {
|
2020-01-11 01:39:17 +03:00
|
|
|
this._attachCommandListener(win, wrapperEl, message);
|
2019-08-07 20:57:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return messageEl;
|
|
|
|
}
|
|
|
|
|
2019-08-24 09:35:25 +03:00
|
|
|
async _contentArguments() {
|
2020-02-14 19:42:42 +03:00
|
|
|
const { defaultEngine } = Services.search;
|
2019-08-24 09:35:25 +03:00
|
|
|
// Between now and 6 weeks ago
|
|
|
|
const dateTo = new Date();
|
|
|
|
const dateFrom = new Date(dateTo.getTime() - 42 * 24 * 60 * 60 * 1000);
|
|
|
|
const eventsByDate = await TrackingDBService.getEventsByDateRange(
|
|
|
|
dateFrom,
|
|
|
|
dateTo
|
|
|
|
);
|
2019-11-28 20:14:21 +03:00
|
|
|
// Make sure we set all types of possible values to 0 because they might
|
|
|
|
// be referenced by fluent strings
|
|
|
|
let totalEvents = { blockedCount: 0 };
|
|
|
|
for (let blockedType of idToTextMap.values()) {
|
|
|
|
totalEvents[blockedType] = 0;
|
|
|
|
}
|
|
|
|
// Count all events in the past 6 weeks. Returns an object with:
|
2019-11-26 18:18:55 +03:00
|
|
|
// `blockedCount` total number of blocked resources
|
|
|
|
// {tracker|cookie|social...} breakdown by event type as defined by `idToTextMap`
|
2019-11-28 20:14:21 +03:00
|
|
|
totalEvents = eventsByDate.reduce((acc, day) => {
|
|
|
|
const type = day.getResultByName("type");
|
|
|
|
const count = day.getResultByName("count");
|
|
|
|
acc[idToTextMap.get(type)] = (acc[idToTextMap.get(type)] || 0) + count;
|
|
|
|
acc.blockedCount += count;
|
|
|
|
return acc;
|
|
|
|
}, totalEvents);
|
2019-08-24 09:35:25 +03:00
|
|
|
return {
|
|
|
|
// Keys need to match variable names used in asrouter.ftl
|
|
|
|
// `earliestDate` will be either 6 weeks ago or when tracking recording
|
|
|
|
// started. Whichever is more recent.
|
2019-11-26 18:18:55 +03:00
|
|
|
earliestDate: Math.max(
|
|
|
|
new Date(await TrackingDBService.getEarliestRecordedDate()),
|
|
|
|
dateFrom
|
|
|
|
),
|
|
|
|
...totalEvents,
|
2020-02-14 19:42:42 +03:00
|
|
|
// Passing in `undefined` as string for the Fluent variable name
|
|
|
|
// in order to match and select the message that does not require
|
|
|
|
// the variable.
|
|
|
|
searchEngineName: defaultEngine ? defaultEngine.name : "undefined",
|
2019-08-24 09:35:25 +03:00
|
|
|
};
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
2019-11-20 22:45:18 +03:00
|
|
|
async _showAppmenuButton(win) {
|
2019-07-12 00:50:17 +03:00
|
|
|
this.maybeInsertFTL(win);
|
2019-11-20 22:45:18 +03:00
|
|
|
await this._showElement(
|
2019-07-12 00:50:17 +03:00
|
|
|
win.browser.ownerDocument,
|
|
|
|
APPMENU_BUTTON_ID,
|
|
|
|
BUTTON_STRING_ID
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-08-21 15:16:35 +03:00
|
|
|
_hideAppmenuButton(win, windowClosed) {
|
|
|
|
// No need to do something if the window is going away
|
|
|
|
if (!windowClosed) {
|
|
|
|
this._hideElement(win.browser.ownerDocument, APPMENU_BUTTON_ID);
|
|
|
|
}
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
_showToolbarButton(win) {
|
|
|
|
const document = win.browser.ownerDocument;
|
|
|
|
this.maybeInsertFTL(win);
|
2019-11-20 22:45:18 +03:00
|
|
|
return this._showElement(document, TOOLBAR_BUTTON_ID, BUTTON_STRING_ID);
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
_hideToolbarButton(win) {
|
|
|
|
this._hideElement(win.browser.ownerDocument, TOOLBAR_BUTTON_ID);
|
|
|
|
}
|
|
|
|
|
2020-04-18 14:24:44 +03:00
|
|
|
_showElement(document, id, string_id) {
|
2020-08-10 20:09:55 +03:00
|
|
|
const el = PanelMultiView.getViewNode(document, id);
|
2020-04-18 14:24:44 +03:00
|
|
|
document.l10n.setAttributes(el, string_id);
|
2019-07-12 00:50:17 +03:00
|
|
|
el.removeAttribute("hidden");
|
|
|
|
}
|
|
|
|
|
|
|
|
_hideElement(document, id) {
|
2020-08-21 15:16:35 +03:00
|
|
|
const el = PanelMultiView.getViewNode(document, id);
|
|
|
|
if (el) {
|
|
|
|
el.setAttribute("hidden", true);
|
|
|
|
}
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
2019-07-29 09:55:43 +03:00
|
|
|
|
|
|
|
_sendTelemetry(ping) {
|
|
|
|
this._dispatch({
|
|
|
|
type: "TOOLBAR_PANEL_TELEMETRY",
|
2020-05-26 16:33:46 +03:00
|
|
|
data: { action: "whats-new-panel_user_event", ...ping },
|
2019-07-29 09:55:43 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
sendUserEventTelemetry(win, event, message, options = {}) {
|
|
|
|
// Only send pings for non private browsing windows
|
|
|
|
if (
|
|
|
|
win &&
|
|
|
|
!PrivateBrowsingUtils.isBrowserPrivate(
|
|
|
|
win.ownerGlobal.gBrowser.selectedBrowser
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
this._sendTelemetry({
|
|
|
|
message_id: message.id,
|
|
|
|
bucket_id: message.id,
|
|
|
|
event,
|
2019-11-06 23:43:58 +03:00
|
|
|
event_context: options.value,
|
2019-07-29 09:55:43 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 20:57:48 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a message into the Protections Panel. The message is visible once
|
|
|
|
* and afterwards set in a collapsed state. It can be shown again using the
|
|
|
|
* info button in the panel header.
|
|
|
|
*/
|
|
|
|
async insertProtectionPanelMessage(event) {
|
|
|
|
const win = event.target.ownerGlobal;
|
2019-08-16 08:54:16 +03:00
|
|
|
this.maybeInsertFTL(win);
|
|
|
|
|
2019-08-07 20:57:48 +03:00
|
|
|
const doc = event.target.ownerDocument;
|
|
|
|
const container = doc.getElementById("messaging-system-message-container");
|
|
|
|
const infoButton = doc.getElementById("protections-popup-info-button");
|
|
|
|
const panelContainer = doc.getElementById("protections-popup");
|
|
|
|
const toggleMessage = () => {
|
2020-01-11 01:39:17 +03:00
|
|
|
const learnMoreLink = doc.querySelector(
|
|
|
|
"#messaging-system-message-container .text-link"
|
|
|
|
);
|
2019-08-07 20:57:48 +03:00
|
|
|
container.toggleAttribute("disabled");
|
|
|
|
infoButton.toggleAttribute("checked");
|
2019-08-24 22:28:06 +03:00
|
|
|
panelContainer.toggleAttribute("infoMessageShowing");
|
2020-01-11 01:39:17 +03:00
|
|
|
learnMoreLink.disabled = !learnMoreLink.disabled;
|
2019-08-07 20:57:48 +03:00
|
|
|
};
|
|
|
|
if (!container.childElementCount) {
|
|
|
|
const message = await this._getMessages({
|
|
|
|
template: "protections_panel",
|
|
|
|
triggerId: "protectionsPanelOpen",
|
|
|
|
});
|
|
|
|
if (message) {
|
2020-04-18 14:24:44 +03:00
|
|
|
const messageEl = this._createHeroElement(win, doc, message);
|
2019-08-07 20:57:48 +03:00
|
|
|
container.appendChild(messageEl);
|
|
|
|
infoButton.addEventListener("click", toggleMessage);
|
2019-10-19 03:41:03 +03:00
|
|
|
this.sendUserEventTelemetry(win, "IMPRESSION", message);
|
2019-08-07 20:57:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Message is collapsed by default. If it was never shown before we want
|
|
|
|
// to expand it
|
|
|
|
if (
|
|
|
|
!this.state.protectionPanelMessageSeen &&
|
|
|
|
container.hasAttribute("disabled")
|
|
|
|
) {
|
|
|
|
toggleMessage();
|
|
|
|
}
|
|
|
|
// Save state that we displayed the message
|
|
|
|
if (!this.state.protectionPanelMessageSeen) {
|
|
|
|
Services.prefs.setBoolPref(PROTECTIONS_PANEL_INFOMSG_PREF, true);
|
|
|
|
this.state.protectionPanelMessageSeen = true;
|
|
|
|
}
|
|
|
|
// Collapse the message after the panel is hidden so we don't get the
|
|
|
|
// animation when opening the panel
|
|
|
|
panelContainer.addEventListener(
|
|
|
|
"popuphidden",
|
|
|
|
() => {
|
|
|
|
if (
|
|
|
|
this.state.protectionPanelMessageSeen &&
|
|
|
|
!container.hasAttribute("disabled")
|
|
|
|
) {
|
|
|
|
toggleMessage();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
once: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2019-09-14 01:57:40 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {object} browser MessageChannel target argument as a response to a user action
|
2020-01-23 19:56:08 +03:00
|
|
|
* @param {object[]} messages Messages selected from devtools page
|
2019-09-14 01:57:40 +03:00
|
|
|
*/
|
2020-01-23 19:56:08 +03:00
|
|
|
forceShowMessage(browser, messages) {
|
2019-09-14 01:57:40 +03:00
|
|
|
const win = browser.browser.ownerGlobal;
|
|
|
|
const doc = browser.browser.ownerDocument;
|
|
|
|
this.removeMessages(win, WHATS_NEW_PANEL_SELECTOR);
|
|
|
|
this.renderMessages(win, doc, WHATS_NEW_PANEL_SELECTOR, {
|
|
|
|
force: true,
|
2020-01-23 19:56:08 +03:00
|
|
|
messages: Array.isArray(messages) ? messages : [messages],
|
2019-09-14 01:57:40 +03:00
|
|
|
});
|
|
|
|
win.PanelUI.panel.addEventListener("popuphidden", event =>
|
|
|
|
this.removeMessages(event.target.ownerGlobal, WHATS_NEW_PANEL_SELECTOR)
|
|
|
|
);
|
|
|
|
}
|
2019-07-12 00:50:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
this._ToolbarPanelHub = _ToolbarPanelHub;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ToolbarPanelHub - singleton instance of _ToolbarPanelHub that can initiate
|
|
|
|
* message requests and render messages.
|
|
|
|
*/
|
|
|
|
this.ToolbarPanelHub = new _ToolbarPanelHub();
|
|
|
|
|
|
|
|
const EXPORTED_SYMBOLS = ["ToolbarPanelHub", "_ToolbarPanelHub"];
|