зеркало из https://github.com/mozilla/gecko-dev.git
219 строки
7.2 KiB
JavaScript
219 строки
7.2 KiB
JavaScript
/* 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";
|
|
|
|
ChromeUtils.defineModuleGetter(this, "DOMLocalization",
|
|
"resource://gre/modules/DOMLocalization.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "FxAccounts",
|
|
"resource://gre/modules/FxAccounts.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "Services",
|
|
"resource://gre/modules/Services.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
|
|
|
class _BookmarkPanelHub {
|
|
constructor() {
|
|
this._id = "BookmarkPanelHub";
|
|
this._trigger = {id: "bookmark-panel"};
|
|
this._handleMessageRequest = null;
|
|
this._addImpression = null;
|
|
this._dispatch = null;
|
|
this._initalized = false;
|
|
this._response = null;
|
|
this._l10n = null;
|
|
|
|
this.messageRequest = this.messageRequest.bind(this);
|
|
this.toggleRecommendation = this.toggleRecommendation.bind(this);
|
|
this.sendUserEventTelemetry = this.sendUserEventTelemetry.bind(this);
|
|
this.collapseMessage = this.collapseMessage.bind(this);
|
|
}
|
|
|
|
/**
|
|
* @param {function} handleMessageRequest
|
|
* @param {function} addImpression
|
|
* @param {function} dispatch - Used for sending user telemetry information
|
|
*/
|
|
init(handleMessageRequest, addImpression, dispatch) {
|
|
this._handleMessageRequest = handleMessageRequest;
|
|
this._addImpression = addImpression;
|
|
this._dispatch = dispatch;
|
|
this._l10n = new DOMLocalization([
|
|
"browser/branding/sync-brand.ftl",
|
|
"browser/newtab/asrouter.ftl",
|
|
]);
|
|
this._initalized = true;
|
|
}
|
|
|
|
uninit() {
|
|
this._l10n = null;
|
|
this._initalized = false;
|
|
this._handleMessageRequest = null;
|
|
this._addImpression = null;
|
|
this._dispatch = null;
|
|
this._response = null;
|
|
}
|
|
|
|
/**
|
|
* Checks if a similar cached requests exists before forwarding the request
|
|
* to ASRouter. Caches only 1 request, unique identifier is `request.url`.
|
|
* Caching ensures we don't duplicate requests and telemetry pings.
|
|
* Return value is important for the caller to know if a message will be
|
|
* shown.
|
|
*
|
|
* @returns {obj|null} response object or null if no messages matched
|
|
*/
|
|
async messageRequest(target, win) {
|
|
if (this._response && this._response.win === win && this._response.url === target.url && this._response.content) {
|
|
this.showMessage(this._response.content, target, win);
|
|
return true;
|
|
}
|
|
|
|
const response = await this._handleMessageRequest(this._trigger);
|
|
|
|
return this.onResponse(response, target, win);
|
|
}
|
|
|
|
/**
|
|
* If the response contains a message render it and send an impression.
|
|
* Otherwise we remove the message from the container.
|
|
*/
|
|
onResponse(response, target, win) {
|
|
this._response = {
|
|
...response,
|
|
collapsed: false,
|
|
target,
|
|
win,
|
|
url: target.url,
|
|
};
|
|
|
|
if (response && response.content) {
|
|
this.showMessage(response.content, target, win);
|
|
this.sendImpression();
|
|
this.sendUserEventTelemetry("IMPRESSION", win);
|
|
} else {
|
|
this.hideMessage(target);
|
|
}
|
|
|
|
target.infoButton.disabled = !response;
|
|
|
|
return !!response;
|
|
}
|
|
|
|
showMessage(message, target, win) {
|
|
if (this._response.collapsed) {
|
|
this.toggleRecommendation(false);
|
|
return;
|
|
}
|
|
|
|
const createElement = elem => target.document.createElementNS("http://www.w3.org/1999/xhtml", elem);
|
|
|
|
if (!target.container.querySelector("#cfrMessageContainer")) {
|
|
const recommendation = createElement("div");
|
|
recommendation.setAttribute("id", "cfrMessageContainer");
|
|
recommendation.addEventListener("click", async e => {
|
|
target.hidePopup();
|
|
const url = await FxAccounts.config.promiseEmailFirstURI("bookmark");
|
|
win.ownerGlobal.openLinkIn(url, "tabshifted", {
|
|
private: false,
|
|
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
|
|
csp: null,
|
|
});
|
|
this.sendUserEventTelemetry("CLICK", win);
|
|
});
|
|
recommendation.style.color = message.color;
|
|
recommendation.style.background = `-moz-linear-gradient(-45deg, ${message.background_color_1} 0%, ${message.background_color_2} 70%)`;
|
|
const close = createElement("a");
|
|
close.setAttribute("id", "cfrClose");
|
|
close.setAttribute("aria-label", "close");
|
|
this._l10n.setAttributes(close, message.close_button.tooltiptext);
|
|
close.addEventListener("click", e => {
|
|
this.sendUserEventTelemetry("DISMISS", win);
|
|
this.collapseMessage();
|
|
target.close(e);
|
|
});
|
|
const title = createElement("h1");
|
|
title.setAttribute("id", "editBookmarkPanelRecommendationTitle");
|
|
this._l10n.setAttributes(title, message.title);
|
|
const content = createElement("p");
|
|
content.setAttribute("id", "editBookmarkPanelRecommendationContent");
|
|
this._l10n.setAttributes(content, message.text);
|
|
const cta = createElement("button");
|
|
cta.setAttribute("id", "editBookmarkPanelRecommendationCta");
|
|
this._l10n.setAttributes(cta, message.cta);
|
|
recommendation.appendChild(close);
|
|
recommendation.appendChild(title);
|
|
recommendation.appendChild(content);
|
|
recommendation.appendChild(cta);
|
|
this._l10n.translateElements([...recommendation.children]);
|
|
target.container.appendChild(recommendation);
|
|
}
|
|
|
|
this.toggleRecommendation(true);
|
|
}
|
|
|
|
toggleRecommendation(visible) {
|
|
const {target} = this._response;
|
|
if (visible === undefined) {
|
|
// When called from the info button of the bookmark panel
|
|
target.infoButton.checked = !target.infoButton.checked;
|
|
} else {
|
|
target.infoButton.checked = visible;
|
|
}
|
|
if (target.infoButton.checked) {
|
|
// If it was ever collapsed we need to cancel the state
|
|
this._response.collapsed = false;
|
|
target.recommendationContainer.removeAttribute("disabled");
|
|
} else {
|
|
target.recommendationContainer.setAttribute("disabled", "disabled");
|
|
}
|
|
}
|
|
|
|
collapseMessage() {
|
|
this._response.collapsed = true;
|
|
this.toggleRecommendation(false);
|
|
}
|
|
|
|
hideMessage(target) {
|
|
const container = target.container.querySelector("#cfrMessageContainer");
|
|
if (container) {
|
|
container.remove();
|
|
}
|
|
this.toggleRecommendation(false);
|
|
this._response = null;
|
|
}
|
|
|
|
_forceShowMessage(message) {
|
|
this.toggleRecommendation(true);
|
|
this.showMessage(message.content, this._response.target, this._response.win);
|
|
}
|
|
|
|
sendImpression() {
|
|
this._addImpression(this._response);
|
|
}
|
|
|
|
sendUserEventTelemetry(event, win) {
|
|
// Only send pings for non private browsing windows
|
|
if (!PrivateBrowsingUtils.isBrowserPrivate(win.ownerGlobal.gBrowser.selectedBrowser)) {
|
|
this._sendTelemetry({message_id: this._response.id, bucket_id: this._response.id, event});
|
|
}
|
|
}
|
|
|
|
_sendTelemetry(ping) {
|
|
this._dispatch({
|
|
type: "DOORHANGER_TELEMETRY",
|
|
data: {action: "cfr_user_event", source: "CFR", ...ping},
|
|
});
|
|
}
|
|
}
|
|
|
|
this._BookmarkPanelHub = _BookmarkPanelHub;
|
|
|
|
/**
|
|
* BookmarkPanelHub - singleton instance of _BookmarkPanelHub that can initiate
|
|
* message requests and render messages.
|
|
*/
|
|
this.BookmarkPanelHub = new _BookmarkPanelHub();
|
|
|
|
const EXPORTED_SYMBOLS = ["BookmarkPanelHub", "_BookmarkPanelHub"];
|